diff --git a/Makefile b/Makefile index 86ec3220d..9c57e63d6 100644 --- a/Makefile +++ b/Makefile @@ -1,43 +1,45 @@ -DIRS = pjlib pjsdp pjmedia pjsip - -MAKE_FLAGS := TARGET=$(TARGET) - -ifdef MINSIZE -MAKE_FLAGS := $(MAKE_FLAGS) MINSIZE=1 -endif - -all clean dep depend distclean doc print realclean: - for dir in $(DIRS); do \ - if [ -d $$dir ]; then \ - if make $(MAKE_FLAGS) -C $$dir/build $@; then \ - true; \ - else \ - exit 1; \ - fi; \ - fi \ - done - -LIBS = pjlib/lib/libpj.a pjsdp/lib/libpjsdp.a pjmedia/lib/libpjmedia.a \ - pjsip/lib/libpjsip_core.a pjsip/lib/libpjsip_ua.a -BINS = pjsip/bin/pjsua$(EXE) - -include pjlib/build/make-$(TARGET).inc - -size: - @echo 'TARGET=$(TARGET)' - @echo -n 'Date: ' - @date - @echo - @for lib in $(LIBS); do \ - echo "$$lib:"; \ - ar tv $$lib | awk '{print $$3 "\t" $$8}' | sort -n; \ - echo -n 'Total: '; \ - ar tv $$lib | awk '{print " + " $$3}' | xargs expr 0; \ - echo; \ - done - @echo - @for bin in $(BINS); do \ - echo "size $$bin:"; \ - size $$bin; \ - done - +DIRS = pjlib pjsdp pjmedia pjsip + +ifdef MINSIZE +MAKE_FLAGS := MINSIZE=1 +endif + +all clean dep depend distclean doc print realclean: + for dir in $(DIRS); do \ + if [ -d $$dir ]; then \ + if make $(MAKE_FLAGS) -C $$dir/build $@; then \ + true; \ + else \ + exit 1; \ + fi; \ + fi \ + done + +LIBS = pjlib/lib/libpj.a pjsdp/lib/libpjsdp.a pjmedia/lib/libpjmedia.a \ + pjsip/lib/libpjsip_core.a pjsip/lib/libpjsip_ua.a +BINS = pjsip/bin/pjsua$(EXE) + +size: + @echo -n 'Date: ' + @date + @echo + @for lib in $(LIBS); do \ + echo "$$lib:"; \ + ar tv $$lib | awk '{print $$3 "\t" $$8}' | sort -n; \ + echo -n 'Total: '; \ + ar tv $$lib | awk '{print " + " $$3}' | xargs expr 0; \ + echo; \ + done + @echo + @for bin in $(BINS); do \ + echo "size $$bin:"; \ + size $$bin; \ + done + +dos2unix: + for f in `find . | egrep '(mak|h|c|S|s|Makefile)$$'`; do \ + dos2unix "$$f" > dos2unix.tmp; \ + cp dos2unix.tmp "$$f"; \ + done + rm -f dos2unix.tmp + diff --git a/build.mak b/build.mak index ec65debc6..145b59a4e 100644 --- a/build.mak +++ b/build.mak @@ -1,55 +1,63 @@ -# Build configurations: -# -# MACHINE_NAME values: -# - i386 (generic x86) -# - m68k -# -# OS_NAME values: -# - win32 (generic windows) -# - linux -# -# CC_NAME values: -# - gcc -# - msvc -# -# HOST_NAME values: -# - win32 (Windows command line) -# - mingw (Windows, mingw) -# - -# -# PalmOS 6 cross-compile, cygwin -# -#export MACHINE_NAME := m68k -#export OS_NAME := palmos -#export CC_NAME := gcc -#export HOST_NAME := mingw - -# -# Win32, mingw -# -#export MACHINE_NAME := i386 -#export OS_NAME := win32 -#export CC_NAME := gcc -#export HOST_NAME := mingw - -# -# Linux i386, gcc -# -export MACHINE_NAME := i386 -export OS_NAME := linux -export CC_NAME := gcc -export HOST_NAME := unix - -# -# Linux KERNEL i386, gcc -# -#export MACHINE_NAME := i386 -#export OS_NAME := linux-kernel -#export CC_NAME := gcc -#export HOST_NAME := unix -#export PJPROJECT_DIR := /usr/src/pjproject-0.3 -##export KERNEL_DIR = /usr/src/linux -#export KERNEL_DIR = /usr/src/uml/linux -#export KERNEL_ARCH = ARCH=um - +# Build configurations: +# +# MACHINE_NAME values: +# - i386 (generic x86) +# - m68k +# +# OS_NAME values: +# - win32 (generic windows) +# - linux +# +# CC_NAME values: +# - gcc +# - msvc +# +# HOST_NAME values: +# - win32 (Windows command line) +# - mingw (Windows, mingw) +# + +# +# PalmOS 6 cross-compile, cygwin +# +#export MACHINE_NAME := m68k +#export OS_NAME := palmos +#export CC_NAME := gcc +#export HOST_NAME := mingw + +# +# Win32, mingw +# +#export MACHINE_NAME := i386 +#export OS_NAME := win32 +#export CC_NAME := gcc +#export HOST_NAME := mingw + +# +# Linux i386, gcc +# +export MACHINE_NAME := i386 +export OS_NAME := linux +export CC_NAME := gcc +export HOST_NAME := unix + +# +# Linux KERNEL i386, gcc +# +#export MACHINE_NAME := i386 +#export OS_NAME := linux-kernel +#export CC_NAME := gcc +#export HOST_NAME := unix +#export PJPROJECT_DIR := /usr/src/pjproject-0.3 +##export KERNEL_DIR = /usr/src/linux +#export KERNEL_DIR = /usr/src/uml/linux +#export KERNEL_ARCH = ARCH=um + +# +# SunOS, sparc, gcc +# +export MACHINE_NAME := sparc +export OS_NAME := sunos +export CC_NAME := gcc +export HOST_NAME := unix + diff --git a/build/cc-gcc.mak b/build/cc-gcc.mak index 9a48519dc..9a5ff2f6a 100644 --- a/build/cc-gcc.mak +++ b/build/cc-gcc.mak @@ -1,22 +1,22 @@ -export CC = $(CROSS_COMPILE)gcc -c -export AR = $(CROSS_COMPILE)ar r -export LD = $(CROSS_COMPILE)gcc -export LDOUT = -o -export RANLIB = $(CROSS_COMPILE)ranlib - -export OBJEXT := .o -export LIBEXT := .a -export LIBEXT2 := - -export CC_OUT := -o -export CC_INC := -I -export CC_DEF := -D -export CC_OPTIMIZE := -O2 -export CC_LIB := -l - -export CC_SOURCES := -export CC_CFLAGS := -Wall -#export CC_CFLAGS += -Wdeclaration-after-statement -#export CC_CXXFLAGS := -Wdeclaration-after-statement -export CC_LDFLAGS := - +export CC = $(CROSS_COMPILE)gcc -c +export AR = $(CROSS_COMPILE)ar r +export LD = $(CROSS_COMPILE)gcc +export LDOUT = -o +export RANLIB = $(CROSS_COMPILE)ranlib + +export OBJEXT := .o +export LIBEXT := .a +export LIBEXT2 := + +export CC_OUT := -o +export CC_INC := -I +export CC_DEF := -D +export CC_OPTIMIZE := -O2 +export CC_LIB := -l + +export CC_SOURCES := +export CC_CFLAGS := -Wall +#export CC_CFLAGS += -Wdeclaration-after-statement +#export CC_CXXFLAGS := -Wdeclaration-after-statement +export CC_LDFLAGS := + diff --git a/build/cc-vc.mak b/build/cc-vc.mak index 68870ea53..187372d1c 100644 --- a/build/cc-vc.mak +++ b/build/cc-vc.mak @@ -1,20 +1,20 @@ -export CC := cl /c /nologo -export AR := lib /NOLOGO /OUT: -export LD := cl /nologo -export LDOUT := /Fe -export RANLIB := echo ranlib - -export OBJEXT := .obj -export LIBEXT := .lib -export LIBEXT2 := .LIB - -export CC_OUT := /Fo -export CC_INC := /I -export CC_DEF := /D -export CC_OPTIMIZE := /Ox -export CC_LIB := - -export CC_SOURCES := -export CC_CFLAGS := /W4 /MT -export CC_CXXFLAGS := /GX -export CC_LDFLAGS := /MT +export CC := cl /c /nologo +export AR := lib /NOLOGO /OUT: +export LD := cl /nologo +export LDOUT := /Fe +export RANLIB := echo ranlib + +export OBJEXT := .obj +export LIBEXT := .lib +export LIBEXT2 := .LIB + +export CC_OUT := /Fo +export CC_INC := /I +export CC_DEF := /D +export CC_OPTIMIZE := /Ox +export CC_LIB := + +export CC_SOURCES := +export CC_CFLAGS := /W4 /MT +export CC_CXXFLAGS := /GX +export CC_LDFLAGS := /MT diff --git a/build/host-mingw.mak b/build/host-mingw.mak index fc7eaf35b..94d03af32 100644 --- a/build/host-mingw.mak +++ b/build/host-mingw.mak @@ -1,13 +1,13 @@ -export HOST_MV := mv -export HOST_RM := rm -f @@ -export HOST_RMR := rm -rf @@ -export HOST_RMDIR := rm -rf @@ -export HOST_MKDIR := mkdir @@ -export HOST_EXE := .exe -export HOST_PSEP := / - -export HOST_SOURCES := -export HOST_CFLAGS := -export HOST_CXXFLAGS := -export HOST_LDFLAGS := $(CC_LIB)stdc++$(LIBEXT2) - +export HOST_MV := mv +export HOST_RM := rm -f @@ +export HOST_RMR := rm -rf @@ +export HOST_RMDIR := rm -rf @@ +export HOST_MKDIR := mkdir @@ +export HOST_EXE := .exe +export HOST_PSEP := / + +export HOST_SOURCES := +export HOST_CFLAGS := +export HOST_CXXFLAGS := +export HOST_LDFLAGS := $(CC_LIB)stdc++$(LIBEXT2) + diff --git a/build/host-unix.mak b/build/host-unix.mak index ae692a317..a84f3ec10 100644 --- a/build/host-unix.mak +++ b/build/host-unix.mak @@ -1,13 +1,13 @@ -export HOST_MV := mv -export HOST_RM := rm -f @@ -export HOST_RMR := rm -rf @@ -export HOST_RMDIR := rm -rf @@ -export HOST_MKDIR := mkdir @@ -export HOST_EXE := -export HOST_PSEP := / - -export HOST_SOURCES := -export HOST_CFLAGS := -export HOST_CXXFLAGS := -export HOST_LDFLAGS := - +export HOST_MV := mv +export HOST_RM := rm -f @@ +export HOST_RMR := rm -rf @@ +export HOST_RMDIR := rm -rf @@ +export HOST_MKDIR := mkdir @@ +export HOST_EXE := +export HOST_PSEP := / + +export HOST_SOURCES := +export HOST_CFLAGS := +export HOST_CXXFLAGS := +export HOST_LDFLAGS := + diff --git a/build/host-win32.mak b/build/host-win32.mak index d9dc635ec..9d0b379fb 100644 --- a/build/host-win32.mak +++ b/build/host-win32.mak @@ -1,12 +1,12 @@ -export HOST_MV := ren -export HOST_RM := if exist @@; del /F /Q @@ -export HOST_RMR := if exist @@; del /F /Q @@ -export HOST_RMDIR := if exist @@; rmdir @@ -export HOST_MKDIR := if not exist @@; mkdir @@ -export HOST_EXE := .exe -export HOST_PSEP := \\ - -export HOST_SOURCES := -export HOST_CFLAGS := -export HOST_CXXFLAGS := -export HOST_LDFLAGS := +export HOST_MV := ren +export HOST_RM := if exist @@; del /F /Q @@ +export HOST_RMR := if exist @@; del /F /Q @@ +export HOST_RMDIR := if exist @@; rmdir @@ +export HOST_MKDIR := if not exist @@; mkdir @@ +export HOST_EXE := .exe +export HOST_PSEP := \\ + +export HOST_SOURCES := +export HOST_CFLAGS := +export HOST_CXXFLAGS := +export HOST_LDFLAGS := diff --git a/build/m-alpha.mak b/build/m-alpha.mak index 90b760602..f1c750251 100644 --- a/build/m-alpha.mak +++ b/build/m-alpha.mak @@ -1,4 +1,4 @@ -export M_CFLAGS := $(CC_DEF)PJ_M_ALPHA=1 -export M_CXXFLAGS := -export M_LDFLAGS := -export M_SOURCES := +export M_CFLAGS := $(CC_DEF)PJ_M_ALPHA=1 +export M_CXXFLAGS := +export M_LDFLAGS := +export M_SOURCES := diff --git a/build/m-i386.mak b/build/m-i386.mak index dc5c1328d..1a8229d71 100644 --- a/build/m-i386.mak +++ b/build/m-i386.mak @@ -1,4 +1,4 @@ -export M_CFLAGS := $(CC_DEF)PJ_M_I386=1 -export M_CXXFLAGS := -export M_LDFLAGS := -export M_SOURCES := +export M_CFLAGS := $(CC_DEF)PJ_M_I386=1 +export M_CXXFLAGS := +export M_LDFLAGS := +export M_SOURCES := diff --git a/build/m-m68k.mak b/build/m-m68k.mak index d5ba9e922..265a6eae9 100644 --- a/build/m-m68k.mak +++ b/build/m-m68k.mak @@ -1,4 +1,4 @@ -export M_CFLAGS := $(CC_DEF)PJ_M_M68K=1 -export M_CXXFLAGS := -export M_LDFLAGS := -export M_SOURCES := +export M_CFLAGS := $(CC_DEF)PJ_M_M68K=1 +export M_CXXFLAGS := +export M_LDFLAGS := +export M_SOURCES := diff --git a/build/m-sparc.mak b/build/m-sparc.mak new file mode 100644 index 000000000..fa27aacc5 --- /dev/null +++ b/build/m-sparc.mak @@ -0,0 +1,4 @@ +export M_CFLAGS := $(CC_DEF)PJ_M_SPARC=1 +export M_CXXFLAGS := +export M_LDFLAGS := +export M_SOURCES := diff --git a/build/os-linux-kernel.mak b/build/os-linux-kernel.mak index f9b3e1fa1..f5591161a 100644 --- a/build/os-linux-kernel.mak +++ b/build/os-linux-kernel.mak @@ -1,43 +1,43 @@ - -include $(KERNEL_DIR)/.config - -# -# Basic kernel compilation flags. -# -export OS_CFLAGS := $(CC_DEF)PJ_LINUX_KERNEL=1 -D__KERNEL__ \ - -I$(KERNEL_DIR)/include -iwithprefix include \ - -nostdinc -msoft-float - -# -# Additional kernel compilation flags are taken from the kernel Makefile -# itself. -# - -KERNEL_CFLAGS := \ - $(shell cd $(KERNEL_DIR) ; \ - make script SCRIPT='@echo $$(CFLAGS) $$(CFLAGS_MODULE)' $(KERNEL_ARCH)) - -export OS_CFLAGS += $(KERNEL_CFLAGS) - -# -DMODULE -I$(KERNEL_DIR)/include -nostdinc \ -# -Wstrict-prototypes \ -# -Wno-trigraphs -fno-strict-aliasing -fno-common \ -# -msoft-float -m32 -fno-builtin-sprintf -fno-builtin-log2\ -# -fno-builtin-puts -mpreferred-stack-boundary=2 \ -# -fno-unit-at-a-time -march=i686 -mregparm=3 \ -# -iwithprefix include - -#export OS_CFLAGS += -U__i386__ -Ui386 -D__arch_um__ -DSUBARCH=\"i386\" \ -# -D_LARGEFILE64_SOURCE -I$(KERNEL_DIR)/arch/um/include \ -# -Derrno=kernel_errno \ -# -I$(KERNEL_DIR)/arch/um/kernel/tt/include \ -# -I$(KERNEL_DIR)/arch/um/kernel/skas/include \ - - -export OS_CXXFLAGS := - -export OS_LDFLAGS := - -export OS_SOURCES := - - + +include $(KERNEL_DIR)/.config + +# +# Basic kernel compilation flags. +# +export OS_CFLAGS := $(CC_DEF)PJ_LINUX_KERNEL=1 -D__KERNEL__ \ + -I$(KERNEL_DIR)/include -iwithprefix include \ + -nostdinc -msoft-float + +# +# Additional kernel compilation flags are taken from the kernel Makefile +# itself. +# + +KERNEL_CFLAGS := \ + $(shell cd $(KERNEL_DIR) ; \ + make script SCRIPT='@echo $$(CFLAGS) $$(CFLAGS_MODULE)' $(KERNEL_ARCH)) + +export OS_CFLAGS += $(KERNEL_CFLAGS) + +# -DMODULE -I$(KERNEL_DIR)/include -nostdinc \ +# -Wstrict-prototypes \ +# -Wno-trigraphs -fno-strict-aliasing -fno-common \ +# -msoft-float -m32 -fno-builtin-sprintf -fno-builtin-log2\ +# -fno-builtin-puts -mpreferred-stack-boundary=2 \ +# -fno-unit-at-a-time -march=i686 -mregparm=3 \ +# -iwithprefix include + +#export OS_CFLAGS += -U__i386__ -Ui386 -D__arch_um__ -DSUBARCH=\"i386\" \ +# -D_LARGEFILE64_SOURCE -I$(KERNEL_DIR)/arch/um/include \ +# -Derrno=kernel_errno \ +# -I$(KERNEL_DIR)/arch/um/kernel/tt/include \ +# -I$(KERNEL_DIR)/arch/um/kernel/skas/include \ + + +export OS_CXXFLAGS := + +export OS_LDFLAGS := + +export OS_SOURCES := + + diff --git a/build/os-linux.mak b/build/os-linux.mak index da44bc977..c0c4a445d 100644 --- a/build/os-linux.mak +++ b/build/os-linux.mak @@ -1,9 +1,9 @@ -export OS_CFLAGS := $(CC_DEF)PJ_LINUX=1 - -export OS_CXXFLAGS := - -export OS_LDFLAGS := $(CC_LIB)pthread$(LIBEXT2) - -export OS_SOURCES := - - +export OS_CFLAGS := $(CC_DEF)PJ_LINUX=1 + +export OS_CXXFLAGS := + +export OS_LDFLAGS := $(CC_LIB)pthread$(LIBEXT2) + +export OS_SOURCES := + + diff --git a/build/os-palmos.mak b/build/os-palmos.mak index 2a86b25a3..43f83bb3e 100644 --- a/build/os-palmos.mak +++ b/build/os-palmos.mak @@ -1,32 +1,32 @@ -# -# make-mingw.inc: Mingw specific compilation switches. -# -PALM_OS_SDK_VER := 0x06000000 -PALM_OS_TARGET_HOST := TARGET_HOST_PALMOS -PALM_OS_TARGET_PLATFORM := TARGET_PLATFORM_PALMSIM_WIN32 -PALM_OS_BUILD_TYPE := BUILD_TYPE_DEBUG -PALM_OS_TRACE_OUTPUT := TRACE_OUTPUT_ON -PALM_OS_CPU_TYPE := CPU_ARM - -export CROSS_COMPILE := - -ifeq ($(CC_NAME),gcc) - export CFLAGS += -mno-cygwin -fexceptions -frtti -endif - -export OS_CFLAGS := $(CC_DEF)PJ_PALMOS=1 \ - $(CC_DEF)__PALMOS_KERNEL__=1 \ - $(CC_DEF)__PALMOS__=$(PALM_OS_SDK_VER) \ - $(CC_DEF)BUILD_TYPE=$(PALM_OS_BUILD_TYPE) \ - $(CC_DEF)TRACE_OUTPUT=$(PALM_OS_TRACE_OUTPUT) \ - $(CC_DEF)_SUPPORTS_NAMESPACE=0 \ - $(CC_DEF)_SUPPORTS_RTTI=0 \ - $(CC_DEF)TARGET_HOST=$(PALM_OS_TRAGET_HOST) \ - $(CC_DEF)TARGET_PLATFORM=$(PALM_OS_TARGET_PLATFORM) - -export OS_CXXFLAGS := - -export OS_LDFLAGS := - -export OS_SOURCES := - +# +# make-mingw.inc: Mingw specific compilation switches. +# +PALM_OS_SDK_VER := 0x06000000 +PALM_OS_TARGET_HOST := TARGET_HOST_PALMOS +PALM_OS_TARGET_PLATFORM := TARGET_PLATFORM_PALMSIM_WIN32 +PALM_OS_BUILD_TYPE := BUILD_TYPE_DEBUG +PALM_OS_TRACE_OUTPUT := TRACE_OUTPUT_ON +PALM_OS_CPU_TYPE := CPU_ARM + +export CROSS_COMPILE := + +ifeq ($(CC_NAME),gcc) + export CFLAGS += -mno-cygwin -fexceptions -frtti +endif + +export OS_CFLAGS := $(CC_DEF)PJ_PALMOS=1 \ + $(CC_DEF)__PALMOS_KERNEL__=1 \ + $(CC_DEF)__PALMOS__=$(PALM_OS_SDK_VER) \ + $(CC_DEF)BUILD_TYPE=$(PALM_OS_BUILD_TYPE) \ + $(CC_DEF)TRACE_OUTPUT=$(PALM_OS_TRACE_OUTPUT) \ + $(CC_DEF)_SUPPORTS_NAMESPACE=0 \ + $(CC_DEF)_SUPPORTS_RTTI=0 \ + $(CC_DEF)TARGET_HOST=$(PALM_OS_TRAGET_HOST) \ + $(CC_DEF)TARGET_PLATFORM=$(PALM_OS_TARGET_PLATFORM) + +export OS_CXXFLAGS := + +export OS_LDFLAGS := + +export OS_SOURCES := + diff --git a/build/os-sunos.mak b/build/os-sunos.mak new file mode 100644 index 000000000..4af21040d --- /dev/null +++ b/build/os-sunos.mak @@ -0,0 +1,12 @@ +export OS_CFLAGS := $(CC_DEF)PJ_SUNOS=1 + +export OS_CXXFLAGS := + +export OS_LDFLAGS := $(CC_LIB)pthread$(LIBEXT2) \ + $(CC_LIB)socket$(LIBEXT2) \ + $(CC_LIB)rt$(LIBEXT2) \ + $(CC_LIB)nsl$(LIBEXT2) + +export OS_SOURCES := + + diff --git a/build/os-win32.mak b/build/os-win32.mak index 652502d6b..3f29776c9 100644 --- a/build/os-win32.mak +++ b/build/os-win32.mak @@ -1,11 +1,11 @@ -export OS_CFLAGS := $(CC_DEF)PJ_WIN32=1 - -export OS_CXXFLAGS := - -export OS_LDFLAGS := $(CC_LIB)wsock32$(LIBEXT2) \ - $(CC_LIB)ws2_32$(LIBEXT2)\ - $(CC_LIB)ole32$(LIBEXT2) - -export OS_SOURCES := - - +export OS_CFLAGS := $(CC_DEF)PJ_WIN32=1 + +export OS_CXXFLAGS := + +export OS_LDFLAGS := $(CC_LIB)wsock32$(LIBEXT2) \ + $(CC_LIB)ws2_32$(LIBEXT2)\ + $(CC_LIB)ole32$(LIBEXT2) + +export OS_SOURCES := + + diff --git a/build/rules.mak b/build/rules.mak index d76343350..9757712ea 100644 --- a/build/rules.mak +++ b/build/rules.mak @@ -1,164 +1,165 @@ -LIBDIR = ../lib -BINDIR = ../bin - -# -# The full path of output lib file (e.g. ../lib/libapp.a). -# -LIB = $($(APP)_LIB) - -# -# The full path of output executable file (e.g. ../bin/app.exe). -# -EXE = $($(APP)_EXE) - -# -# Source directory -# -SRCDIR = $($(APP)_SRCDIR) - -# -# Output directory for object files (i.e. output/target) -# -OBJDIR = output/$(app)-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME) - -ifeq ($(OS_NAME),linux-kernel) -export $(APP)_CFLAGS += -DKBUILD_MODNAME=$(app) -DKBUILD_BASENAME=$(app) -endif - - -# -# OBJS is ./output/target/file.o -# -OBJS = $(foreach file, $($(APP)_OBJS), $(OBJDIR)/$(file)) -OBJDIRS := $(sort $(dir $(OBJS))) - -# -# FULL_SRCS is ../src/app/file1.c ../src/app/file1.S -# -FULL_SRCS = $(foreach file, $($(APP)_OBJS), $(SRCDIR)/$(basename $(file)).c $(SRCDIR)/$(basename $(file)).cpp $(SRCDIR)/$(basename $(file)).S) - -# -# When generating dependency (gcc -MM), ideally we use only either -# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work. -# -DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS) - -# Dependency file -DEP_FILE := .$(app)-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).depend - - -print_common: - @echo "###" - @echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):" - @echo "###" - @echo APP=$(APP) - @echo OBJDIR=$(OBJDIR) - @echo OBJDIRS=$(OBJDIRS) - @echo OBJS=$(OBJS) - @echo SRCDIR=$(SRCDIR) - @echo FULL_SRCS=$(FULL_SRCS) - @echo $(APP)_CFLAGS=$($(APP)_CFLAGS) - @echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS) - @echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS) - @echo DEPFLAGS=$(DEPFLAGS) - -print_bin: print_common - @echo EXE=$(EXE) - @echo BINDIR=$(BINDIR) - -print_lib: print_common - @echo LIB=$(LIB) - @echo LIBDIR=$(LIBDIR) - -$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) - $(AR)$(LIB) $(OBJS) - $(RANLIB) $(LIB) - -$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) - $(LD) $(LDOUT)$(subst /,$(HOST_PSEP),$(EXE)) \ - $(subst /,$(HOST_PSEP),$(OBJS)) $($(APP)_LDFLAGS) - -$(OBJDIR)/$(app).o: $(OBJDIRS) $(OBJS) - $(CROSS_COMPILE)ld -r -o $@ $(OBJS) - -$(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o - @echo Creating kbuild Makefile... - @echo "# Our module name:" > $(OBJDIR)/Makefile - @echo 'obj-m += $(app).o' >> $(OBJDIR)/Makefile - @echo >> $(OBJDIR)/Makefile - @echo "# Object members:" >> $(OBJDIR)/Makefile - @echo -n '$(app)-objs += ' >> $(OBJDIR)/Makefile - @for file in $($(APP)_OBJS); do \ - echo -n "$$file " >> $(OBJDIR)/Makefile; \ - done - @echo >> $(OBJDIR)/Makefile - @echo >> $(OBJDIR)/Makefile - @echo "# Prevent .o files to be built by kbuild:" >> $(OBJDIR)/Makefile - @for file in $($(APP)_OBJS); do \ - echo ".PHONY: `pwd`/$(OBJDIR)/$$file" >> $(OBJDIR)/Makefile; \ - done - @echo >> $(OBJDIR)/Makefile - @echo all: >> $(OBJDIR)/Makefile - @echo -e "\tmake -C $(KERNEL_DIR) M=`pwd`/$(OBJDIR) modules $(KERNEL_ARCH)" >> $(OBJDIR)/Makefile - @echo Invoking kbuild... - make -C $(OBJDIR) - -../lib/$(app).ko: $(LIB) $(OBJDIR)/$(app).ko - cp $(OBJDIR)/$(app).ko ../lib - -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c - $(CC) $($(APP)_CFLAGS) \ - $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ - $(subst /,$(HOST_PSEP),$<) - -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S - $(CC) $($(APP)_CFLAGS) \ - $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ - $(subst /,$(HOST_PSEP),$<) - -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp - $(CC) $($(APP)_CXXFLAGS) \ - $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ - $(subst /,$(HOST_PSEP),$<) - -$(OBJDIRS): - $(subst @@,$(subst /,$(HOST_PSEP),$@),$(HOST_MKDIR)) - -$(LIBDIR): - $(subst @@,$(subst /,$(HOST_PSEP),$(LIBDIR)),$(HOST_MKDIR)) - -$(BINDIR): - $(subst @@,$(subst /,$(HOST_PSEP),$(BINDIR)),$(HOST_MKDIR)) - -clean: - $(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)/*),$(HOST_RMR)) - $(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)),$(HOST_RMDIR)) -ifeq ($(OS_NAME),linux-kernel) - rm -f ../lib/$(app).o -endif - -realclean: clean - $(subst @@,$(subst /,$(HOST_PSEP),$(LIB)) $(subst /,$(HOST_PSEP),$(EXE)),$(HOST_RM)) - $(subst @@,$(DEP_FILE),$(HOST_RM)) -ifeq ($(OS_NAME),linux-kernel) - rm -f ../lib/$(app).ko -endif - -depend: - $(subst @@,$(DEP_FILE),$(HOST_RM)) - for F in $(FULL_SRCS); do \ - if test -f $$F; then \ - echo -n $(OBJDIR)/ >> $(DEP_FILE); \ - if gcc -MM $(DEPFLAGS) $$F | sed '/^#/d' >> $(DEP_FILE); then \ - true; \ - else \ - echo 'err:' >> $(DEP_FILE); \ - exit 1; \ - fi; \ - fi; \ - done - -dep: depend - --include $(DEP_FILE) - +LIBDIR = ../lib +BINDIR = ../bin + +# +# The full path of output lib file (e.g. ../lib/libapp.a). +# +LIB = $($(APP)_LIB) + +# +# The full path of output executable file (e.g. ../bin/app.exe). +# +EXE = $($(APP)_EXE) + +# +# Source directory +# +SRCDIR = $($(APP)_SRCDIR) + +# +# Output directory for object files (i.e. output/target) +# +OBJDIR = output/$(app)-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME) + +ifeq ($(OS_NAME),linux-kernel) +export $(APP)_CFLAGS += -DKBUILD_MODNAME=$(app) -DKBUILD_BASENAME=$(app) +endif + + +# +# OBJS is ./output/target/file.o +# +OBJS = $(foreach file, $($(APP)_OBJS), $(OBJDIR)/$(file)) +OBJDIRS := $(sort $(dir $(OBJS))) + +# +# FULL_SRCS is ../src/app/file1.c ../src/app/file1.S +# +FULL_SRCS = $(foreach file, $($(APP)_OBJS), $(SRCDIR)/$(basename $(file)).c $(SRCDIR)/$(basename $(file)).cpp $(SRCDIR)/$(basename $(file)).S) + +# +# When generating dependency (gcc -MM), ideally we use only either +# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work. +# +DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS) + +# Dependency file +DEP_FILE := .$(app)-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).depend + + +print_common: + @echo "###" + @echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):" + @echo "###" + @echo APP=$(APP) + @echo OBJDIR=$(OBJDIR) + @echo OBJDIRS=$(OBJDIRS) + @echo OBJS=$(OBJS) + @echo SRCDIR=$(SRCDIR) + @echo FULL_SRCS=$(FULL_SRCS) + @echo $(APP)_CFLAGS=$($(APP)_CFLAGS) + @echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS) + @echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS) + @echo DEPFLAGS=$(DEPFLAGS) + +print_bin: print_common + @echo EXE=$(EXE) + @echo BINDIR=$(BINDIR) + +print_lib: print_common + @echo LIB=$(LIB) + @echo LIBDIR=$(LIBDIR) + +$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) + $(AR)$(LIB) $(OBJS) + $(RANLIB) $(LIB) + +$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) + $(LD) $(LDOUT)$(subst /,$(HOST_PSEP),$(EXE)) \ + $(subst /,$(HOST_PSEP),$(OBJS)) $($(APP)_LDFLAGS) + +$(OBJDIR)/$(app).o: $(OBJDIRS) $(OBJS) + $(CROSS_COMPILE)ld -r -o $@ $(OBJS) + +$(OBJDIR)/$(app).ko: $(OBJDIR)/$(app).o + @echo Creating kbuild Makefile... + @echo "# Our module name:" > $(OBJDIR)/Makefile + @echo 'obj-m += $(app).o' >> $(OBJDIR)/Makefile + @echo >> $(OBJDIR)/Makefile + @echo "# Object members:" >> $(OBJDIR)/Makefile + @echo -n '$(app)-objs += ' >> $(OBJDIR)/Makefile + @for file in $($(APP)_OBJS); do \ + echo -n "$$file " >> $(OBJDIR)/Makefile; \ + done + @echo >> $(OBJDIR)/Makefile + @echo >> $(OBJDIR)/Makefile + @echo "# Prevent .o files to be built by kbuild:" >> $(OBJDIR)/Makefile + @for file in $($(APP)_OBJS); do \ + echo ".PHONY: `pwd`/$(OBJDIR)/$$file" >> $(OBJDIR)/Makefile; \ + done + @echo >> $(OBJDIR)/Makefile + @echo all: >> $(OBJDIR)/Makefile + @echo -e "\tmake -C $(KERNEL_DIR) M=`pwd`/$(OBJDIR) modules $(KERNEL_ARCH)" >> $(OBJDIR)/Makefile + @echo Invoking kbuild... + make -C $(OBJDIR) + +../lib/$(app).ko: $(LIB) $(OBJDIR)/$(app).ko + cp $(OBJDIR)/$(app).ko ../lib + +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c + $(CC) $($(APP)_CFLAGS) \ + $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ + $(subst /,$(HOST_PSEP),$<) + +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.S + $(CC) $($(APP)_CFLAGS) \ + $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ + $(subst /,$(HOST_PSEP),$<) + +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp + $(CC) $($(APP)_CXXFLAGS) \ + $(CC_OUT)$(subst /,$(HOST_PSEP),$@) \ + $(subst /,$(HOST_PSEP),$<) + +$(OBJDIRS): + $(subst @@,$(subst /,$(HOST_PSEP),$@),$(HOST_MKDIR)) + +$(LIBDIR): + $(subst @@,$(subst /,$(HOST_PSEP),$(LIBDIR)),$(HOST_MKDIR)) + +$(BINDIR): + $(subst @@,$(subst /,$(HOST_PSEP),$(BINDIR)),$(HOST_MKDIR)) + +clean: + $(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)/*),$(HOST_RMR)) + $(subst @@,$(subst /,$(HOST_PSEP),$(OBJDIR)),$(HOST_RMDIR)) +ifeq ($(OS_NAME),linux-kernel) + rm -f ../lib/$(app).o +endif + +realclean: clean + $(subst @@,$(subst /,$(HOST_PSEP),$(LIB)) $(subst /,$(HOST_PSEP),$(EXE)),$(HOST_RM)) + $(subst @@,$(DEP_FILE),$(HOST_RM)) +ifeq ($(OS_NAME),linux-kernel) + rm -f ../lib/$(app).ko +endif + +depend: + $(subst @@,$(DEP_FILE),$(HOST_RM)) + for F in $(FULL_SRCS); do \ + if test -f $$F; then \ + bash -c "echo -n $(OBJDIR)/" >> $(DEP_FILE); \ + if gcc -MM $(DEPFLAGS) $$F | sed '/^#/d' >> $(DEP_FILE); then \ + true; \ + else \ + echo 'err:' >> $(DEP_FILE); \ + rm -f $(DEP_FILE); \ + exit 1; \ + fi; \ + fi; \ + done; + +dep: depend + +-include $(DEP_FILE) + diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index 7bffdc998..21b2d3871 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -1,151 +1,151 @@ -# -# Include host/target/compiler selection. -# This will export CC_NAME, MACHINE_NAME, OS_NAME, and HOST_NAME variables. -# -include ../../build.mak - -# -# Include global compiler specific definitions -# -include ../../build/cc-$(CC_NAME).mak - -# -# (Optionally) Include compiler specific configuration that is -# specific to this project. This configuration file is -# located in this directory. -# --include cc-$(CC_NAME).mak - -# -# Include global machine specific definitions -# -include ../../build/m-$(MACHINE_NAME).mak --include m-$(MACHINE_NAME).mak - -# -# Include target OS specific definitions -# -include ../../build/os-$(OS_NAME).mak - -# -# (Optionally) Include target OS specific configuration that is -# specific to this project. This configuration file is -# located in this directory. -# --include os-$(OS_NAME).mak - -# -# Include host specific definitions -# -include ../../build/host-$(HOST_NAME).mak - -# -# (Optionally) Include host specific configuration that is -# specific to this project. This configuration file is -# located in this directory. -# --include host-$(HOST_NAME).mak - -# -# Include global user configuration, if any -# --include ../../user.mak - - -RULES_MAK := ../../build/rules.mak - - -export PJLIB_LIB := ../lib/libpj-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) - -############################################################################### -# Gather all flags. -# -export _CFLAGS := $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ - $(CFLAGS) $(CC_INC)../include -export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ - $(HOST_CXXFLAGS) $(CXXFLAGS) -export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \ - $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ - $(LDFLAGS) - -############################################################################### -# Defines for building PJLIB library -# -export PJLIB_SRCDIR = ../src/pj -export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ - array.o config.o errno.o except.o fifobuf.o guid.o \ - hash.o list.o lock.o log.o \ - md5.o pool.o pool_caching.o rand.o \ - rbtree.o scanner.o string.o stun.o stun_client.o timer.o \ - types.o xml.o symbols.o -export PJLIB_CFLAGS += $(_CFLAGS) - -############################################################################### -# Defines for building test application -# -export TEST_SRCDIR = ../src/pjlib-test -export TEST_OBJS += atomic.o echo_clt.o echo_srv.o errno.o exception.o \ - fifobuf.o \ - ioq_perf.o ioq_udp.o ioq_tcp.o \ - list.o mutex.o os.o pool.o pool_perf.o rand.o rbtree.o \ - select.o sleep.o sock.o sock_perf.o \ - string.o test.o thread.o timer.o timestamp.o \ - udp_echo_srv_sync.o \ - util.o xml.o -export TEST_CFLAGS += $(_CFLAGS) -export TEST_LDFLAGS += $(_LDFLAGS) -export TEST_EXE := ../bin/pjlib-test-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE) - - -export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT - -############################################################################### -# Main entry -# -# $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. -# - -all: $(TARGETS) - -doc: - cd .. && doxygen docs/doxygen.cfg - -print: - $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib print_lib - $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test print_bin - -depend: - $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib depend - $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test depend - echo '$(TEST_EXE): $(PJLIB_LIB)' >> .pjlib-test-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).depend - - -.PHONY: dep depend pjlib pjlib-test clean realclean distclean - -dep: depend - -pjlib: - $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib $(PJLIB_LIB) - -pjlib-test: - $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test $(TEST_EXE) - -.PHONY: ../lib/pjlib.ko -../lib/pjlib.ko: - echo Making $@ - $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib $@ - -.PHONY: ../lib/pjlib-test.ko -../lib/pjlib-test.ko: - $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test $@ - -clean: - $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib clean - $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test clean - -realclean: - $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib realclean - $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test realclean - -distclean: realclean - +# +# Include host/target/compiler selection. +# This will export CC_NAME, MACHINE_NAME, OS_NAME, and HOST_NAME variables. +# +include ../../build.mak + +# +# Include global compiler specific definitions +# +include ../../build/cc-$(CC_NAME).mak + +# +# (Optionally) Include compiler specific configuration that is +# specific to this project. This configuration file is +# located in this directory. +# +-include cc-$(CC_NAME).mak + +# +# Include global machine specific definitions +# +include ../../build/m-$(MACHINE_NAME).mak +-include m-$(MACHINE_NAME).mak + +# +# Include target OS specific definitions +# +include ../../build/os-$(OS_NAME).mak + +# +# (Optionally) Include target OS specific configuration that is +# specific to this project. This configuration file is +# located in this directory. +# +-include os-$(OS_NAME).mak + +# +# Include host specific definitions +# +include ../../build/host-$(HOST_NAME).mak + +# +# (Optionally) Include host specific configuration that is +# specific to this project. This configuration file is +# located in this directory. +# +-include host-$(HOST_NAME).mak + +# +# Include global user configuration, if any +# +-include ../../user.mak + + +RULES_MAK := ../../build/rules.mak + + +export PJLIB_LIB := ../lib/libpj-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(LIBEXT) + +############################################################################### +# Gather all flags. +# +export _CFLAGS := -O2 $(CC_CFLAGS) $(OS_CFLAGS) $(HOST_CFLAGS) $(M_CFLAGS) \ + $(CFLAGS) $(CC_INC)../include +export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \ + $(HOST_CXXFLAGS) $(CXXFLAGS) +export _LDFLAGS := $(subst /,$(HOST_PSEP),$(PJLIB_LIB)) \ + $(CC_LDFLAGS) $(OS_LDFLAGS) $(M_LDFLAGS) $(HOST_LDFLAGS) \ + $(LDFLAGS) + +############################################################################### +# Defines for building PJLIB library +# +export PJLIB_SRCDIR = ../src/pj +export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ + array.o config.o errno.o except.o fifobuf.o guid.o \ + hash.o list.o lock.o log.o \ + md5.o pool.o pool_caching.o rand.o \ + rbtree.o scanner.o string.o stun.o stun_client.o timer.o \ + types.o xml.o symbols.o +export PJLIB_CFLAGS += $(_CFLAGS) + +############################################################################### +# Defines for building test application +# +export TEST_SRCDIR = ../src/pjlib-test +export TEST_OBJS += atomic.o echo_clt.o echo_srv.o errno.o exception.o \ + fifobuf.o \ + ioq_perf.o ioq_udp.o ioq_tcp.o \ + list.o mutex.o os.o pool.o pool_perf.o rand.o rbtree.o \ + select.o sleep.o sock.o sock_perf.o \ + string.o test.o thread.o timer.o timestamp.o \ + udp_echo_srv_sync.o \ + util.o xml.o +export TEST_CFLAGS += $(_CFLAGS) +export TEST_LDFLAGS += $(_LDFLAGS) +export TEST_EXE := ../bin/pjlib-test-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME)$(HOST_EXE) + + +export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT + +############################################################################### +# Main entry +# +# $(TARGET) is defined in os-$(OS_NAME).mak file in current directory. +# + +all: $(TARGETS) + +doc: + cd .. && doxygen docs/doxygen.cfg + +print: + $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib print_lib + $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test print_bin + +depend: + $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib depend + $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test depend + echo '$(TEST_EXE): $(PJLIB_LIB)' >> .pjlib-test-$(MACHINE_NAME)-$(OS_NAME)-$(CC_NAME).depend + + +.PHONY: dep depend pjlib pjlib-test clean realclean distclean + +dep: depend + +pjlib: + $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib $(PJLIB_LIB) + +pjlib-test: + $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test $(TEST_EXE) + +.PHONY: ../lib/pjlib.ko +../lib/pjlib.ko: + echo Making $@ + $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib $@ + +.PHONY: ../lib/pjlib-test.ko +../lib/pjlib-test.ko: + $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test $@ + +clean: + $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib clean + $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test clean + +realclean: + $(MAKE) -f $(RULES_MAK) APP=PJLIB app=pjlib realclean + $(MAKE) -f $(RULES_MAK) APP=TEST app=pjlib-test realclean + +distclean: realclean + diff --git a/pjlib/build/os-linux-kernel.mak b/pjlib/build/os-linux-kernel.mak index 1503156ae..934c88981 100644 --- a/pjlib/build/os-linux-kernel.mak +++ b/pjlib/build/os-linux-kernel.mak @@ -1,47 +1,47 @@ -# -# OS specific configuration for Linux Kernel module target. -# - -# -# PJLIB_OBJS specified here are object files to be included in PJLIB -# (the library) for this specific operating system. Object files common -# to all operating systems should go in Makefile instead. -# -export PJLIB_OBJS += compat/sigjmp.o compat/setjmp_i386.o \ - compat/longjmp_i386.o compat/string.o \ - addr_resolv_linux_kernel.o \ - guid_simple.o \ - log_writer_printk.o pool_policy_kmalloc.o \ - os_error_linux_kernel.o os_core_linux_kernel.o \ - os_time_linux_kernel.o os_timestamp_common.o \ - os_timestamp_linux_kernel.o \ - sock_linux_kernel.o sock_select.o - -# For IOQueue, we can use either epoll or select -export PJLIB_OBJS += ioqueue_epoll.o -#export PJLIB_OBJS += ioqueue_select.o - -# -# TEST_OBJS are operating system specific object files to be included in -# the test application. -# -export TEST_OBJS += main_mod.o - -# -# Additional CFLAGS -# -export TEST_CFLAGS += -msoft-float - -# -# Additional LD_FLAGS for this target. -# -export TEST_LDFLAGS += -lgcc - - -# -# TARGETS are make targets in the Makefile, to be executed for this given -# operating system. -# -export TARGETS := ../lib/pjlib.ko ../lib/pjlib-test.ko - - +# +# OS specific configuration for Linux Kernel module target. +# + +# +# PJLIB_OBJS specified here are object files to be included in PJLIB +# (the library) for this specific operating system. Object files common +# to all operating systems should go in Makefile instead. +# +export PJLIB_OBJS += compat/sigjmp.o compat/setjmp_i386.o \ + compat/longjmp_i386.o compat/string.o \ + addr_resolv_linux_kernel.o \ + guid_simple.o \ + log_writer_printk.o pool_policy_kmalloc.o \ + os_error_linux_kernel.o os_core_linux_kernel.o \ + os_time_linux_kernel.o os_timestamp_common.o \ + os_timestamp_linux_kernel.o \ + sock_linux_kernel.o sock_select.o + +# For IOQueue, we can use either epoll or select +export PJLIB_OBJS += ioqueue_epoll.o +#export PJLIB_OBJS += ioqueue_select.o + +# +# TEST_OBJS are operating system specific object files to be included in +# the test application. +# +export TEST_OBJS += main_mod.o + +# +# Additional CFLAGS +# +export TEST_CFLAGS += -msoft-float + +# +# Additional LD_FLAGS for this target. +# +export TEST_LDFLAGS += -lgcc + + +# +# TARGETS are make targets in the Makefile, to be executed for this given +# operating system. +# +export TARGETS := ../lib/pjlib.ko ../lib/pjlib-test.ko + + diff --git a/pjlib/build/os-linux.mak b/pjlib/build/os-linux.mak index 41dff5347..fd9fc307f 100644 --- a/pjlib/build/os-linux.mak +++ b/pjlib/build/os-linux.mak @@ -1,36 +1,36 @@ -# -# OS specific configuration for Linux OS target. -# - -# -# PJLIB_OBJS specified here are object files to be included in PJLIB -# (the library) for this specific operating system. Object files common -# to all operating systems should go in Makefile instead. -# -export PJLIB_OBJS += addr_resolv_sock.o guid_simple.o \ - log_writer_stdout.o os_core_unix.o \ - os_error_unix.o os_time_ansi.o \ - os_timestamp_common.o os_timestamp_linux.o \ - os_time_ansi.o \ - pool_policy_malloc.o sock_bsd.o sock_select.o - -export PJLIB_OBJS += ioqueue_select.o -#export PJLIB_OBJS += ioqueue_epoll.o - -# -# TEST_OBJS are operating system specific object files to be included in -# the test application. -# -export TEST_OBJS += main.o - -# -# Additional LDFLAGS for pjlib-test -# -export TEST_LDFLAGS += -lm - -# -# TARGETS are make targets in the Makefile, to be executed for this given -# operating system. -# -export TARGETS = pjlib pjlib-test - +# +# OS specific configuration for Linux OS target. +# + +# +# PJLIB_OBJS specified here are object files to be included in PJLIB +# (the library) for this specific operating system. Object files common +# to all operating systems should go in Makefile instead. +# +export PJLIB_OBJS += addr_resolv_sock.o guid_simple.o \ + log_writer_stdout.o os_core_unix.o \ + os_error_unix.o os_time_ansi.o \ + os_timestamp_common.o os_timestamp_linux.o \ + os_time_ansi.o \ + pool_policy_malloc.o sock_bsd.o sock_select.o + +export PJLIB_OBJS += ioqueue_select.o +#export PJLIB_OBJS += ioqueue_epoll.o + +# +# TEST_OBJS are operating system specific object files to be included in +# the test application. +# +export TEST_OBJS += main.o + +# +# Additional LDFLAGS for pjlib-test +# +export TEST_LDFLAGS += -lm + +# +# TARGETS are make targets in the Makefile, to be executed for this given +# operating system. +# +export TARGETS = pjlib pjlib-test + diff --git a/pjlib/build/os-sunos.mak b/pjlib/build/os-sunos.mak new file mode 100644 index 000000000..81ab64da5 --- /dev/null +++ b/pjlib/build/os-sunos.mak @@ -0,0 +1,36 @@ +# +# PJLIB OS specific configuration for SunOS target. +# + +# +# PJLIB_OBJS specified here are object files to be included in PJLIB +# (the library) for this specific operating system. Object files common +# to all operating systems should go in Makefile instead. +# +export PJLIB_OBJS += addr_resolv_sock.o guid_simple.o \ + log_writer_stdout.o os_core_unix.o \ + os_error_unix.o os_time_ansi.o \ + os_timestamp_common.o os_timestamp_linux.o \ + os_time_ansi.o \ + pool_policy_malloc.o sock_bsd.o sock_select.o + +export PJLIB_OBJS += ioqueue_select.o +#export PJLIB_OBJS += ioqueue_epoll.o + +# +# TEST_OBJS are operating system specific object files to be included in +# the test application. +# +export TEST_OBJS += main.o + +# +# Additional LDFLAGS for pjlib-test +# +export TEST_LDFLAGS += -lm + +# +# TARGETS are make targets in the Makefile, to be executed for this given +# operating system. +# +export TARGETS = pjlib pjlib-test + diff --git a/pjlib/build/os-win32.mak b/pjlib/build/os-win32.mak index fbe3a66cd..7b236c6ab 100644 --- a/pjlib/build/os-win32.mak +++ b/pjlib/build/os-win32.mak @@ -1,27 +1,27 @@ -# -# OS specific configuration for Win32 OS target. -# - -# -# PJLIB_OBJS specified here are object files to be included in PJLIB -# (the library) for this specific operating system. Object files common -# to all operating systems should go in Makefile instead. -# -export PJLIB_OBJS += addr_resolv_sock.o guid_win32.o ioqueue_winnt.o \ - log_writer_stdout.o os_core_win32.o \ - os_error_win32.o os_time_ansi.o os_timestamp_common.o \ - os_timestamp_win32.o \ - pool_policy_malloc.o sock_bsd.o sock_select.o - -# -# TEST_OBJS are operating system specific object files to be included in -# the test application. -# -export TEST_OBJS += main.o - -# -# TARGETS are make targets in the Makefile, to be executed for this given -# operating system. -# -export TARGETS = pjlib pjlib-test - +# +# OS specific configuration for Win32 OS target. +# + +# +# PJLIB_OBJS specified here are object files to be included in PJLIB +# (the library) for this specific operating system. Object files common +# to all operating systems should go in Makefile instead. +# +export PJLIB_OBJS += addr_resolv_sock.o guid_win32.o ioqueue_winnt.o \ + log_writer_stdout.o os_core_win32.o \ + os_error_win32.o os_time_ansi.o os_timestamp_common.o \ + os_timestamp_win32.o \ + pool_policy_malloc.o sock_bsd.o sock_select.o + +# +# TEST_OBJS are operating system specific object files to be included in +# the test application. +# +export TEST_OBJS += main.o + +# +# TARGETS are make targets in the Makefile, to be executed for this given +# operating system. +# +export TARGETS = pjlib pjlib-test + diff --git a/pjlib/build/pjlib_samples.mak b/pjlib/build/pjlib_samples.mak index 21b42072a..322cc212a 100644 --- a/pjlib/build/pjlib_samples.mak +++ b/pjlib/build/pjlib_samples.mak @@ -1,35 +1,35 @@ -OUTDIR=.\output\pjlib-samples-i386-win32-vc6-$(MODE) - -SRCDIR=../src/pjlib-samples - -SAMPLES=$(OUTDIR)/except.exe \ - $(OUTDIR)/log.exe \ - $(OUTDIR)/list.exe \ - -!IF "$(MODE)" == "debug" -MODE_CFLAGS=/MTd -!ELSE -MODE_CFLAGS=/MT -!ENDIF - -CFLAGS=/nologo /W4 $(MODE_CFLAGS) /DPJ_WIN32=1 /DPJ_M_I386=1 /I../include - -PJLIB=../lib/pjlib-i386-win32-vc6-$(MODE).lib - -DEPEND=$(PJLIB) -LIBS=netapi32.lib mswsock.lib ws2_32.lib ole32.lib -CL=cl.exe - -all: "$(OUTDIR)" $(SAMPLES) - -$(SAMPLES): "$(SRCDIR)/$(@B).c" $(DEPEND) - $(CL) /Fe$@ \ - /Fo$(@R).obj \ - $(CFLAGS) \ - $** $(LIBS) - -"$(OUTDIR)" : - @IF NOT EXIST "$(OUTDIR)" MKDIR "$(OUTDIR)" - -clean : - @IF EXIST "$(OUTDIR)" DEL /Q "$(OUTDIR)\*.*" && RMDIR "$(OUTDIR)" +OUTDIR=.\output\pjlib-samples-i386-win32-vc6-$(MODE) + +SRCDIR=../src/pjlib-samples + +SAMPLES=$(OUTDIR)/except.exe \ + $(OUTDIR)/log.exe \ + $(OUTDIR)/list.exe \ + +!IF "$(MODE)" == "debug" +MODE_CFLAGS=/MTd +!ELSE +MODE_CFLAGS=/MT +!ENDIF + +CFLAGS=/nologo /W4 $(MODE_CFLAGS) /DPJ_WIN32=1 /DPJ_M_I386=1 /I../include + +PJLIB=../lib/pjlib-i386-win32-vc6-$(MODE).lib + +DEPEND=$(PJLIB) +LIBS=netapi32.lib mswsock.lib ws2_32.lib ole32.lib +CL=cl.exe + +all: "$(OUTDIR)" $(SAMPLES) + +$(SAMPLES): "$(SRCDIR)/$(@B).c" $(DEPEND) + $(CL) /Fe$@ \ + /Fo$(@R).obj \ + $(CFLAGS) \ + $** $(LIBS) + +"$(OUTDIR)" : + @IF NOT EXIST "$(OUTDIR)" MKDIR "$(OUTDIR)" + +clean : + @IF EXIST "$(OUTDIR)" DEL /Q "$(OUTDIR)\*.*" && RMDIR "$(OUTDIR)" diff --git a/pjlib/docs/doxygen.css b/pjlib/docs/doxygen.css index accad1412..015c0c272 100644 --- a/pjlib/docs/doxygen.css +++ b/pjlib/docs/doxygen.css @@ -1,305 +1,305 @@ -BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { - font-family: Geneva, Arial, Helvetica, sans-serif; -} -BODY,TD { - font-size: 80%; -} -CODE { - font-size: 120%; - font-family: monospace; -} -.fragment, pre { - font-size: 110%; - font-family: monospace; -} -H1 { - text-align: center; - font-size: 240%; -} -H2 { - font-size: 200%; - margin-top : 60px; -} -H3 { - font-size: 160%; -} -H4 { - font-size: 120%; -} -CAPTION { font-weight: bold } -DIV.qindex { - width: 100%; - background-color: #eeeeff; - border: 1px solid #b0b0b0; - text-align: center; - margin: 2px; - padding: 2px; - line-height: 140%; -} -DIV.nav { - width: 100%; - background-color: #eeeeff; - border: 1px solid #b0b0b0; - text-align: center; - margin: 2px; - padding: 2px; - line-height: 140%; -} -A.qindex { - text-decoration: none; - font-size: 120%; - color: #1A419D; -} -A.qindex:visited { - text-decoration: none; - color: #1A419D -} -A.qindex:hover { - text-decoration: none; - background-color: #ddddff; -} -A.qindexHL { - text-decoration: none; - font-weight: bold; - background-color: #6666cc; - color: #ffffff; - border: 1px double #9295C2; -} -A.qindexHL:hover { - text-decoration: none; - background-color: #6666cc; - color: #ffffff; -} -A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } -A.el { text-decoration: none; font-weight: bold } -A.elRef { font-weight: bold } -A.code:link { text-decoration: none; font-weight: normal; color: #0000FF; } -A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} -A.codeRef:link { font-weight: normal; color: #0000FF} -A.codeRef:visited { font-weight: normal; color: #0000FF} -A:hover { text-decoration: none; background-color: #f2f2ff } -DL.el { margin-left: -1cm } -PRE.fragment { - border: 1px solid #CCCCCC; - background-color: #f5f5f5; - margin-top: 4px; - margin-bottom: 4px; - margin-left: 2px; - margin-right: 8px; - padding-left: 6px; - padding-right: 6px; - padding-top: 4px; - padding-bottom: 4px; -} -DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } -TD.md { background-color: #F4F4FB; font-weight: bold; } -TD.mdPrefix { - background-color: #F4F4FB; - color: #606060; - font-size: 80%; -} -TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } -TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } -DIV.groupHeader { - margin-left: 16px; - margin-top: 12px; - margin-bottom: 6px; - font-weight: bold; -} -DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } -BODY { - background: white; - color: black; - margin-right: 20px; - margin-left: 20px; -} -TD.indexkey { - background-color: #eeeeff; - font-weight: bold; - padding-right : 10px; - padding-top : 2px; - padding-left : 10px; - padding-bottom : 2px; - margin-left : 0px; - margin-right : 0px; - margin-top : 2px; - margin-bottom : 2px; - border: 1px solid #CCCCCC; -} -TD.indexvalue { - background-color: #eeeeff; - font-style: italic; - padding-right : 10px; - padding-top : 2px; - padding-left : 10px; - padding-bottom : 2px; - margin-left : 0px; - margin-right : 0px; - margin-top : 2px; - margin-bottom : 2px; - border: 1px solid #CCCCCC; -} -TR.memlist { - background-color: #f0f0f0; -} -P.formulaDsp { text-align: center; } -IMG.formulaDsp { } -IMG.formulaInl { vertical-align: middle; } -SPAN.keyword { color: #008000 } -SPAN.keywordtype { color: #604020 } -SPAN.keywordflow { color: #e08000 } -SPAN.comment { color: #800000 } -SPAN.preprocessor { color: #806020 } -SPAN.stringliteral { color: #002080 } -SPAN.charliteral { color: #008080 } -.mdTable { - border: 1px solid #868686; - background-color: #F4F4FB; -} -.mdRow { - padding: 8px 10px; -} -.mdescLeft { - padding: 0px 8px 4px 8px; - font-size: 80%; - font-style: italic; - background-color: #FAFAFA; - border-top: 1px none #E0E0E0; - border-right: 1px none #E0E0E0; - border-bottom: 1px none #E0E0E0; - border-left: 1px none #E0E0E0; - margin: 0px; -} -.mdescRight { - padding: 0px 8px 4px 8px; - font-size: 80%; - font-style: italic; - background-color: #FAFAFA; - border-top: 1px none #E0E0E0; - border-right: 1px none #E0E0E0; - border-bottom: 1px none #E0E0E0; - border-left: 1px none #E0E0E0; - margin: 0px; -} -.memItemLeft { - padding: 1px 0px 0px 8px; - margin: 4px; - border-top-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - border-left-width: 1px; - border-top-color: #E0E0E0; - border-right-color: #E0E0E0; - border-bottom-color: #E0E0E0; - border-left-color: #E0E0E0; - border-top-style: solid; - border-right-style: none; - border-bottom-style: none; - border-left-style: none; - background-color: #FAFAFA; - font-size: 80%; -} -.memItemRight { - padding: 1px 8px 0px 8px; - margin: 4px; - border-top-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - border-left-width: 1px; - border-top-color: #E0E0E0; - border-right-color: #E0E0E0; - border-bottom-color: #E0E0E0; - border-left-color: #E0E0E0; - border-top-style: solid; - border-right-style: none; - border-bottom-style: none; - border-left-style: none; - background-color: #FAFAFA; - font-size: 80%; -} -.memTemplItemLeft { - padding: 1px 0px 0px 8px; - margin: 4px; - border-top-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - border-left-width: 1px; - border-top-color: #E0E0E0; - border-right-color: #E0E0E0; - border-bottom-color: #E0E0E0; - border-left-color: #E0E0E0; - border-top-style: none; - border-right-style: none; - border-bottom-style: none; - border-left-style: none; - background-color: #FAFAFA; - font-size: 80%; -} -.memTemplItemRight { - padding: 1px 8px 0px 8px; - margin: 4px; - border-top-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - border-left-width: 1px; - border-top-color: #E0E0E0; - border-right-color: #E0E0E0; - border-bottom-color: #E0E0E0; - border-left-color: #E0E0E0; - border-top-style: none; - border-right-style: none; - border-bottom-style: none; - border-left-style: none; - background-color: #FAFAFA; - font-size: 80%; -} -.memTemplParams { - padding: 1px 0px 0px 8px; - margin: 4px; - border-top-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - border-left-width: 1px; - border-top-color: #E0E0E0; - border-right-color: #E0E0E0; - border-bottom-color: #E0E0E0; - border-left-color: #E0E0E0; - border-top-style: solid; - border-right-style: none; - border-bottom-style: none; - border-left-style: none; - color: #606060; - background-color: #FAFAFA; - font-size: 80%; -} -.search { color: #003399; - font-weight: bold; -} -FORM.search { - margin-bottom: 0px; - margin-top: 0px; -} -INPUT.search { font-size: 75%; - color: #000080; - font-weight: normal; - background-color: #eeeeff; -} -TD.tiny { font-size: 75%; -} -a { - color: #252E78; -} -a:visited { - color: #3D2185; -} -.dirtab { padding: 4px; - border-collapse: collapse; - border: 1px solid #b0b0b0; -} -TH.dirtab { background: #eeeeff; - font-weight: bold; -} -HR { height: 1px; - border: none; - border-top: 1px solid black; -} +BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { + font-family: Geneva, Arial, Helvetica, sans-serif; +} +BODY,TD { + font-size: 80%; +} +CODE { + font-size: 120%; + font-family: monospace; +} +.fragment, pre { + font-size: 110%; + font-family: monospace; +} +H1 { + text-align: center; + font-size: 240%; +} +H2 { + font-size: 200%; + margin-top : 60px; +} +H3 { + font-size: 160%; +} +H4 { + font-size: 120%; +} +CAPTION { font-weight: bold } +DIV.qindex { + width: 100%; + background-color: #eeeeff; + border: 1px solid #b0b0b0; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +DIV.nav { + width: 100%; + background-color: #eeeeff; + border: 1px solid #b0b0b0; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +A.qindex { + text-decoration: none; + font-size: 120%; + color: #1A419D; +} +A.qindex:visited { + text-decoration: none; + color: #1A419D +} +A.qindex:hover { + text-decoration: none; + background-color: #ddddff; +} +A.qindexHL { + text-decoration: none; + font-weight: bold; + background-color: #6666cc; + color: #ffffff; + border: 1px double #9295C2; +} +A.qindexHL:hover { + text-decoration: none; + background-color: #6666cc; + color: #ffffff; +} +A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } +A.el { text-decoration: none; font-weight: bold } +A.elRef { font-weight: bold } +A.code:link { text-decoration: none; font-weight: normal; color: #0000FF; } +A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} +A.codeRef:link { font-weight: normal; color: #0000FF} +A.codeRef:visited { font-weight: normal; color: #0000FF} +A:hover { text-decoration: none; background-color: #f2f2ff } +DL.el { margin-left: -1cm } +PRE.fragment { + border: 1px solid #CCCCCC; + background-color: #f5f5f5; + margin-top: 4px; + margin-bottom: 4px; + margin-left: 2px; + margin-right: 8px; + padding-left: 6px; + padding-right: 6px; + padding-top: 4px; + padding-bottom: 4px; +} +DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } +TD.md { background-color: #F4F4FB; font-weight: bold; } +TD.mdPrefix { + background-color: #F4F4FB; + color: #606060; + font-size: 80%; +} +TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } +TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } +DIV.groupHeader { + margin-left: 16px; + margin-top: 12px; + margin-bottom: 6px; + font-weight: bold; +} +DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } +BODY { + background: white; + color: black; + margin-right: 20px; + margin-left: 20px; +} +TD.indexkey { + background-color: #eeeeff; + font-weight: bold; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TD.indexvalue { + background-color: #eeeeff; + font-style: italic; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TR.memlist { + background-color: #f0f0f0; +} +P.formulaDsp { text-align: center; } +IMG.formulaDsp { } +IMG.formulaInl { vertical-align: middle; } +SPAN.keyword { color: #008000 } +SPAN.keywordtype { color: #604020 } +SPAN.keywordflow { color: #e08000 } +SPAN.comment { color: #800000 } +SPAN.preprocessor { color: #806020 } +SPAN.stringliteral { color: #002080 } +SPAN.charliteral { color: #008080 } +.mdTable { + border: 1px solid #868686; + background-color: #F4F4FB; +} +.mdRow { + padding: 8px 10px; +} +.mdescLeft { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.mdescRight { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.memItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplParams { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + color: #606060; + background-color: #FAFAFA; + font-size: 80%; +} +.search { color: #003399; + font-weight: bold; +} +FORM.search { + margin-bottom: 0px; + margin-top: 0px; +} +INPUT.search { font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #eeeeff; +} +TD.tiny { font-size: 75%; +} +a { + color: #252E78; +} +a:visited { + color: #3D2185; +} +.dirtab { padding: 4px; + border-collapse: collapse; + border: 1px solid #b0b0b0; +} +TH.dirtab { background: #eeeeff; + font-weight: bold; +} +HR { height: 1px; + border: none; + border-top: 1px solid black; +} diff --git a/pjlib/include/pj/addr_resolv.h b/pjlib/include/pj/addr_resolv.h index 2daf07335..9b466918f 100644 --- a/pjlib/include/pj/addr_resolv.h +++ b/pjlib/include/pj/addr_resolv.h @@ -1,76 +1,76 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/addr_resolv.h 2 10/14/05 12:25a Bennylp $ */ - -#ifndef __PJ_ADDR_RESOLV_H__ -#define __PJ_ADDR_RESOLV_H__ - -/** - * @file addr_resolv.h - * @brief Address resolve (pj_gethostbyname()). - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup pj_addr_resolve Address Resolution - * @ingroup PJ_IO - * @{ - * - * This module provides function to resolve Internet address of the - * specified host name. To resolve a particular host name, application - * can just call #pj_gethostbyname(). - * - * Example: - *
- *   ...
- *   pj_hostent he;
- *   pj_status_t rc;
- *   pj_str_t host = pj_str("host.example.com");
- *   
- *   rc = pj_gethostbyname( &host, &he);
- *   if (rc != PJ_SUCCESS) {
- *      char errbuf[80];
- *      pj_strerror( rc, errbuf, sizeof(errbuf));
- *      PJ_LOG(2,("sample", "Unable to resolve host, error=%s", errbuf));
- *      return rc;
- *   }
- *
- *   // process address...
- *   addr.sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
- *   ...
- * 
- * - * It's pretty simple really... - */ - -/** This structure describes an Internet host address. */ -typedef struct pj_hostent -{ - char *h_name; /**< The official name of the host. */ - char **h_aliases; /**< Aliases list. */ - int h_addrtype; /**< Host address type. */ - int h_length; /**< Length of address. */ - char **h_addr_list; /**< List of addresses. */ -} pj_hostent; - -/** Shortcut to h_addr_list[0] */ -#define h_addr h_addr_list[0] - -/** - * This function fills the structure of type pj_hostent for a given host name. - * - * @param name Host name, or IPv4 or IPv6 address in standard dot notation. - * @param he The pj_hostent structure to be filled. - * - * @return PJ_SUCCESS, or the appropriate error codes. - */ -PJ_DECL(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he); - - -/** @} */ - -PJ_END_DECL - -#endif /* __PJ_ADDR_RESOLV_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/addr_resolv.h 2 10/14/05 12:25a Bennylp $ */ + +#ifndef __PJ_ADDR_RESOLV_H__ +#define __PJ_ADDR_RESOLV_H__ + +/** + * @file addr_resolv.h + * @brief Address resolve (pj_gethostbyname()). + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup pj_addr_resolve Address Resolution + * @ingroup PJ_IO + * @{ + * + * This module provides function to resolve Internet address of the + * specified host name. To resolve a particular host name, application + * can just call #pj_gethostbyname(). + * + * Example: + *
+ *   ...
+ *   pj_hostent he;
+ *   pj_status_t rc;
+ *   pj_str_t host = pj_str("host.example.com");
+ *   
+ *   rc = pj_gethostbyname( &host, &he);
+ *   if (rc != PJ_SUCCESS) {
+ *      char errbuf[80];
+ *      pj_strerror( rc, errbuf, sizeof(errbuf));
+ *      PJ_LOG(2,("sample", "Unable to resolve host, error=%s", errbuf));
+ *      return rc;
+ *   }
+ *
+ *   // process address...
+ *   addr.sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+ *   ...
+ * 
+ * + * It's pretty simple really... + */ + +/** This structure describes an Internet host address. */ +typedef struct pj_hostent +{ + char *h_name; /**< The official name of the host. */ + char **h_aliases; /**< Aliases list. */ + int h_addrtype; /**< Host address type. */ + int h_length; /**< Length of address. */ + char **h_addr_list; /**< List of addresses. */ +} pj_hostent; + +/** Shortcut to h_addr_list[0] */ +#define h_addr h_addr_list[0] + +/** + * This function fills the structure of type pj_hostent for a given host name. + * + * @param name Host name, or IPv4 or IPv6 address in standard dot notation. + * @param he The pj_hostent structure to be filled. + * + * @return PJ_SUCCESS, or the appropriate error codes. + */ +PJ_DECL(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he); + + +/** @} */ + +PJ_END_DECL + +#endif /* __PJ_ADDR_RESOLV_H__ */ + diff --git a/pjlib/include/pj/array.h b/pjlib/include/pj/array.h index bdc83d9b2..1e9e158b2 100644 --- a/pjlib/include/pj/array.h +++ b/pjlib/include/pj/array.h @@ -1,78 +1,78 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/array.h 7 10/14/05 12:25a Bennylp $ */ -#ifndef __PJ_ARRAY_H__ -#define __PJ_ARRAY_H__ - -/** - * @file array.h - * @brief PJLIB Array helper. - */ -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_ARRAY Array helper. - * @ingroup PJ_DS - * @{ - * - * This module provides helper to manipulate array of elements of any size. - * It provides most used array operations such as insert, erase, and search. - */ - -/** - * Insert value to the array at the given position, and rearrange the - * remaining nodes after the position. - * - * @param array the array. - * @param elem_size the size of the individual element. - * @param count the current number of elements in the array. - * @param pos the position where the new element is put. - * @param value the value to copy to the new element. - */ -PJ_DECL(void) pj_array_insert( void *array, - unsigned elem_size, - unsigned count, - unsigned pos, - const void *value); - -/** - * Erase a value from the array at given position, and rearrange the remaining - * elements post the erased element. - * - * @param array the array. - * @param elem_size the size of the individual element. - * @param count the current number of elements in the array. - * @param pos the index/position to delete. - */ -PJ_DECL(void) pj_array_erase( void *array, - unsigned elem_size, - unsigned count, - unsigned pos); - -/** - * Search the first value in the array according to matching function. - * - * @param array the array. - * @param elem_size the individual size of the element. - * @param count the number of elements. - * @param matching the matching function, which MUST return PJ_SUCCESS if - * the specified element match. - * @param result the pointer to the value found. - * - * @return PJ_SUCCESS if value is found, otherwise the error code. - */ -PJ_DECL(pj_status_t) pj_array_find( const void *array, - unsigned elem_size, - unsigned count, - pj_status_t (*matching)(const void *value), - void **result); - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJ_ARRAY_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/array.h 7 10/14/05 12:25a Bennylp $ */ +#ifndef __PJ_ARRAY_H__ +#define __PJ_ARRAY_H__ + +/** + * @file array.h + * @brief PJLIB Array helper. + */ +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_ARRAY Array helper. + * @ingroup PJ_DS + * @{ + * + * This module provides helper to manipulate array of elements of any size. + * It provides most used array operations such as insert, erase, and search. + */ + +/** + * Insert value to the array at the given position, and rearrange the + * remaining nodes after the position. + * + * @param array the array. + * @param elem_size the size of the individual element. + * @param count the current number of elements in the array. + * @param pos the position where the new element is put. + * @param value the value to copy to the new element. + */ +PJ_DECL(void) pj_array_insert( void *array, + unsigned elem_size, + unsigned count, + unsigned pos, + const void *value); + +/** + * Erase a value from the array at given position, and rearrange the remaining + * elements post the erased element. + * + * @param array the array. + * @param elem_size the size of the individual element. + * @param count the current number of elements in the array. + * @param pos the index/position to delete. + */ +PJ_DECL(void) pj_array_erase( void *array, + unsigned elem_size, + unsigned count, + unsigned pos); + +/** + * Search the first value in the array according to matching function. + * + * @param array the array. + * @param elem_size the individual size of the element. + * @param count the number of elements. + * @param matching the matching function, which MUST return PJ_SUCCESS if + * the specified element match. + * @param result the pointer to the value found. + * + * @return PJ_SUCCESS if value is found, otherwise the error code. + */ +PJ_DECL(pj_status_t) pj_array_find( const void *array, + unsigned elem_size, + unsigned count, + pj_status_t (*matching)(const void *value), + void **result); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJ_ARRAY_H__ */ + diff --git a/pjlib/include/pj/assert.h b/pjlib/include/pj/assert.h index 74705509b..96fe83948 100644 --- a/pjlib/include/pj/assert.h +++ b/pjlib/include/pj/assert.h @@ -1,56 +1,56 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/assert.h 4 10/14/05 12:25a Bennylp $ */ -#ifndef __PJ_ASSERT_H__ -#define __PJ_ASSERT_H__ - -/** - * @file assert.h - * @brief Assertion macro pj_assert(). - */ - -#include -#include - -/** - * @defgroup pj_assert Assertion Macro - * @ingroup PJ_MISC - * @{ - * - * Assertion and other helper macros for sanity checking. - */ - -/** - * @hideinitializer - * Check during debug build that an expression is true. If the expression - * computes to false during run-time, then the program will stop at the - * offending statements. - * For release build, this macro will not do anything. - * - * @param expr The expression to be evaluated. - */ -#define pj_assert(expr) assert(expr) - - -/** - * @hideinitializer - * If #PJ_ENABLE_EXTRA_CHECK is declared and non-zero, then - * #PJ_ASSERT_RETURN macro will evaluate the expression in @a expr during - * run-time. If the expression yields false, assertion will be triggered - * and the current function will return with the specified return value. - * - * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time - * checking will be performed. The macro simply evaluates to pj_assert(expr). - */ -#if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0 -# define PJ_ASSERT_RETURN(expr,retval) \ - do { \ - pj_assert(expr); \ - if (!(expr)) return retval; \ - } while (0) -#else -# define PJ_ASSERT_RETURN(expr,retval) pj_assert(expr) -#endif - -/** @} */ - -#endif /* __PJ_ASSERT_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/assert.h 4 10/14/05 12:25a Bennylp $ */ +#ifndef __PJ_ASSERT_H__ +#define __PJ_ASSERT_H__ + +/** + * @file assert.h + * @brief Assertion macro pj_assert(). + */ + +#include +#include + +/** + * @defgroup pj_assert Assertion Macro + * @ingroup PJ_MISC + * @{ + * + * Assertion and other helper macros for sanity checking. + */ + +/** + * @hideinitializer + * Check during debug build that an expression is true. If the expression + * computes to false during run-time, then the program will stop at the + * offending statements. + * For release build, this macro will not do anything. + * + * @param expr The expression to be evaluated. + */ +#define pj_assert(expr) assert(expr) + + +/** + * @hideinitializer + * If #PJ_ENABLE_EXTRA_CHECK is declared and non-zero, then + * #PJ_ASSERT_RETURN macro will evaluate the expression in @a expr during + * run-time. If the expression yields false, assertion will be triggered + * and the current function will return with the specified return value. + * + * If #PJ_ENABLE_EXTRA_CHECK is not declared or is zero, then no run-time + * checking will be performed. The macro simply evaluates to pj_assert(expr). + */ +#if defined(PJ_ENABLE_EXTRA_CHECK) && PJ_ENABLE_EXTRA_CHECK != 0 +# define PJ_ASSERT_RETURN(expr,retval) \ + do { \ + pj_assert(expr); \ + if (!(expr)) return retval; \ + } while (0) +#else +# define PJ_ASSERT_RETURN(expr,retval) pj_assert(expr) +#endif + +/** @} */ + +#endif /* __PJ_ASSERT_H__ */ + diff --git a/pjlib/include/pj/compat/assert.h b/pjlib/include/pj/compat/assert.h index c3df94e04..658a0a976 100644 --- a/pjlib/include/pj/compat/assert.h +++ b/pjlib/include/pj/compat/assert.h @@ -1,38 +1,38 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/assert.h 3 9/22/05 10:31a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/assert.h $ - * - * 3 9/22/05 10:31a Bennylp - * Moving all *.h files to include/. - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_ASSERT_H__ -#define __PJ_COMPAT_ASSERT_H__ - -/** - * @file assert.h - * @brief Provides assert() macro. - */ - -#if defined(PJ_HAS_ASSERT_H) && PJ_HAS_ASSERT_H != 0 -# include - -#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 -# define assert(expr) do { \ - if (!(expr)) \ - printk("!!ASSERTION FAILED: [%s:%d] \"" #expr "\"\n",\ - __FILE__, __LINE__); \ - } while (0) - -#else -# warning "assert() is not implemented" -# define assert(expr) -#endif - -#endif /* __PJ_COMPAT_ASSERT_H__ */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/assert.h 3 9/22/05 10:31a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/assert.h $ + * + * 3 9/22/05 10:31a Bennylp + * Moving all *.h files to include/. + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_ASSERT_H__ +#define __PJ_COMPAT_ASSERT_H__ + +/** + * @file assert.h + * @brief Provides assert() macro. + */ + +#if defined(PJ_HAS_ASSERT_H) && PJ_HAS_ASSERT_H != 0 +# include + +#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 +# define assert(expr) do { \ + if (!(expr)) \ + printk("!!ASSERTION FAILED: [%s:%d] \"" #expr "\"\n",\ + __FILE__, __LINE__); \ + } while (0) + +#else +# warning "assert() is not implemented" +# define assert(expr) +#endif + +#endif /* __PJ_COMPAT_ASSERT_H__ */ + diff --git a/pjlib/include/pj/compat/cc_gcc.h b/pjlib/include/pj/compat/cc_gcc.h index dcae099d2..83ae430fb 100644 --- a/pjlib/include/pj/compat/cc_gcc.h +++ b/pjlib/include/pj/compat/cc_gcc.h @@ -1,32 +1,32 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/cc_gcc.h 2 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/cc_gcc.h $ - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_CC_GCC_H__ -#define __PJ_COMPAT_CC_GCC_H__ - -/** - * @file cc_gcc.h - * @brief Describes GCC compiler specifics. - */ - -#ifndef __GNUC__ -# error "This file is only for gcc!" -#endif - -#define PJ_INLINE_SPECIFIER static inline -#define PJ_THREAD_FUNC -#define PJ_NORETURN -#define PJ_ATTR_NORETURN __attribute__ ((noreturn)) - -#define PJ_HAS_INT64 1 - -typedef long long pj_int64_t; -typedef unsigned long long pj_uint64_t; - - -#endif /* __PJ_COMPAT_CC_GCC_H__ */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/cc_gcc.h 2 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/cc_gcc.h $ + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_CC_GCC_H__ +#define __PJ_COMPAT_CC_GCC_H__ + +/** + * @file cc_gcc.h + * @brief Describes GCC compiler specifics. + */ + +#ifndef __GNUC__ +# error "This file is only for gcc!" +#endif + +#define PJ_INLINE_SPECIFIER static inline +#define PJ_THREAD_FUNC +#define PJ_NORETURN +#define PJ_ATTR_NORETURN __attribute__ ((noreturn)) + +#define PJ_HAS_INT64 1 + +typedef long long pj_int64_t; +typedef unsigned long long pj_uint64_t; + + +#endif /* __PJ_COMPAT_CC_GCC_H__ */ + diff --git a/pjlib/include/pj/compat/cc_msvc.h b/pjlib/include/pj/compat/cc_msvc.h index 39f0acd93..f9271bae2 100644 --- a/pjlib/include/pj/compat/cc_msvc.h +++ b/pjlib/include/pj/compat/cc_msvc.h @@ -1,40 +1,40 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/cc_msvc.h 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/cc_msvc.h $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_CC_MSVC_H__ -#define __PJ_COMPAT_CC_MSVC_H__ - -/** - * @file cc_msvc.h - * @brief Describes Microsoft Visual C compiler specifics. - */ - -#ifndef _MSC_VER -# error "This header file is only for Visual C compiler!" -#endif - -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4611) // not wise to mix setjmp with C++ -# pragma warning(disable: 4514) // unreferenced inline function has been removed -# ifdef __cplusplus -# define PJ_INLINE_SPECIFIER inline -# else -# define PJ_INLINE_SPECIFIER static __inline -# endif -# define PJ_THREAD_FUNC -# define PJ_NORETURN __declspec(noreturn) -# define PJ_ATTR_NORETURN - -# define PJ_HAS_INT64 1 -typedef __int64 pj_int64_t; -typedef unsigned __int64 pj_uint64_t; - -#endif /* __PJ_COMPAT_CC_MSVC_H__ */ +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/cc_msvc.h 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/cc_msvc.h $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_CC_MSVC_H__ +#define __PJ_COMPAT_CC_MSVC_H__ + +/** + * @file cc_msvc.h + * @brief Describes Microsoft Visual C compiler specifics. + */ + +#ifndef _MSC_VER +# error "This header file is only for Visual C compiler!" +#endif + +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4611) // not wise to mix setjmp with C++ +# pragma warning(disable: 4514) // unreferenced inline function has been removed +# ifdef __cplusplus +# define PJ_INLINE_SPECIFIER inline +# else +# define PJ_INLINE_SPECIFIER static __inline +# endif +# define PJ_THREAD_FUNC +# define PJ_NORETURN __declspec(noreturn) +# define PJ_ATTR_NORETURN + +# define PJ_HAS_INT64 1 +typedef __int64 pj_int64_t; +typedef unsigned __int64 pj_uint64_t; + +#endif /* __PJ_COMPAT_CC_MSVC_H__ */ diff --git a/pjlib/include/pj/compat/ctype.h b/pjlib/include/pj/compat/ctype.h index 18e8d4754..7b53bf541 100644 --- a/pjlib/include/pj/compat/ctype.h +++ b/pjlib/include/pj/compat/ctype.h @@ -1,42 +1,42 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/ctype.h 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/ctype.h $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/22/05 10:31a Bennylp - * Moving all *.h files to include/. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_CTYPE_H__ -#define __PJ_COMPAT_CTYPE_H__ - -/** - * @file ctype.h - * @brief Provides ctype function family. - */ - -#if defined(PJ_HAS_CTYPE_H) && PJ_HAS_CTYPE_H != 0 -# include -#else -# define isalnum(c) (isalpha(c) || isdigit(c)) -# define isalpha(c) (islower(c) || isupper(c)) -# define isascii(c) (((unsigned char)(c))<=0x7f) -# define isdigit(c) ((c)>='0' && (c)<='9') -# define isspace(c) ((c)==' ' || (c)=='\t' ||\ - (c)=='\n' || (c)=='\r' || (c)=='\v') -# define islower(c) ((c)>='a' && (c)<='z') -# define isupper(c) ((c)>='A' && (c)<='Z') -# define isxdigit(c) (isdigit(c) || (tolower(c)>='a'&&tolower(c)<='f')) -# define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)+('a'-'A') : (c)) -# define toupper(c) (((c) >= 'a' && (c) <= 'z') ? (c)-('a'-'A') : (c)) -#endif - -#define isblank(c) (c==' ' || c=='\t') - - -#endif /* __PJ_COMPAT_CTYPE_H__ */ +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/ctype.h 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/ctype.h $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/22/05 10:31a Bennylp + * Moving all *.h files to include/. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_CTYPE_H__ +#define __PJ_COMPAT_CTYPE_H__ + +/** + * @file ctype.h + * @brief Provides ctype function family. + */ + +#if defined(PJ_HAS_CTYPE_H) && PJ_HAS_CTYPE_H != 0 +# include +#else +# define isalnum(c) (isalpha(c) || isdigit(c)) +# define isalpha(c) (islower(c) || isupper(c)) +# define isascii(c) (((unsigned char)(c))<=0x7f) +# define isdigit(c) ((c)>='0' && (c)<='9') +# define isspace(c) ((c)==' ' || (c)=='\t' ||\ + (c)=='\n' || (c)=='\r' || (c)=='\v') +# define islower(c) ((c)>='a' && (c)<='z') +# define isupper(c) ((c)>='A' && (c)<='Z') +# define isxdigit(c) (isdigit(c) || (tolower(c)>='a'&&tolower(c)<='f')) +# define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)+('a'-'A') : (c)) +# define toupper(c) (((c) >= 'a' && (c) <= 'z') ? (c)-('a'-'A') : (c)) +#endif + +#define isblank(c) (c==' ' || c=='\t') + + +#endif /* __PJ_COMPAT_CTYPE_H__ */ diff --git a/pjlib/include/pj/compat/errno.h b/pjlib/include/pj/compat/errno.h index a0ea2ea6f..959f57fe5 100644 --- a/pjlib/include/pj/compat/errno.h +++ b/pjlib/include/pj/compat/errno.h @@ -1,26 +1,27 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/errno.h 2 10/14/05 12:26a Bennylp $ */ -#ifndef __PJ_COMPAT_ERRNO_H__ -#define __PJ_COMPAT_ERRNO_H__ - -#if defined(PJ_WIN32) && PJ_WIN32 != 0 - - typedef unsigned long pj_os_err_type; -# define pj_get_native_os_error() GetLastError() -# define pj_get_native_netos_error() WSAGetLastError() - -#elif (defined(PJ_LINUX) && PJ_LINUX != 0) || \ - (defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0) - - typedef int pj_os_err_type; -# define pj_get_native_os_error() (errno) -# define pj_get_native_netos_error() (errno) - -#else - -# error "Please define pj_os_err_type for this platform here!" - -#endif - - -#endif /* __PJ_COMPAT_ERRNO_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/errno.h 2 10/14/05 12:26a Bennylp $ */ +#ifndef __PJ_COMPAT_ERRNO_H__ +#define __PJ_COMPAT_ERRNO_H__ + +#if defined(PJ_WIN32) && PJ_WIN32 != 0 + + typedef unsigned long pj_os_err_type; +# define pj_get_native_os_error() GetLastError() +# define pj_get_native_netos_error() WSAGetLastError() + +#elif (defined(PJ_LINUX) && PJ_LINUX != 0) || \ + (defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0) || \ + (defined(PJ_SUNOS) && PJ_SUNOS != 0) + + typedef int pj_os_err_type; +# define pj_get_native_os_error() (errno) +# define pj_get_native_netos_error() (errno) + +#else + +# error "Please define pj_os_err_type for this platform here!" + +#endif + + +#endif /* __PJ_COMPAT_ERRNO_H__ */ + diff --git a/pjlib/include/pj/compat/high_precision.h b/pjlib/include/pj/compat/high_precision.h index 2004fb2ac..a29f5886d 100644 --- a/pjlib/include/pj/compat/high_precision.h +++ b/pjlib/include/pj/compat/high_precision.h @@ -1,83 +1,85 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/high_precision.h 3 10/29/05 11:51a Bennylp $ */ -#ifndef __PJ_COMPAT_HIGH_PRECISION_H__ -#define __PJ_COMPAT_HIGH_PRECISION_H__ - - -#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT != 0 - /* - * The first choice for high precision math is to use double. - */ -# include - typedef double pj_highprec_t; - -# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0) -# define pj_highprec_mod(a,b) (a=fmod(a,b)) - -#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 - -# include - - typedef pj_int64_t pj_highprec_t; - -# define pj_highprec_div(a1,a2) do_div(a1,a2) -# define pj_highprec_mod(a1,a2) (a1=do_mod(a1, a2)) - - PJ_INLINE(pj_int64_t) do_mod( pj_int64_t a1, pj_int64_t a2) - { - return do_div(a1,a2); - } - - -#elif defined(PJ_HAS_INT64) && PJ_HAS_INT64 != 0 - /* - * Next choice is to use 64-bit arithmatics. - */ - typedef pj_int64_t pj_highprec_t; - -#else - /* - * Last, fallback to 32-bit arithmetics. - */ - typedef pj_int32_t pj_highprec_t; - -#endif - -/** - * @def pj_highprec_mul - * pj_highprec_mul(a1, a2) - High Precision Multiplication - * Multiply a1 and a2, and store the result in a1. - */ -#ifndef pj_highprec_mul -# define pj_highprec_mul(a1,a2) (a1 = a1 * a2) -#endif - -/** - * @def pj_highprec_div - * pj_highprec_div(a1, a2) - High Precision Division - * Divide a2 from a1, and store the result in a1. - */ -#ifndef pj_highprec_div -# define pj_highprec_div(a1,a2) (a1 = a1 / a2) -#endif - -/** - * @def pj_highprec_mod - * pj_highprec_mod(a1, a2) - High Precision Modulus - * Get the modulus a2 from a1, and store the result in a1. - */ -#ifndef pj_highprec_mod -# define pj_highprec_mod(a1,a2) (a1 = a1 % a2) -#endif - - -/** - * @def PJ_HIGHPREC_VALUE_IS_ZERO(a) - * Test if the specified high precision value is zero. - */ -#ifndef PJ_HIGHPREC_VALUE_IS_ZERO -# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0) -#endif - - -#endif /* __PJ_COMPAT_HIGH_PRECISION_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/high_precision.h 3 10/29/05 11:51a Bennylp $ */ +#ifndef __PJ_COMPAT_HIGH_PRECISION_H__ +#define __PJ_COMPAT_HIGH_PRECISION_H__ + + +#if defined(PJ_HAS_FLOATING_POINT) && PJ_HAS_FLOATING_POINT != 0 + /* + * The first choice for high precision math is to use double. + */ +# include + typedef double pj_highprec_t; + +# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0) +# define pj_highprec_mod(a,b) (a=fmod(a,b)) + +#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 + +# include + + typedef pj_int64_t pj_highprec_t; + +# define pj_highprec_div(a1,a2) do_div(a1,a2) +# define pj_highprec_mod(a1,a2) (a1=do_mod(a1, a2)) + + PJ_INLINE(pj_int64_t) do_mod( pj_int64_t a1, pj_int64_t a2) + { + return do_div(a1,a2); + } + + +#elif defined(PJ_HAS_INT64) && PJ_HAS_INT64 != 0 + /* + * Next choice is to use 64-bit arithmatics. + */ + typedef pj_int64_t pj_highprec_t; + +#else +# warning "High precision math is not available" + + /* + * Last, fallback to 32-bit arithmetics. + */ + typedef pj_int32_t pj_highprec_t; + +#endif + +/** + * @def pj_highprec_mul + * pj_highprec_mul(a1, a2) - High Precision Multiplication + * Multiply a1 and a2, and store the result in a1. + */ +#ifndef pj_highprec_mul +# define pj_highprec_mul(a1,a2) (a1 = a1 * a2) +#endif + +/** + * @def pj_highprec_div + * pj_highprec_div(a1, a2) - High Precision Division + * Divide a2 from a1, and store the result in a1. + */ +#ifndef pj_highprec_div +# define pj_highprec_div(a1,a2) (a1 = a1 / a2) +#endif + +/** + * @def pj_highprec_mod + * pj_highprec_mod(a1, a2) - High Precision Modulus + * Get the modulus a2 from a1, and store the result in a1. + */ +#ifndef pj_highprec_mod +# define pj_highprec_mod(a1,a2) (a1 = a1 % a2) +#endif + + +/** + * @def PJ_HIGHPREC_VALUE_IS_ZERO(a) + * Test if the specified high precision value is zero. + */ +#ifndef PJ_HIGHPREC_VALUE_IS_ZERO +# define PJ_HIGHPREC_VALUE_IS_ZERO(a) (a==0) +#endif + + +#endif /* __PJ_COMPAT_HIGH_PRECISION_H__ */ + diff --git a/pjlib/include/pj/compat/m_alpha.h b/pjlib/include/pj/compat/m_alpha.h index a112e0c22..02681b826 100644 --- a/pjlib/include/pj/compat/m_alpha.h +++ b/pjlib/include/pj/compat/m_alpha.h @@ -1,23 +1,23 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/m_alpha.h 1 10/29/05 5:23p Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/include/pj/compat/m_alpha.h $ - * - * 1 10/29/05 5:23p Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_M_ALPHA_H__ -#define __PJ_COMPAT_M_ALPHA_H__ - -/** - * @file m_alpha.h - * @brief Describes Alpha processor family specifics. - */ - -#define PJ_HAS_PENTIUM 0 -#define PJ_IS_LITTLE_ENDIAN 1 -#define PJ_IS_BIG_ENDIAN 0 - - -#endif /* __PJ_COMPAT_M_ALPHA_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/m_alpha.h 1 10/29/05 5:23p Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/include/pj/compat/m_alpha.h $ + * + * 1 10/29/05 5:23p Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_M_ALPHA_H__ +#define __PJ_COMPAT_M_ALPHA_H__ + +/** + * @file m_alpha.h + * @brief Describes Alpha processor family specifics. + */ + +#define PJ_HAS_PENTIUM 0 +#define PJ_IS_LITTLE_ENDIAN 1 +#define PJ_IS_BIG_ENDIAN 0 + + +#endif /* __PJ_COMPAT_M_ALPHA_H__ */ + diff --git a/pjlib/include/pj/compat/m_i386.h b/pjlib/include/pj/compat/m_i386.h index 2c9ef2627..b291aee2f 100644 --- a/pjlib/include/pj/compat/m_i386.h +++ b/pjlib/include/pj/compat/m_i386.h @@ -1,26 +1,26 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/m_i386.h 3 9/21/05 1:39p Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_i386.h $ - * - * 3 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_M_i386_H__ -#define __PJ_COMPAT_M_i386_H__ - -/** - * @file m_i386.h - * @brief Describes Intel i386 family processor specifics. - */ - -#define PJ_M_I386 1 - -#define PJ_HAS_PENTIUM 1 -#define PJ_IS_LITTLE_ENDIAN 1 -#define PJ_IS_BIG_ENDIAN 0 - - -#endif /* __PJ_COMPAT_M_i386_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/m_i386.h 3 9/21/05 1:39p Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_i386.h $ + * + * 3 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_M_i386_H__ +#define __PJ_COMPAT_M_i386_H__ + +/** + * @file m_i386.h + * @brief Describes Intel i386 family processor specifics. + */ + +#define PJ_M_I386 1 + +#define PJ_HAS_PENTIUM 1 +#define PJ_IS_LITTLE_ENDIAN 1 +#define PJ_IS_BIG_ENDIAN 0 + + +#endif /* __PJ_COMPAT_M_i386_H__ */ diff --git a/pjlib/include/pj/compat/m_m68k.h b/pjlib/include/pj/compat/m_m68k.h index 5cdcde53c..bb126d16f 100644 --- a/pjlib/include/pj/compat/m_m68k.h +++ b/pjlib/include/pj/compat/m_m68k.h @@ -1,21 +1,21 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/m_m68k.h 2 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_m68k.h $ - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_M_M68K_H__ -#define __PJ_COMPAT_M_M68K_H__ - -/** - * @file m_m68k.h - * @brief Describes Motorola m68k family processor specifics. - */ - -#define PJ_HAS_PENTIUM 0 -#define PJ_IS_LITTLE_ENDIAN 1 -#define PJ_IS_BIG_ENDIAN 0 - - -#endif /* __PJ_COMPAT_M_M68K_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/m_m68k.h 2 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/m_m68k.h $ + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_M_M68K_H__ +#define __PJ_COMPAT_M_M68K_H__ + +/** + * @file m_m68k.h + * @brief Describes Motorola m68k family processor specifics. + */ + +#define PJ_HAS_PENTIUM 0 +#define PJ_IS_LITTLE_ENDIAN 1 +#define PJ_IS_BIG_ENDIAN 0 + + +#endif /* __PJ_COMPAT_M_M68K_H__ */ diff --git a/pjlib/include/pj/compat/m_sparc.h b/pjlib/include/pj/compat/m_sparc.h new file mode 100644 index 000000000..f0b55732f --- /dev/null +++ b/pjlib/include/pj/compat/m_sparc.h @@ -0,0 +1,21 @@ +/* $Header: $ */ +/* + *$Log: $ + * + */ +#ifndef __PJ_COMPAT_M_SPARC_H__ +#define __PJ_COMPAT_M_SPARC_H__ + +/** + * @file m_sparc.h + * @brief Describes SPARC family processor specifics. + */ + +#define PJ_SPARC 1 + +#define PJ_HAS_PENTIUM 0 +#define PJ_IS_LITTLE_ENDIAN 0 +#define PJ_IS_BIG_ENDIAN 1 + + +#endif /* __PJ_COMPAT_M_SPARC_H__ */ diff --git a/pjlib/include/pj/compat/malloc.h b/pjlib/include/pj/compat/malloc.h index b5a812657..225713c26 100644 --- a/pjlib/include/pj/compat/malloc.h +++ b/pjlib/include/pj/compat/malloc.h @@ -1,25 +1,25 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/malloc.h 2 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/malloc.h $ - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - * 1 9/16/05 10:02p Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_MALLOC_H__ -#define __PJ_COMPAT_MALLOC_H__ - -/** - * @file malloc.h - * @brief Provides malloc() and free() functions. - */ - -#if defined(PJ_HAS_MALLOC_H) && PJ_HAS_MALLOC_H != 0 -# include -#elif defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0 -# include -#endif - -#endif /* __PJ_COMPAT_MALLOC_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/malloc.h 2 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/malloc.h $ + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + * 1 9/16/05 10:02p Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_MALLOC_H__ +#define __PJ_COMPAT_MALLOC_H__ + +/** + * @file malloc.h + * @brief Provides malloc() and free() functions. + */ + +#if defined(PJ_HAS_MALLOC_H) && PJ_HAS_MALLOC_H != 0 +# include +#elif defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0 +# include +#endif + +#endif /* __PJ_COMPAT_MALLOC_H__ */ diff --git a/pjlib/include/pj/compat/os_linux.h b/pjlib/include/pj/compat/os_linux.h index 2f5e65afa..2779f58ff 100644 --- a/pjlib/include/pj/compat/os_linux.h +++ b/pjlib/include/pj/compat/os_linux.h @@ -1,69 +1,69 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/os_linux.h 6 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux.h $ - * - * 6 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 5 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 4 9/22/05 10:31a Bennylp - * Moving all *.h files to include/. - * - * 3 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_OS_LINUX_H__ -#define __PJ_COMPAT_OS_LINUX_H__ - -/** - * @file os_linux.h - * @brief Describes Linux operating system specifics. - */ - -#define PJ_HAS_ARPA_INET_H 1 -#define PJ_HAS_ASSERT_H 1 -#define PJ_HAS_CTYPE_H 1 -#define PJ_HAS_ERRNO_H 1 -#define PJ_HAS_LINUX_SOCKET_H 0 -#define PJ_HAS_MALLOC_H 1 -#define PJ_HAS_NETDB_H 1 -#define PJ_HAS_NETINET_IN_H 1 -#define PJ_HAS_SETJMP_H 1 -#define PJ_HAS_STDARG_H 1 -#define PJ_HAS_STDDEF_H 1 -#define PJ_HAS_STDIO_H 1 -#define PJ_HAS_STDLIB_H 1 -#define PJ_HAS_STRING_H 1 -#define PJ_HAS_SYS_IOCTL_H 1 -#define PJ_HAS_SYS_SELECT_H 1 -#define PJ_HAS_SYS_SOCKET_H 1 -#define PJ_HAS_SYS_TIMEB_H 1 -#define PJ_HAS_SYS_TYPES_H 1 -#define PJ_HAS_TIME_H 1 -#define PJ_HAS_UNISTD_H 1 - -#define PJ_HAS_MSWSOCK_H 0 -#define PJ_HAS_WINSOCK_H 0 -#define PJ_HAS_WINSOCK2_H 0 - -#define PJ_SOCK_HAS_INET_ATON 1 - -/* Default threading is enabled, unless it's overridden. */ -#ifndef PJ_HAS_THREADS -# define PJ_HAS_THREADS (1) -#endif - -#define PJ_HAS_HIGH_RES_TIMER 1 -#define PJ_HAS_MALLOC 1 -#define PJ_OS_HAS_CHECK_STACK 0 - -#define PJ_ATOMIC_VALUE_TYPE long - -#endif /* __PJ_COMPAT_OS_LINUX_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/os_linux.h 6 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux.h $ + * + * 6 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 5 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 4 9/22/05 10:31a Bennylp + * Moving all *.h files to include/. + * + * 3 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_OS_LINUX_H__ +#define __PJ_COMPAT_OS_LINUX_H__ + +/** + * @file os_linux.h + * @brief Describes Linux operating system specifics. + */ + +#define PJ_HAS_ARPA_INET_H 1 +#define PJ_HAS_ASSERT_H 1 +#define PJ_HAS_CTYPE_H 1 +#define PJ_HAS_ERRNO_H 1 +#define PJ_HAS_LINUX_SOCKET_H 0 +#define PJ_HAS_MALLOC_H 1 +#define PJ_HAS_NETDB_H 1 +#define PJ_HAS_NETINET_IN_H 1 +#define PJ_HAS_SETJMP_H 1 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 1 +#define PJ_HAS_STDIO_H 1 +#define PJ_HAS_STDLIB_H 1 +#define PJ_HAS_STRING_H 1 +#define PJ_HAS_SYS_IOCTL_H 1 +#define PJ_HAS_SYS_SELECT_H 1 +#define PJ_HAS_SYS_SOCKET_H 1 +#define PJ_HAS_SYS_TIMEB_H 1 +#define PJ_HAS_SYS_TYPES_H 1 +#define PJ_HAS_TIME_H 1 +#define PJ_HAS_UNISTD_H 1 + +#define PJ_HAS_MSWSOCK_H 0 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 0 + +#define PJ_SOCK_HAS_INET_ATON 1 + +/* Default threading is enabled, unless it's overridden. */ +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_HAS_MALLOC 1 +#define PJ_OS_HAS_CHECK_STACK 0 + +#define PJ_ATOMIC_VALUE_TYPE long + +#endif /* __PJ_COMPAT_OS_LINUX_H__ */ + diff --git a/pjlib/include/pj/compat/os_linux_kernel.h b/pjlib/include/pj/compat/os_linux_kernel.h index 2ebbb3228..e2cec3440 100644 --- a/pjlib/include/pj/compat/os_linux_kernel.h +++ b/pjlib/include/pj/compat/os_linux_kernel.h @@ -1,86 +1,86 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/os_linux_kernel.h 4 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux_kernel.h $ - * - * 4 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/22/05 10:31a Bennylp - * Moving all *.h files to include/. - * - * 1 9/21/05 1:38p Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_OS_LINUX_KERNEL_H__ -#define __PJ_COMPAT_OS_LINUX_KERNEL_H__ - -/** - * @file os_linux.h - * @brief Describes Linux operating system specifics. - */ - -#define PJ_HAS_ARPA_INET_H 0 -#define PJ_HAS_ASSERT_H 0 -#define PJ_HAS_CTYPE_H 0 -#define PJ_HAS_ERRNO_H 0 -#define PJ_HAS_LINUX_SOCKET_H 1 -#define PJ_HAS_MALLOC_H 0 -#define PJ_HAS_NETDB_H 0 -#define PJ_HAS_NETINET_IN_H 0 -#define PJ_HAS_SETJMP_H 0 -#define PJ_HAS_STDARG_H 1 -#define PJ_HAS_STDDEF_H 0 -#define PJ_HAS_STDIO_H 0 -#define PJ_HAS_STDLIB_H 0 -#define PJ_HAS_STRING_H 0 -#define PJ_HAS_SYS_IOCTL_H 0 -#define PJ_HAS_SYS_SELECT_H 0 -#define PJ_HAS_SYS_SOCKET_H 0 -#define PJ_HAS_SYS_TIMEB_H 0 -#define PJ_HAS_SYS_TYPES_H 0 -#define PJ_HAS_TIME_H 0 -#define PJ_HAS_UNISTD_H 0 - -#define PJ_HAS_MSWSOCK_H 0 -#define PJ_HAS_WINSOCK_H 0 -#define PJ_HAS_WINSOCK2_H 0 - -#define PJ_SOCK_HAS_INET_ATON 0 - -#ifndef PJ_HAS_THREADS -# define PJ_HAS_THREADS (1) -#endif - - -/* - * Declare __FD_SETSIZE now before including . - */ -#define __FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES - -#define NULL ((void*)0) - -#include /* Needed by all modules */ -#include /* Needed for KERN_INFO */ - -#define __PJ_EXPORT_SYMBOL(a) EXPORT_SYMBOL(a); - -/* - * Override features. - */ -#define PJ_HAS_FLOATING_POINT 0 -#define PJ_HAS_MALLOC 0 -#define PJ_HAS_SEMAPHORE 0 -#define PJ_HAS_EVENT_OBJ 0 -#define PJ_HAS_HIGH_RES_TIMER 1 -#define PJ_OS_HAS_CHECK_STACK 0 -#define PJ_TERM_HAS_COLOR 0 - -#define PJ_ATOMIC_VALUE_TYPE int -#define PJ_THREAD_DESC_SIZE 128 - -#endif /* __PJ_COMPAT_OS_LINUX_KERNEL_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/os_linux_kernel.h 4 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_linux_kernel.h $ + * + * 4 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/22/05 10:31a Bennylp + * Moving all *.h files to include/. + * + * 1 9/21/05 1:38p Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_OS_LINUX_KERNEL_H__ +#define __PJ_COMPAT_OS_LINUX_KERNEL_H__ + +/** + * @file os_linux.h + * @brief Describes Linux operating system specifics. + */ + +#define PJ_HAS_ARPA_INET_H 0 +#define PJ_HAS_ASSERT_H 0 +#define PJ_HAS_CTYPE_H 0 +#define PJ_HAS_ERRNO_H 0 +#define PJ_HAS_LINUX_SOCKET_H 1 +#define PJ_HAS_MALLOC_H 0 +#define PJ_HAS_NETDB_H 0 +#define PJ_HAS_NETINET_IN_H 0 +#define PJ_HAS_SETJMP_H 0 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 0 +#define PJ_HAS_STDIO_H 0 +#define PJ_HAS_STDLIB_H 0 +#define PJ_HAS_STRING_H 0 +#define PJ_HAS_SYS_IOCTL_H 0 +#define PJ_HAS_SYS_SELECT_H 0 +#define PJ_HAS_SYS_SOCKET_H 0 +#define PJ_HAS_SYS_TIMEB_H 0 +#define PJ_HAS_SYS_TYPES_H 0 +#define PJ_HAS_TIME_H 0 +#define PJ_HAS_UNISTD_H 0 + +#define PJ_HAS_MSWSOCK_H 0 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 0 + +#define PJ_SOCK_HAS_INET_ATON 0 + +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + + +/* + * Declare __FD_SETSIZE now before including . + */ +#define __FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES + +#define NULL ((void*)0) + +#include /* Needed by all modules */ +#include /* Needed for KERN_INFO */ + +#define __PJ_EXPORT_SYMBOL(a) EXPORT_SYMBOL(a); + +/* + * Override features. + */ +#define PJ_HAS_FLOATING_POINT 0 +#define PJ_HAS_MALLOC 0 +#define PJ_HAS_SEMAPHORE 0 +#define PJ_HAS_EVENT_OBJ 0 +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_OS_HAS_CHECK_STACK 0 +#define PJ_TERM_HAS_COLOR 0 + +#define PJ_ATOMIC_VALUE_TYPE int +#define PJ_THREAD_DESC_SIZE 128 + +#endif /* __PJ_COMPAT_OS_LINUX_KERNEL_H__ */ + diff --git a/pjlib/include/pj/compat/os_palmos.h b/pjlib/include/pj/compat/os_palmos.h index 1242bec12..109b020a3 100644 --- a/pjlib/include/pj/compat/os_palmos.h +++ b/pjlib/include/pj/compat/os_palmos.h @@ -1,54 +1,54 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/os_palmos.h 3 9/21/05 1:39p Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/os_palmos.h $ - * - * 3 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_OS_PALMOS_H__ -#define __PJ_COMPAT_OS_PALMOS_H__ - -/** - * @file os_palmos.h - * @brief Describes PalmOS operating system specifics. - */ - -#define PJ_HAS_ARPA_INET_H 0 -#define PJ_HAS_ASSERT_H 1 -#define PJ_HAS_CTYPE_H 1 -#define PJ_HAS_ERRNO_H 0 -#define PJ_HAS_MALLOC_H 1 -#define PJ_HAS_NETDB_H 0 -#define PJ_HAS_NETINET_IN_H 0 -#define PJ_HAS_SETJMP_H 1 -#define PJ_HAS_STDARG_H 1 -#define PJ_HAS_STDDEF_H 1 -#define PJ_HAS_STDIO_H 1 -#define PJ_HAS_STDLIB_H 1 -#define PJ_HAS_STRING_H 1 -#define PJ_HAS_SYS_IOCTL_H 0 -#define PJ_HAS_SYS_SELECT_H 0 -#define PJ_HAS_SYS_SOCKET_H 0 -#define PJ_HAS_SYS_TIMEB_H 0 -#define PJ_HAS_SYS_TYPES_H 1 -#define PJ_HAS_TIME_H 1 -#define PJ_HAS_UNISTD_H 0 - -#define PJ_HAS_MSWSOCK_H 0 -#define PJ_HAS_WINSOCK_H 0 -#define PJ_HAS_WINSOCK2_H 0 - -#define PJ_SOCK_HAS_INET_ATON 0 - -/* Default threading is enabled, unless it's overridden. */ -#ifndef PJ_HAS_THREADS -# define PJ_HAS_THREADS (1) -#endif - -#define PJ_HAS_HIGH_RES_TIMER 1 -#define PJ_OS_HAS_CHECK_STACK 0 - -#endif /* __PJ_COMPAT_OS_PALMOS_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/os_palmos.h 3 9/21/05 1:39p Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/os_palmos.h $ + * + * 3 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_OS_PALMOS_H__ +#define __PJ_COMPAT_OS_PALMOS_H__ + +/** + * @file os_palmos.h + * @brief Describes PalmOS operating system specifics. + */ + +#define PJ_HAS_ARPA_INET_H 0 +#define PJ_HAS_ASSERT_H 1 +#define PJ_HAS_CTYPE_H 1 +#define PJ_HAS_ERRNO_H 0 +#define PJ_HAS_MALLOC_H 1 +#define PJ_HAS_NETDB_H 0 +#define PJ_HAS_NETINET_IN_H 0 +#define PJ_HAS_SETJMP_H 1 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 1 +#define PJ_HAS_STDIO_H 1 +#define PJ_HAS_STDLIB_H 1 +#define PJ_HAS_STRING_H 1 +#define PJ_HAS_SYS_IOCTL_H 0 +#define PJ_HAS_SYS_SELECT_H 0 +#define PJ_HAS_SYS_SOCKET_H 0 +#define PJ_HAS_SYS_TIMEB_H 0 +#define PJ_HAS_SYS_TYPES_H 1 +#define PJ_HAS_TIME_H 1 +#define PJ_HAS_UNISTD_H 0 + +#define PJ_HAS_MSWSOCK_H 0 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 0 + +#define PJ_SOCK_HAS_INET_ATON 0 + +/* Default threading is enabled, unless it's overridden. */ +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_OS_HAS_CHECK_STACK 0 + +#endif /* __PJ_COMPAT_OS_PALMOS_H__ */ diff --git a/pjlib/include/pj/compat/os_sunos.h b/pjlib/include/pj/compat/os_sunos.h new file mode 100644 index 000000000..1704cedaf --- /dev/null +++ b/pjlib/include/pj/compat/os_sunos.h @@ -0,0 +1,56 @@ +/* $Header: $ */ +/* $Log: $ + * + */ +#ifndef __PJ_COMPAT_OS_SUNOS_H__ +#define __PJ_COMPAT_OS_SUNOS_H__ + +/** + * @file os_sunos.h + * @brief Describes SunOS/Solaris operating system specifics. + */ + +#define PJ_HAS_ARPA_INET_H 1 +#define PJ_HAS_ASSERT_H 1 +#define PJ_HAS_CTYPE_H 1 +#define PJ_HAS_ERRNO_H 1 +#define PJ_HAS_LINUX_SOCKET_H 0 +#define PJ_HAS_MALLOC_H 1 +#define PJ_HAS_NETDB_H 1 +#define PJ_HAS_NETINET_IN_H 1 +#define PJ_HAS_SETJMP_H 1 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 1 +#define PJ_HAS_STDIO_H 1 +#define PJ_HAS_STDLIB_H 1 +#define PJ_HAS_STRING_H 1 +#define PJ_HAS_SYS_IOCTL_H 1 +#define PJ_HAS_SYS_SELECT_H 1 +#define PJ_HAS_SYS_SOCKET_H 1 +#define PJ_HAS_SYS_TIMEB_H 1 +#define PJ_HAS_SYS_TYPES_H 1 +#define PJ_HAS_TIME_H 1 +#define PJ_HAS_UNISTD_H 1 + +#define PJ_HAS_MSWSOCK_H 0 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 0 + +#define PJ_SOCK_HAS_INET_ATON 0 + +/* Default threading is enabled, unless it's overridden. */ +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_HAS_MALLOC 1 +#define PJ_OS_HAS_CHECK_STACK 0 + +#define PJ_ATOMIC_VALUE_TYPE long + +/* Get BSD related identifers in Sun's include files */ +#define BSD_COMP + +#endif /* __PJ_COMPAT_OS_SUNOS_H__ */ + diff --git a/pjlib/include/pj/compat/os_win32.h b/pjlib/include/pj/compat/os_win32.h index d7a92c9b5..13a4a6903 100644 --- a/pjlib/include/pj/compat/os_win32.h +++ b/pjlib/include/pj/compat/os_win32.h @@ -1,72 +1,72 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/os_win32.h 6 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_win32.h $ - * - * 6 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 5 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 4 9/22/05 10:31a Bennylp - * Moving all *.h files to include/. - * - * 3 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_OS_WIN32_H__ -#define __PJ_COMPAT_OS_WIN32_H__ - -/** - * @file os_win32.h - * @brief Describes Win32 operating system family specifics. - */ - -#define WIN32_LEAN_AND_MEAN -#define PJ_WIN32_WINNT 0x0400 -#define _WIN32_WINNT PJ_WIN32_WINNT - -#define PJ_HAS_ARPA_INET_H 0 -#define PJ_HAS_ASSERT_H 1 -#define PJ_HAS_CTYPE_H 1 -#define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */ -#define PJ_HAS_LINUX_SOCKET_H 0 -#define PJ_HAS_MALLOC_H 1 -#define PJ_HAS_NETDB_H 0 -#define PJ_HAS_NETINET_IN_H 0 -#define PJ_HAS_SETJMP_H 1 -#define PJ_HAS_STDARG_H 1 -#define PJ_HAS_STDDEF_H 1 -#define PJ_HAS_STDIO_H 1 -#define PJ_HAS_STDLIB_H 1 -#define PJ_HAS_STRING_H 1 -#define PJ_HAS_SYS_IOCTL_H 0 -#define PJ_HAS_SYS_SELECT_H 0 -#define PJ_HAS_SYS_SOCKET_H 0 -#define PJ_HAS_SYS_TIMEB_H 1 -#define PJ_HAS_SYS_TYPES_H 1 -#define PJ_HAS_TIME_H 1 -#define PJ_HAS_UNISTD_H 0 - -#define PJ_HAS_MSWSOCK_H 1 -#define PJ_HAS_WINSOCK_H 0 -#define PJ_HAS_WINSOCK2_H 1 - -#define PJ_SOCK_HAS_INET_ATON 0 - -/* Default threading is enabled, unless it's overridden. */ -#ifndef PJ_HAS_THREADS -# define PJ_HAS_THREADS (1) -#endif - -#define PJ_HAS_HIGH_RES_TIMER 1 -#define PJ_HAS_MALLOC 1 -#define PJ_OS_HAS_CHECK_STACK 1 - -#define PJ_ATOMIC_VALUE_TYPE long - -#endif /* __PJ_COMPAT_OS_WIN32_H__ */ +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/os_win32.h 6 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/os_win32.h $ + * + * 6 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 5 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 4 9/22/05 10:31a Bennylp + * Moving all *.h files to include/. + * + * 3 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_OS_WIN32_H__ +#define __PJ_COMPAT_OS_WIN32_H__ + +/** + * @file os_win32.h + * @brief Describes Win32 operating system family specifics. + */ + +#define WIN32_LEAN_AND_MEAN +#define PJ_WIN32_WINNT 0x0400 +#define _WIN32_WINNT PJ_WIN32_WINNT + +#define PJ_HAS_ARPA_INET_H 0 +#define PJ_HAS_ASSERT_H 1 +#define PJ_HAS_CTYPE_H 1 +#define PJ_HAS_ERRNO_H 0 /* Must be zero, otherwise errno_test() fails. */ +#define PJ_HAS_LINUX_SOCKET_H 0 +#define PJ_HAS_MALLOC_H 1 +#define PJ_HAS_NETDB_H 0 +#define PJ_HAS_NETINET_IN_H 0 +#define PJ_HAS_SETJMP_H 1 +#define PJ_HAS_STDARG_H 1 +#define PJ_HAS_STDDEF_H 1 +#define PJ_HAS_STDIO_H 1 +#define PJ_HAS_STDLIB_H 1 +#define PJ_HAS_STRING_H 1 +#define PJ_HAS_SYS_IOCTL_H 0 +#define PJ_HAS_SYS_SELECT_H 0 +#define PJ_HAS_SYS_SOCKET_H 0 +#define PJ_HAS_SYS_TIMEB_H 1 +#define PJ_HAS_SYS_TYPES_H 1 +#define PJ_HAS_TIME_H 1 +#define PJ_HAS_UNISTD_H 0 + +#define PJ_HAS_MSWSOCK_H 1 +#define PJ_HAS_WINSOCK_H 0 +#define PJ_HAS_WINSOCK2_H 1 + +#define PJ_SOCK_HAS_INET_ATON 0 + +/* Default threading is enabled, unless it's overridden. */ +#ifndef PJ_HAS_THREADS +# define PJ_HAS_THREADS (1) +#endif + +#define PJ_HAS_HIGH_RES_TIMER 1 +#define PJ_HAS_MALLOC 1 +#define PJ_OS_HAS_CHECK_STACK 1 + +#define PJ_ATOMIC_VALUE_TYPE long + +#endif /* __PJ_COMPAT_OS_WIN32_H__ */ diff --git a/pjlib/include/pj/compat/rand.h b/pjlib/include/pj/compat/rand.h index 31461b37d..60c2be59b 100644 --- a/pjlib/include/pj/compat/rand.h +++ b/pjlib/include/pj/compat/rand.h @@ -1,65 +1,65 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/rand.h 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/rand.h $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_RAND_H__ -#define __PJ_COMPAT_RAND_H__ - -/** - * @file rand.h - * @brief Provides platform_rand() and platform_srand() functions. - */ - -#if defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0 - /* - * Use stdlib based rand() and srand(). - */ -# include -# define platform_srand srand -# if defined(RAND_MAX) && RAND_MAX <= 0xFFFF - /* - * When rand() is only 16 bit strong, double the strength - * by calling it twice! - */ - PJ_INLINE(int) platform_rand(void) - { - return ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF); - } -# else -# define platform_rand rand -# endif - -#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 - /* - * Linux kernel mode random number generator. - */ -# include -# define platform_srand(seed) - - PJ_INLINE(int) platform_rand(void) - { - int value; - get_random_bytes((void*)&value, sizeof(value)); - return value; - } - -#else -# warning "platform_rand() is not implemented" -# define platform_rand() 1 -# define platform_srand(seed) - -#endif - - -#endif /* __PJ_COMPAT_RAND_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/rand.h 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/rand.h $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_RAND_H__ +#define __PJ_COMPAT_RAND_H__ + +/** + * @file rand.h + * @brief Provides platform_rand() and platform_srand() functions. + */ + +#if defined(PJ_HAS_STDLIB_H) && PJ_HAS_STDLIB_H != 0 + /* + * Use stdlib based rand() and srand(). + */ +# include +# define platform_srand srand +# if defined(RAND_MAX) && RAND_MAX <= 0xFFFF + /* + * When rand() is only 16 bit strong, double the strength + * by calling it twice! + */ + PJ_INLINE(int) platform_rand(void) + { + return ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF); + } +# else +# define platform_rand rand +# endif + +#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 + /* + * Linux kernel mode random number generator. + */ +# include +# define platform_srand(seed) + + PJ_INLINE(int) platform_rand(void) + { + int value; + get_random_bytes((void*)&value, sizeof(value)); + return value; + } + +#else +# warning "platform_rand() is not implemented" +# define platform_rand() 1 +# define platform_srand(seed) + +#endif + + +#endif /* __PJ_COMPAT_RAND_H__ */ + diff --git a/pjlib/include/pj/compat/setjmp.h b/pjlib/include/pj/compat/setjmp.h index 2bbd0cd44..1ffd9eabb 100644 --- a/pjlib/include/pj/compat/setjmp.h +++ b/pjlib/include/pj/compat/setjmp.h @@ -1,89 +1,89 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/setjmp.h 4 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/setjmp.h $ - * - * 4 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 3 9/22/05 10:31a Bennylp - * Moving all *.h files to include/. - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_SETJMP_H__ -#define __PJ_COMPAT_SETJMP_H__ - -/** - * @file setjmp.h - * @brief Provides setjmp.h functionality. - */ - -#if defined(PJ_HAS_SETJMP_H) && PJ_HAS_SETJMP_H != 0 -# include - typedef jmp_buf pj_jmp_buf; -# define pj_setjmp(buf) setjmp(buf) -# define pj_longjmp(buf,d) longjmp(buf,d) - -#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 && \ - defined(PJ_M_I386) && PJ_M_I386 != 0 - - /* - * These are taken from uClibc. - * Copyright (C) 2000-2003 Erik Andersen - */ -# if defined __USE_MISC || defined _ASM -# define JB_BX 0 -# define JB_SI 1 -# define JB_DI 2 -# define JB_BP 3 -# define JB_SP 4 -# define JB_PC 5 -# define JB_SIZE 24 -# endif - -# ifndef _ASM - typedef int __jmp_buf[6]; - - /* A `sigset_t' has a bit for each signal. */ -# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) - typedef struct __sigset_t_tag - { - unsigned long int __val[_SIGSET_NWORDS]; - } __sigset_t; - - /* Calling environment, plus possibly a saved signal mask. */ - typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */ - { - /* NOTE: The machine-dependent definitions of `__sigsetjmp' - assume that a `jmp_buf' begins with a `__jmp_buf' and that - `__mask_was_saved' follows it. Do not move these members - or add others before it. */ - __jmp_buf __jmpbuf; /* Calling environment. */ - int __mask_was_saved; /* Saved the signal mask? */ - // we never saved the mask. - __sigset_t __saved_mask; /* Saved signal mask. */ - } jmp_buf[1]; - - typedef jmp_buf sigjmp_buf; - typedef jmp_buf pj_jmp_buf; - - PJ_DECL(int) pj_setjmp(pj_jmp_buf env); - PJ_DECL(void) pj_longjmp(pj_jmp_buf env, int val) __attribute__((noreturn)); - -# endif /* _ASM */ - -#else -# warning "setjmp()/longjmp() is not implemented" - typedef int pj_jmp_buf[1]; -# define pj_setjmp(buf) 0 -# define pj_longjmp(buf,d) 0 -#endif - - -#endif /* __PJ_COMPAT_SETJMP_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/setjmp.h 4 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/setjmp.h $ + * + * 4 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 3 9/22/05 10:31a Bennylp + * Moving all *.h files to include/. + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_SETJMP_H__ +#define __PJ_COMPAT_SETJMP_H__ + +/** + * @file setjmp.h + * @brief Provides setjmp.h functionality. + */ + +#if defined(PJ_HAS_SETJMP_H) && PJ_HAS_SETJMP_H != 0 +# include + typedef jmp_buf pj_jmp_buf; +# define pj_setjmp(buf) setjmp(buf) +# define pj_longjmp(buf,d) longjmp(buf,d) + +#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 && \ + defined(PJ_M_I386) && PJ_M_I386 != 0 + + /* + * These are taken from uClibc. + * Copyright (C) 2000-2003 Erik Andersen + */ +# if defined __USE_MISC || defined _ASM +# define JB_BX 0 +# define JB_SI 1 +# define JB_DI 2 +# define JB_BP 3 +# define JB_SP 4 +# define JB_PC 5 +# define JB_SIZE 24 +# endif + +# ifndef _ASM + typedef int __jmp_buf[6]; + + /* A `sigset_t' has a bit for each signal. */ +# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) + typedef struct __sigset_t_tag + { + unsigned long int __val[_SIGSET_NWORDS]; + } __sigset_t; + + /* Calling environment, plus possibly a saved signal mask. */ + typedef struct __jmp_buf_tag /* C++ doesn't like tagless structs. */ + { + /* NOTE: The machine-dependent definitions of `__sigsetjmp' + assume that a `jmp_buf' begins with a `__jmp_buf' and that + `__mask_was_saved' follows it. Do not move these members + or add others before it. */ + __jmp_buf __jmpbuf; /* Calling environment. */ + int __mask_was_saved; /* Saved the signal mask? */ + // we never saved the mask. + __sigset_t __saved_mask; /* Saved signal mask. */ + } jmp_buf[1]; + + typedef jmp_buf sigjmp_buf; + typedef jmp_buf pj_jmp_buf; + + PJ_DECL(int) pj_setjmp(pj_jmp_buf env); + PJ_DECL(void) pj_longjmp(pj_jmp_buf env, int val) __attribute__((noreturn)); + +# endif /* _ASM */ + +#else +# warning "setjmp()/longjmp() is not implemented" + typedef int pj_jmp_buf[1]; +# define pj_setjmp(buf) 0 +# define pj_longjmp(buf,d) 0 +#endif + + +#endif /* __PJ_COMPAT_SETJMP_H__ */ + diff --git a/pjlib/include/pj/compat/size_t.h b/pjlib/include/pj/compat/size_t.h index 39b416659..80064a760 100644 --- a/pjlib/include/pj/compat/size_t.h +++ b/pjlib/include/pj/compat/size_t.h @@ -1,23 +1,23 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/size_t.h 2 9/21/05 1:39p Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/size_t.h $ - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_SIZE_T_H__ -#define __PJ_COMPAT_SIZE_T_H__ - -/** - * @file size_t.h - * @brief Provides size_t type. - */ -#if PJ_HAS_STDDEF_H -# include -#endif - -#endif /* __PJ_COMPAT_SIZE_T_H__ */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/size_t.h 2 9/21/05 1:39p Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/size_t.h $ + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_SIZE_T_H__ +#define __PJ_COMPAT_SIZE_T_H__ + +/** + * @file size_t.h + * @brief Provides size_t type. + */ +#if PJ_HAS_STDDEF_H +# include +#endif + +#endif /* __PJ_COMPAT_SIZE_T_H__ */ + diff --git a/pjlib/include/pj/compat/socket.h b/pjlib/include/pj/compat/socket.h index 35befaeaf..561218b5e 100644 --- a/pjlib/include/pj/compat/socket.h +++ b/pjlib/include/pj/compat/socket.h @@ -1,129 +1,129 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/socket.h 5 10/29/05 11:51a Bennylp $*/ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/socket.h $ - * - * 5 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 4 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 3 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#ifndef __PJ_COMPAT_SOCKET_H__ -#define __PJ_COMPAT_SOCKET_H__ - -/** - * @file socket.h - * @brief Provides all socket related functions,data types, error codes, etc. - */ - -#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 -# include -#endif - -#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 -# include -#endif - -#if defined(PJ_HAS_SYS_TYPES_H) && PJ_HAS_SYS_TYPES_H != 0 -# include -#endif - -#if defined(PJ_HAS_SYS_SOCKET_H) && PJ_HAS_SYS_SOCKET_H != 0 -# include -#endif - -#if defined(PJ_HAS_LINUX_SOCKET_H) && PJ_HAS_LINUX_SOCKET_H != 0 -# include -#endif - -#if defined(PJ_HAS_SYS_SELECT_H) && PJ_HAS_SYS_SELECT_H != 0 -# include -#endif - -#if defined(PJ_HAS_NETINET_IN_H) && PJ_HAS_NETINET_IN_H != 0 -# include -#endif - -#if defined(PJ_HAS_ARPA_INET_H) && PJ_HAS_ARPA_INET_H != 0 -# include -#endif - -#if defined(PJ_HAS_SYS_IOCTL_H) && PJ_HAS_SYS_IOCTL_H != 0 -# include /* FBIONBIO */ -#endif - -#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0 -# include -#endif - -#if defined(PJ_HAS_NETDB_H) && PJ_HAS_NETDB_H != 0 -# include -#endif - -#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0 -# include -#endif - - -/* - * Define common errors. - */ -#ifdef PJ_WIN32 -# define OSERR_EWOULDBLOCK WSAEWOULDBLOCK -# define OSERR_EINPROGRESS WSAEINPROGRESS -#else -# define OSERR_EWOULDBLOCK EWOULDBLOCK -# define OSERR_EINPROGRESS EINPROGRESS -#endif - - -/* - * And undefine this.. - */ -#undef s_addr - -/* - * Linux kernel specifics - */ -#ifdef PJ_LINUX_KERNEL -# include -# include /* FIONBIO */ -# include /* sys_select() */ -# include /* set/get_fs() */ - - typedef int socklen_t; -# define getsockopt sys_getsockopt - - /* - * Wrapper for select() in Linux kernel. - */ - PJ_INLINE(int) select(int n, fd_set *inp, fd_set *outp, fd_set *exp, - struct timeval *tvp) - { - int count; - mm_segment_t oldfs = get_fs(); - set_fs(KERNEL_DS); - count = sys_select(n, inp, outp, exp, tvp); - set_fs(oldfs); - return count; - } -#endif /* PJ_LINUX_KERNEL */ - - -/* - * Windows specific - */ -#ifdef PJ_WIN32 - typedef int socklen_t;; -#endif - - -#endif /* __PJ_COMPAT_SOCKET_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/socket.h 5 10/29/05 11:51a Bennylp $*/ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/socket.h $ + * + * 5 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 4 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 3 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#ifndef __PJ_COMPAT_SOCKET_H__ +#define __PJ_COMPAT_SOCKET_H__ + +/** + * @file socket.h + * @brief Provides all socket related functions,data types, error codes, etc. + */ + +#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include +#endif + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include +#endif + +#if defined(PJ_HAS_SYS_TYPES_H) && PJ_HAS_SYS_TYPES_H != 0 +# include +#endif + +#if defined(PJ_HAS_SYS_SOCKET_H) && PJ_HAS_SYS_SOCKET_H != 0 +# include +#endif + +#if defined(PJ_HAS_LINUX_SOCKET_H) && PJ_HAS_LINUX_SOCKET_H != 0 +# include +#endif + +#if defined(PJ_HAS_SYS_SELECT_H) && PJ_HAS_SYS_SELECT_H != 0 +# include +#endif + +#if defined(PJ_HAS_NETINET_IN_H) && PJ_HAS_NETINET_IN_H != 0 +# include +#endif + +#if defined(PJ_HAS_ARPA_INET_H) && PJ_HAS_ARPA_INET_H != 0 +# include +#endif + +#if defined(PJ_HAS_SYS_IOCTL_H) && PJ_HAS_SYS_IOCTL_H != 0 +# include /* FBIONBIO */ +#endif + +#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0 +# include +#endif + +#if defined(PJ_HAS_NETDB_H) && PJ_HAS_NETDB_H != 0 +# include +#endif + +#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0 +# include +#endif + + +/* + * Define common errors. + */ +#ifdef PJ_WIN32 +# define OSERR_EWOULDBLOCK WSAEWOULDBLOCK +# define OSERR_EINPROGRESS WSAEINPROGRESS +#else +# define OSERR_EWOULDBLOCK EWOULDBLOCK +# define OSERR_EINPROGRESS EINPROGRESS +#endif + + +/* + * And undefine this.. + */ +#undef s_addr + +/* + * Linux kernel specifics + */ +#ifdef PJ_LINUX_KERNEL +# include +# include /* FIONBIO */ +# include /* sys_select() */ +# include /* set/get_fs() */ + + typedef int socklen_t; +# define getsockopt sys_getsockopt + + /* + * Wrapper for select() in Linux kernel. + */ + PJ_INLINE(int) select(int n, fd_set *inp, fd_set *outp, fd_set *exp, + struct timeval *tvp) + { + int count; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + count = sys_select(n, inp, outp, exp, tvp); + set_fs(oldfs); + return count; + } +#endif /* PJ_LINUX_KERNEL */ + + +/* + * Windows specific + */ +#ifdef PJ_WIN32 + typedef int socklen_t;; +#endif + + +#endif /* __PJ_COMPAT_SOCKET_H__ */ + diff --git a/pjlib/include/pj/compat/sprintf.h b/pjlib/include/pj/compat/sprintf.h index 7ecc929bb..21353fb93 100644 --- a/pjlib/include/pj/compat/sprintf.h +++ b/pjlib/include/pj/compat/sprintf.h @@ -1,31 +1,31 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/compat/sprintf.h 2 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/compat/sprintf.h $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_SPRINTF_H__ -#define __PJ_COMPAT_SPRINTF_H__ - -/** - * @file sprintf.h - * @brief Provides sprintf() and snprintf() functions. - */ - -#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0 -# include -#endif - -#if defined(_MSC_VER) -# define snprintf _snprintf -#endif - -#define pj_sprintf sprintf -#define pj_snprintf snprintf - -#endif /* __PJ_COMPAT_SPRINTF_H__ */ +/* $Header: /pjproject-0.3/pjlib/include/pj/compat/sprintf.h 2 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/compat/sprintf.h $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_SPRINTF_H__ +#define __PJ_COMPAT_SPRINTF_H__ + +/** + * @file sprintf.h + * @brief Provides sprintf() and snprintf() functions. + */ + +#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0 +# include +#endif + +#if defined(_MSC_VER) +# define snprintf _snprintf +#endif + +#define pj_sprintf sprintf +#define pj_snprintf snprintf + +#endif /* __PJ_COMPAT_SPRINTF_H__ */ diff --git a/pjlib/include/pj/compat/stdarg.h b/pjlib/include/pj/compat/stdarg.h index 574cabb9c..3c550ffc4 100644 --- a/pjlib/include/pj/compat/stdarg.h +++ b/pjlib/include/pj/compat/stdarg.h @@ -1,20 +1,20 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/stdarg.h 1 9/17/05 10:36a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdarg.h $ - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_STDARG_H__ -#define __PJ_COMPAT_STDARG_H__ - -/** - * @file stdarg.h - * @brief Provides stdarg functionality. - */ - -#if defined(PJ_HAS_STDARG_H) && PJ_HAS_STDARG_H != 0 -# include -#endif - -#endif /* __PJ_COMPAT_STDARG_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/stdarg.h 1 9/17/05 10:36a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdarg.h $ + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_STDARG_H__ +#define __PJ_COMPAT_STDARG_H__ + +/** + * @file stdarg.h + * @brief Provides stdarg functionality. + */ + +#if defined(PJ_HAS_STDARG_H) && PJ_HAS_STDARG_H != 0 +# include +#endif + +#endif /* __PJ_COMPAT_STDARG_H__ */ diff --git a/pjlib/include/pj/compat/stdfileio.h b/pjlib/include/pj/compat/stdfileio.h index 40b0c3419..a9926a719 100644 --- a/pjlib/include/pj/compat/stdfileio.h +++ b/pjlib/include/pj/compat/stdfileio.h @@ -1,20 +1,20 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/stdfileio.h 1 9/17/05 10:36a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdfileio.h $ - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_STDFILEIO_H__ -#define __PJ_COMPAT_STDFILEIO_H__ - -/** - * @file stdfileio.h - * @brief Compatibility for ANSI file I/O like fputs, fflush, etc. - */ - -#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0 -# include -#endif - -#endif /* __PJ_COMPAT_STDFILEIO_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/stdfileio.h 1 9/17/05 10:36a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/stdfileio.h $ + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_STDFILEIO_H__ +#define __PJ_COMPAT_STDFILEIO_H__ + +/** + * @file stdfileio.h + * @brief Compatibility for ANSI file I/O like fputs, fflush, etc. + */ + +#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0 +# include +#endif + +#endif /* __PJ_COMPAT_STDFILEIO_H__ */ diff --git a/pjlib/include/pj/compat/string.h b/pjlib/include/pj/compat/string.h index 86c8bd6b3..3230fbddb 100644 --- a/pjlib/include/pj/compat/string.h +++ b/pjlib/include/pj/compat/string.h @@ -1,41 +1,41 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/string.h 3 9/22/05 10:31a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/string.h $ - * - * 3 9/22/05 10:31a Bennylp - * Moving all *.h files to include/. - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_STRING_H__ -#define __PJ_COMPAT_STRING_H__ - -/** - * @file string.h - * @brief Provides string manipulation functions found in ANSI string.h. - */ - -#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0 -# include -#else - - PJ_DECL(int) strcasecmp(const char *s1, const char *s2); - PJ_DECL(int) strncasecmp(const char *s1, const char *s2, int len); - -#endif - -#if defined(_MSC_VER) -# define strcasecmp stricmp -# define strncasecmp strnicmp -# define snprintf _snprintf -#else -# define stricmp strcasecmp -# define strnicmp strncasecmp -#endif - - -#endif /* __PJ_COMPAT_STRING_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/string.h 3 9/22/05 10:31a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/string.h $ + * + * 3 9/22/05 10:31a Bennylp + * Moving all *.h files to include/. + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_STRING_H__ +#define __PJ_COMPAT_STRING_H__ + +/** + * @file string.h + * @brief Provides string manipulation functions found in ANSI string.h. + */ + +#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0 +# include +#else + + PJ_DECL(int) strcasecmp(const char *s1, const char *s2); + PJ_DECL(int) strncasecmp(const char *s1, const char *s2, int len); + +#endif + +#if defined(_MSC_VER) +# define strcasecmp stricmp +# define strncasecmp strnicmp +# define snprintf _snprintf +#else +# define stricmp strcasecmp +# define strnicmp strncasecmp +#endif + + +#endif /* __PJ_COMPAT_STRING_H__ */ diff --git a/pjlib/include/pj/compat/time.h b/pjlib/include/pj/compat/time.h index 45585cb16..f76c1e41c 100644 --- a/pjlib/include/pj/compat/time.h +++ b/pjlib/include/pj/compat/time.h @@ -1,25 +1,25 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/time.h 1 9/17/05 10:36a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/time.h $ - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_TIME_H__ -#define __PJ_COMPAT_TIME_H__ - -/** - * @file time.h - * @brief Provides ftime() and localtime() etc functions. - */ - -#if defined(PJ_HAS_TIME_H) && PJ_HAS_TIME_H != 0 -# include -#endif - -#if defined(PJ_HAS_SYS_TIMEB_H) && PJ_HAS_SYS_TIMEB_H != 0 -# include -#endif - - -#endif /* __PJ_COMPAT_TIME_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/time.h 1 9/17/05 10:36a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/time.h $ + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_TIME_H__ +#define __PJ_COMPAT_TIME_H__ + +/** + * @file time.h + * @brief Provides ftime() and localtime() etc functions. + */ + +#if defined(PJ_HAS_TIME_H) && PJ_HAS_TIME_H != 0 +# include +#endif + +#if defined(PJ_HAS_SYS_TIMEB_H) && PJ_HAS_SYS_TIMEB_H != 0 +# include +#endif + + +#endif /* __PJ_COMPAT_TIME_H__ */ diff --git a/pjlib/include/pj/compat/vsprintf.h b/pjlib/include/pj/compat/vsprintf.h index 3d2bd341b..cc93bc557 100644 --- a/pjlib/include/pj/compat/vsprintf.h +++ b/pjlib/include/pj/compat/vsprintf.h @@ -1,26 +1,26 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/vsprintf.h 1 9/17/05 10:36a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/vsprintf.h $ - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#ifndef __PJ_COMPAT_VSPRINTF_H__ -#define __PJ_COMPAT_VSPRINTF_H__ - -/** - * @file vsprintf.h - * @brief Provides vsprintf and vsnprintf function. - */ - -#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0 -# include -#endif - -#if defined(_MSC_VER) -# define vsnprintf _vsnprintf -#endif - -#define pj_vsnprintf vsnprintf - -#endif /* __PJ_COMPAT_VSPRINTF_H__ */ +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/vsprintf.h 1 9/17/05 10:36a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/vsprintf.h $ + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#ifndef __PJ_COMPAT_VSPRINTF_H__ +#define __PJ_COMPAT_VSPRINTF_H__ + +/** + * @file vsprintf.h + * @brief Provides vsprintf and vsnprintf function. + */ + +#if defined(PJ_HAS_STDIO_H) && PJ_HAS_STDIO_H != 0 +# include +#endif + +#if defined(_MSC_VER) +# define vsnprintf _vsnprintf +#endif + +#define pj_vsnprintf vsnprintf + +#endif /* __PJ_COMPAT_VSPRINTF_H__ */ diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 3c093b8ba..fd80e86f7 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -1,438 +1,442 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/config.h 12 10/29/05 10:25p Bennylp $ */ - -#ifndef __PJ_CONFIG_H__ -#define __PJ_CONFIG_H__ - -/** - * @file config.h - * @brief PJLIB Main configuration settings. - */ - -/******************************************************************** - * Include compiler specific configuration. - */ -#if defined(_MSC_VER) -# include -#elif defined(__GNUC__) -# include -#else -# error "Unknown compiler." -#endif - - -/******************************************************************** - * Include target specific configuration. - */ -#if defined(PJ_WIN32) -# include -#elif defined(PJ_LINUX) -# include -#elif defined(PJ_LINUX_KERNEL) -# include -#elif defined(PJ_PALMOS) -# include -#else -# error "Please specify target os." -#endif - - -/******************************************************************** - * Target machine specific configuration. - */ -#if defined (PJ_M_I386) && PJ_M_I386 != 0 -# include -#elif defined (PJ_M_M68K) && PJ_M_M68K != 0 -# include -#elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0 -# include -#else -# error "Please specify target machine." -#endif - -/* Include size_t definition. */ -#include - -/* Include site/user specific configuration to control PJLIB features. - * YOU MUST CREATE THIS FILE YOURSELF!! - */ -#include - -/******************************************************************** - * PJLIB Features. - */ - -/* Overrides for DOXYGEN */ -#ifdef DOXYGEN -# undef PJ_FUNCTIONS_ARE_INLINED -# undef PJ_HAS_FLOATING_POINT -# undef PJ_LOG_MAX_LEVEL -# undef PJ_LOG_MAX_SIZE -# undef PJ_LOG_USE_STACK_BUFFER -# undef PJ_TERM_HAS_COLOR -# undef PJ_POOL_DEBUG -# undef PJ_HAS_TCP -# undef PJ_MAX_HOSTNAME -# undef PJ_IOQUEUE_MAX_HANDLES -# undef FD_SETSIZE -# undef PJ_HAS_SEMAPHORE -# undef PJ_HAS_EVENT_OBJ -# undef PJ_ENABLE_EXTRA_CHECK -#endif - -/** - * @defgroup pj_config Build Configuration - * @ingroup PJ - * @{ - * - * This section contains macros that can set during PJLIB build process - * to controll various aspects of the library. - * - * Note: the values in this page does NOT necessarily reflect to the - * macro values during the build process. - */ - -/** - * If this macro is set to 1, it will enable some debugging checking - * in the library. - * - * Default: equal to (NOT NDEBUG). - */ -#ifndef PJ_DEBUG -# ifndef NDEBUG -# define PJ_DEBUG 1 -# else -# define PJ_DEBUG 0 -# endif -#endif - -/** - * Expand functions in *_i.h header files as inline. - * - * Default: 0. - */ -#ifndef PJ_FUNCTIONS_ARE_INLINED -# define PJ_FUNCTIONS_ARE_INLINED 0 -#endif - -/** - * Use floating point computations in the library. - * - * Default: 1. - */ -#ifndef PJ_HAS_FLOATING_POINT -# define PJ_HAS_FLOATING_POINT 1 -#endif - -/** - * Declare maximum logging level/verbosity. Lower number indicates higher - * importance, with the highest importance has level zero. The least - * important level is five in this implementation, but this can be extended - * by supplying the appropriate implementation. - * - * The level conventions: - * - 0: fatal error - * - 1: error - * - 2: warning - * - 3: info - * - 4: debug - * - 5: trace - * - 6: more detailed trace - * - * Default: 4 - */ -#ifndef PJ_LOG_MAX_LEVEL -# define PJ_LOG_MAX_LEVEL 4 -#endif - -/** - * Maximum message size that can be sent to output device for each call - * to PJ_LOG(). If the message size is longer than this value, it will be cut. - * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER - * flag is set. - * - * Default: 800 - */ -#ifndef PJ_LOG_MAX_SIZE -# define PJ_LOG_MAX_SIZE 800 -#endif - -/** - * Log buffer. - * Does the log get the buffer from the stack? (default is yes). - * If the value is set to NO, then the buffer will be taken from static - * buffer, which in this case will make the log function non-reentrant. - * - * Default: 1 - */ -#ifndef PJ_LOG_USE_STACK_BUFFER -# define PJ_LOG_USE_STACK_BUFFER 1 -#endif - - -/** - * Colorfull terminal (for logging etc). - * - * Default: 1 - */ -#ifndef PJ_TERM_HAS_COLOR -# define PJ_TERM_HAS_COLOR 1 -#endif - -/** - * Pool debugging. - * - * Default: 0 - */ -#ifndef PJ_POOL_DEBUG -# define PJ_POOL_DEBUG 0 -#endif - -/** - * \def PJ_HAS_TCP - * Support TCP in the library. - * Disabling TCP will reduce the footprint slightly (about 6KB). - * - * Default: 1 - */ -#ifndef PJ_HAS_TCP -# define PJ_HAS_TCP 1 -#endif - -/** - * Maximum hostname length. - * Libraries sometimes needs to make copy of an address to stack buffer; - * the value here affects the stack usage. - * - * Default: 128 - */ -#ifndef PJ_MAX_HOSTNAME -# define PJ_MAX_HOSTNAME (128) -#endif - -/** - * Constants for declaring the maximum handles that can be supported by - * a single IOQ framework. This constant might not be relevant to the - * underlying I/O queue impelementation, but still, developers should be - * aware of this constant, to make sure that the program will not break when - * the underlying implementation changes. - * - * For implementation based on select(), the value here will be used as the - * maximum number of socket handles passed to select() (i.e. FD_SETSIZE will - * be set to this value). - * - * Default: 64 - */ -#ifndef PJ_IOQUEUE_MAX_HANDLES -# define PJ_IOQUEUE_MAX_HANDLES (64) -#endif - -/** - * Overrides FD_SETSIZE so it is consistent throughout the library. - * OS specific configuration header (compat/os_*) might have declared - * FD_SETSIZE, thus we only set if it hasn't been declared. - * - * Default: #PJ_IOQUEUE_MAX_HANDLES - */ -#ifndef FD_SETSIZE -# define FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES -#endif - -/** - * Has semaphore functionality? - * - * Default: 1 - */ -#ifndef PJ_HAS_SEMAPHORE -# define PJ_HAS_SEMAPHORE 1 -#endif - - -/** - * Event object (for synchronization, e.g. in Win32) - * - * Default: 1 - */ -#ifndef PJ_HAS_EVENT_OBJ -# define PJ_HAS_EVENT_OBJ 1 -#endif - - -/** - * Enable library's extra check. - * If this macro is enabled, #PJ_ASSERT_RETURN macro will expand to - * run-time checking. If this macro is disabled, #PJ_ASSERT_RETURN - * will simply evaluate to #pj_assert(). - * - * You can disable this macro to reduce size, at the risk of crashes - * if invalid value (e.g. NULL) is passed to the library. - * - * Default: 1 - */ -#ifndef PJ_ENABLE_EXTRA_CHECK -# define PJ_ENABLE_EXTRA_CHECK 1 -#endif - - -/** - * Enable name registration for exceptions with #pj_exception_id_alloc(). - * If this feature is enabled, then the library will keep track of - * names associated with each exception ID requested by application via - * #pj_exception_id_alloc(). - * - * Disabling this macro will reduce the code and .bss size by a tad bit. - * See also #PJ_MAX_EXCEPTION_ID. - * - * Default: 1 - */ -#ifndef PJ_HAS_EXCEPTION_NAMES -# define PJ_HAS_EXCEPTION_NAMES 1 -#endif - -/** - * Maximum number of unique exception IDs that can be requested - * with #pj_exception_id_alloc(). For each entry, a small record will - * be allocated in the .bss segment. - * - * Default: 16 - */ -#ifndef PJ_MAX_EXCEPTION_ID -# define PJ_MAX_EXCEPTION_ID 16 -#endif - -/** @} */ - -/******************************************************************** - * General macros. - */ - -/** - * @def PJ_INLINE(type) - * @param type The return type of the function. - * Expand the function as inline. - */ -#define PJ_INLINE(type) PJ_INLINE_SPECIFIER type - -/** - * @def PJ_DECL(type) - * @param type The return type of the function. - * Declare a function. - */ -/** - * @def PJ_DECL_NO_RETURN(type) - * @param type The return type of the function. - * Declare a function that will not return. - */ -/** - * @def PJ_BEGIN_DECL - * Mark beginning of declaration section in a header file. - */ -/** - * @def PJ_END_DECL - * Mark end of declaration section in a header file. - */ -#ifdef __cplusplus -# define PJ_DECL(type) type -# define PJ_DECL_NO_RETURN(type) type PJ_NORETURN -# define PJ_BEGIN_DECL extern "C" { -# define PJ_END_DECL } -#else -# define PJ_DECL(type) extern type -# define PJ_DECL_NO_RETURN(type) PJ_NORETURN type -# define PJ_BEGIN_DECL -# define PJ_END_DECL -#endif - -/** - * @def PJ_DEF(type) - * @param type The return type of the function. - * Define a function. - */ -#define PJ_DEF(type) type - -/** - * @def PJ_EXPORT_SYMBOL(sym) - * @param sym The symbol to export. - * Export the specified symbol in compilation type that requires export - * (e.g. Linux kernel). - */ -#ifdef __PJ_EXPORT_SYMBOL -# define PJ_EXPORT_SYMBOL(sym) __PJ_EXPORT_SYMBOL(sym) -#else -# define PJ_EXPORT_SYMBOL(sym) -#endif - -/** - * @def PJ_IDECL(type) - * @param type The function's return type. - * Declare a function that may be expanded as inline. - */ -/** - * @def PJ_IDEF(type) - * @param type The function's return type. - * Define a function that may be expanded as inline. - */ - -#if PJ_FUNCTIONS_ARE_INLINED -# define PJ_IDECL(type) PJ_INLINE(type) -# define PJ_IDEF(type) PJ_INLINE(type) -#else -# define PJ_IDECL(type) PJ_DECL(type) -# define PJ_IDEF(type) PJ_DEF(type) -#endif - -/** - * @def PJ_UNUSED_ARG(arg) - * @param arg The argument name. - * PJ_UNUSED_ARG prevents warning about unused argument in a function. - */ -#define PJ_UNUSED_ARG(arg) (void)arg - -/** - * @def PJ_TODO(id) - * @param id Any identifier that will be printed as TODO message. - * PJ_TODO macro will display TODO message as warning during compilation. - * Example: PJ_TODO(CLEAN_UP_ERROR); - */ -#ifndef PJ_TODO -# define PJ_TODO(id) TODO___##id: -#endif - - -/******************************************************************** - * Sanity Checks - */ -#ifndef PJ_HAS_HIGH_RES_TIMER -# error "PJ_HAS_HIGH_RES_TIMER is not defined!" -#endif - -#if !defined(PJ_HAS_PENTIUM) -# error "PJ_HAS_PENTIUM is not defined!" -#endif - -#if !defined(PJ_IS_LITTLE_ENDIAN) -# error "PJ_IS_LITTLE_ENDIAN is not defined!" -#endif - -#if !defined(PJ_IS_BIG_ENDIAN) -# error "PJ_IS_BIG_ENDIAN is not defined!" -#endif - - - -PJ_BEGIN_DECL - -/** - * PJLIB version string. - */ -extern const char *PJ_VERSION; - -/** - * Dump configuration to log with verbosity equal to info(3). - */ -PJ_DECL(void) pj_dump_config(void); - -PJ_END_DECL - - -#endif /* __PJ_CONFIG_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/config.h 12 10/29/05 10:25p Bennylp $ */ + +#ifndef __PJ_CONFIG_H__ +#define __PJ_CONFIG_H__ + +/** + * @file config.h + * @brief PJLIB Main configuration settings. + */ + +/******************************************************************** + * Include compiler specific configuration. + */ +#if defined(_MSC_VER) +# include +#elif defined(__GNUC__) +# include +#else +# error "Unknown compiler." +#endif + + +/******************************************************************** + * Include target OS specific configuration. + */ +#if defined(PJ_WIN32) && PJ_WIN32!=0 +# include +#elif defined(PJ_LINUX) && PJ_LINUX!=0 +# include +#elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0 +# include +#elif defined(PJ_PALMOS) && PJ_PALMOS!=0 +# include +#elif defined(PJ_SUNOS) && PJ_SUNOS!=0 +# include +#else +# error "Please specify target os." +#endif + + +/******************************************************************** + * Target machine specific configuration. + */ +#if defined (PJ_M_I386) && PJ_M_I386 != 0 +# include +#elif defined (PJ_M_M68K) && PJ_M_M68K != 0 +# include +#elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0 +# include +#elif defined (PJ_M_SPARC) && PJ_M_SPARC != 0 +# include +#else +# error "Please specify target machine." +#endif + +/* Include size_t definition. */ +#include + +/* Include site/user specific configuration to control PJLIB features. + * YOU MUST CREATE THIS FILE YOURSELF!! + */ +#include + +/******************************************************************** + * PJLIB Features. + */ + +/* Overrides for DOXYGEN */ +#ifdef DOXYGEN +# undef PJ_FUNCTIONS_ARE_INLINED +# undef PJ_HAS_FLOATING_POINT +# undef PJ_LOG_MAX_LEVEL +# undef PJ_LOG_MAX_SIZE +# undef PJ_LOG_USE_STACK_BUFFER +# undef PJ_TERM_HAS_COLOR +# undef PJ_POOL_DEBUG +# undef PJ_HAS_TCP +# undef PJ_MAX_HOSTNAME +# undef PJ_IOQUEUE_MAX_HANDLES +# undef FD_SETSIZE +# undef PJ_HAS_SEMAPHORE +# undef PJ_HAS_EVENT_OBJ +# undef PJ_ENABLE_EXTRA_CHECK +#endif + +/** + * @defgroup pj_config Build Configuration + * @ingroup PJ + * @{ + * + * This section contains macros that can set during PJLIB build process + * to controll various aspects of the library. + * + * Note: the values in this page does NOT necessarily reflect to the + * macro values during the build process. + */ + +/** + * If this macro is set to 1, it will enable some debugging checking + * in the library. + * + * Default: equal to (NOT NDEBUG). + */ +#ifndef PJ_DEBUG +# ifndef NDEBUG +# define PJ_DEBUG 1 +# else +# define PJ_DEBUG 0 +# endif +#endif + +/** + * Expand functions in *_i.h header files as inline. + * + * Default: 0. + */ +#ifndef PJ_FUNCTIONS_ARE_INLINED +# define PJ_FUNCTIONS_ARE_INLINED 0 +#endif + +/** + * Use floating point computations in the library. + * + * Default: 1. + */ +#ifndef PJ_HAS_FLOATING_POINT +# define PJ_HAS_FLOATING_POINT 1 +#endif + +/** + * Declare maximum logging level/verbosity. Lower number indicates higher + * importance, with the highest importance has level zero. The least + * important level is five in this implementation, but this can be extended + * by supplying the appropriate implementation. + * + * The level conventions: + * - 0: fatal error + * - 1: error + * - 2: warning + * - 3: info + * - 4: debug + * - 5: trace + * - 6: more detailed trace + * + * Default: 4 + */ +#ifndef PJ_LOG_MAX_LEVEL +# define PJ_LOG_MAX_LEVEL 4 +#endif + +/** + * Maximum message size that can be sent to output device for each call + * to PJ_LOG(). If the message size is longer than this value, it will be cut. + * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER + * flag is set. + * + * Default: 800 + */ +#ifndef PJ_LOG_MAX_SIZE +# define PJ_LOG_MAX_SIZE 800 +#endif + +/** + * Log buffer. + * Does the log get the buffer from the stack? (default is yes). + * If the value is set to NO, then the buffer will be taken from static + * buffer, which in this case will make the log function non-reentrant. + * + * Default: 1 + */ +#ifndef PJ_LOG_USE_STACK_BUFFER +# define PJ_LOG_USE_STACK_BUFFER 1 +#endif + + +/** + * Colorfull terminal (for logging etc). + * + * Default: 1 + */ +#ifndef PJ_TERM_HAS_COLOR +# define PJ_TERM_HAS_COLOR 1 +#endif + +/** + * Pool debugging. + * + * Default: 0 + */ +#ifndef PJ_POOL_DEBUG +# define PJ_POOL_DEBUG 0 +#endif + +/** + * \def PJ_HAS_TCP + * Support TCP in the library. + * Disabling TCP will reduce the footprint slightly (about 6KB). + * + * Default: 1 + */ +#ifndef PJ_HAS_TCP +# define PJ_HAS_TCP 1 +#endif + +/** + * Maximum hostname length. + * Libraries sometimes needs to make copy of an address to stack buffer; + * the value here affects the stack usage. + * + * Default: 128 + */ +#ifndef PJ_MAX_HOSTNAME +# define PJ_MAX_HOSTNAME (128) +#endif + +/** + * Constants for declaring the maximum handles that can be supported by + * a single IOQ framework. This constant might not be relevant to the + * underlying I/O queue impelementation, but still, developers should be + * aware of this constant, to make sure that the program will not break when + * the underlying implementation changes. + * + * For implementation based on select(), the value here will be used as the + * maximum number of socket handles passed to select() (i.e. FD_SETSIZE will + * be set to this value). + * + * Default: 64 + */ +#ifndef PJ_IOQUEUE_MAX_HANDLES +# define PJ_IOQUEUE_MAX_HANDLES (64) +#endif + +/** + * Overrides FD_SETSIZE so it is consistent throughout the library. + * OS specific configuration header (compat/os_*) might have declared + * FD_SETSIZE, thus we only set if it hasn't been declared. + * + * Default: #PJ_IOQUEUE_MAX_HANDLES + */ +#ifndef FD_SETSIZE +# define FD_SETSIZE PJ_IOQUEUE_MAX_HANDLES +#endif + +/** + * Has semaphore functionality? + * + * Default: 1 + */ +#ifndef PJ_HAS_SEMAPHORE +# define PJ_HAS_SEMAPHORE 1 +#endif + + +/** + * Event object (for synchronization, e.g. in Win32) + * + * Default: 1 + */ +#ifndef PJ_HAS_EVENT_OBJ +# define PJ_HAS_EVENT_OBJ 1 +#endif + + +/** + * Enable library's extra check. + * If this macro is enabled, #PJ_ASSERT_RETURN macro will expand to + * run-time checking. If this macro is disabled, #PJ_ASSERT_RETURN + * will simply evaluate to #pj_assert(). + * + * You can disable this macro to reduce size, at the risk of crashes + * if invalid value (e.g. NULL) is passed to the library. + * + * Default: 1 + */ +#ifndef PJ_ENABLE_EXTRA_CHECK +# define PJ_ENABLE_EXTRA_CHECK 1 +#endif + + +/** + * Enable name registration for exceptions with #pj_exception_id_alloc(). + * If this feature is enabled, then the library will keep track of + * names associated with each exception ID requested by application via + * #pj_exception_id_alloc(). + * + * Disabling this macro will reduce the code and .bss size by a tad bit. + * See also #PJ_MAX_EXCEPTION_ID. + * + * Default: 1 + */ +#ifndef PJ_HAS_EXCEPTION_NAMES +# define PJ_HAS_EXCEPTION_NAMES 1 +#endif + +/** + * Maximum number of unique exception IDs that can be requested + * with #pj_exception_id_alloc(). For each entry, a small record will + * be allocated in the .bss segment. + * + * Default: 16 + */ +#ifndef PJ_MAX_EXCEPTION_ID +# define PJ_MAX_EXCEPTION_ID 16 +#endif + +/** @} */ + +/******************************************************************** + * General macros. + */ + +/** + * @def PJ_INLINE(type) + * @param type The return type of the function. + * Expand the function as inline. + */ +#define PJ_INLINE(type) PJ_INLINE_SPECIFIER type + +/** + * @def PJ_DECL(type) + * @param type The return type of the function. + * Declare a function. + */ +/** + * @def PJ_DECL_NO_RETURN(type) + * @param type The return type of the function. + * Declare a function that will not return. + */ +/** + * @def PJ_BEGIN_DECL + * Mark beginning of declaration section in a header file. + */ +/** + * @def PJ_END_DECL + * Mark end of declaration section in a header file. + */ +#ifdef __cplusplus +# define PJ_DECL(type) type +# define PJ_DECL_NO_RETURN(type) type PJ_NORETURN +# define PJ_BEGIN_DECL extern "C" { +# define PJ_END_DECL } +#else +# define PJ_DECL(type) extern type +# define PJ_DECL_NO_RETURN(type) PJ_NORETURN type +# define PJ_BEGIN_DECL +# define PJ_END_DECL +#endif + +/** + * @def PJ_DEF(type) + * @param type The return type of the function. + * Define a function. + */ +#define PJ_DEF(type) type + +/** + * @def PJ_EXPORT_SYMBOL(sym) + * @param sym The symbol to export. + * Export the specified symbol in compilation type that requires export + * (e.g. Linux kernel). + */ +#ifdef __PJ_EXPORT_SYMBOL +# define PJ_EXPORT_SYMBOL(sym) __PJ_EXPORT_SYMBOL(sym) +#else +# define PJ_EXPORT_SYMBOL(sym) +#endif + +/** + * @def PJ_IDECL(type) + * @param type The function's return type. + * Declare a function that may be expanded as inline. + */ +/** + * @def PJ_IDEF(type) + * @param type The function's return type. + * Define a function that may be expanded as inline. + */ + +#if PJ_FUNCTIONS_ARE_INLINED +# define PJ_IDECL(type) PJ_INLINE(type) +# define PJ_IDEF(type) PJ_INLINE(type) +#else +# define PJ_IDECL(type) PJ_DECL(type) +# define PJ_IDEF(type) PJ_DEF(type) +#endif + +/** + * @def PJ_UNUSED_ARG(arg) + * @param arg The argument name. + * PJ_UNUSED_ARG prevents warning about unused argument in a function. + */ +#define PJ_UNUSED_ARG(arg) (void)arg + +/** + * @def PJ_TODO(id) + * @param id Any identifier that will be printed as TODO message. + * PJ_TODO macro will display TODO message as warning during compilation. + * Example: PJ_TODO(CLEAN_UP_ERROR); + */ +#ifndef PJ_TODO +# define PJ_TODO(id) TODO___##id: +#endif + + +/******************************************************************** + * Sanity Checks + */ +#ifndef PJ_HAS_HIGH_RES_TIMER +# error "PJ_HAS_HIGH_RES_TIMER is not defined!" +#endif + +#if !defined(PJ_HAS_PENTIUM) +# error "PJ_HAS_PENTIUM is not defined!" +#endif + +#if !defined(PJ_IS_LITTLE_ENDIAN) +# error "PJ_IS_LITTLE_ENDIAN is not defined!" +#endif + +#if !defined(PJ_IS_BIG_ENDIAN) +# error "PJ_IS_BIG_ENDIAN is not defined!" +#endif + + + +PJ_BEGIN_DECL + +/** + * PJLIB version string. + */ +extern const char *PJ_VERSION; + +/** + * Dump configuration to log with verbosity equal to info(3). + */ +PJ_DECL(void) pj_dump_config(void); + +PJ_END_DECL + + +#endif /* __PJ_CONFIG_H__ */ + diff --git a/pjlib/include/pj/ctype.h b/pjlib/include/pj/ctype.h index 943034f68..4df689ce2 100644 --- a/pjlib/include/pj/ctype.h +++ b/pjlib/include/pj/ctype.h @@ -1,119 +1,119 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/ctype.h 4 10/14/05 12:26a Bennylp $ */ -#ifndef __PJ_CTYPE_H__ -#define __PJ_CTYPE_H__ - -/** - * @file ctype.h - * @brief C type helper macros. - */ - -#include - -/** - * @defgroup pj_ctype ctype - Character Type - * @ingroup PJ_MISC - * @{ - * - * This module contains several inline functions/macros for testing or - * manipulating character types. It is provided in PJLIB because PJLIB - * must not depend to LIBC. - */ - -/** - * Returns a non-zero value if either isalpha or isdigit is true for c. - * @param c The integer character to test. - * @return Non-zero value if either isalpha or isdigit is true for c. - */ -PJ_INLINE(int) pj_isalnum(int c) { return isalnum(c); } - -/** - * Returns a non-zero value if c is a particular representation of an - * alphabetic character. - * @param c The integer character to test. - * @return Non-zero value if c is a particular representation of an - * alphabetic character. - */ -PJ_INLINE(int) pj_isalpha(int c) { return isalpha(c); } - -/** - * Returns a non-zero value if c is a particular representation of an - * ASCII character. - * @param c The integer character to test. - * @return Non-zero value if c is a particular representation of - * an ASCII character. - */ -PJ_INLINE(int) pj_isascii(int c) { return isascii(c); } - -/** - * Returns a non-zero value if c is a particular representation of - * a decimal-digit character. - * @param c The integer character to test. - * @return Non-zero value if c is a particular representation of - * a decimal-digit character. - */ -PJ_INLINE(int) pj_isdigit(int c) { return isdigit(c); } - -/** - * Returns a non-zero value if c is a particular representation of - * a space character (0x09 - 0x0D or 0x20). - * @param c The integer character to test. - * @return Non-zero value if c is a particular representation of - * a space character (0x09 - 0x0D or 0x20). - */ -PJ_INLINE(int) pj_isspace(int c) { return isspace(c); } - -/** - * Returns a non-zero value if c is a particular representation of - * a lowercase character. - * @param c The integer character to test. - * @return Non-zero value if c is a particular representation of - * a lowercase character. - */ -PJ_INLINE(int) pj_islower(int c) { return islower(c); } - - -/** - * Returns a non-zero value if c is a particular representation of - * a uppercase character. - * @param c The integer character to test. - * @return Non-zero value if c is a particular representation of - * a uppercase character. - */ -PJ_INLINE(int) pj_isupper(int c) { return isupper(c); } - -/** - * Returns a non-zero value if c is a particular representation of - * an hexadecimal digit character. - * @param c The integer character to test. - * @return Non-zero value if c is a particular representation of - * an hexadecimal digit character. - */ -PJ_INLINE(int) pj_isxdigit(int c){ return isxdigit(c); } - -/** - * Returns a non-zero value if c is a either a space (' ') or horizontal - * tab ('\\t') character. - * @param c The integer character to test. - * @return Non-zero value if c is a either a space (' ') or horizontal - * tab ('\\t') character. - */ -PJ_INLINE(int) pj_isblank(int c) { return isblank(c); } - -/** - * Converts character to lowercase. - * @param c The integer character to convert. - * @return Lowercase character of c. - */ -PJ_INLINE(int) pj_tolower(int c) { return tolower(c); } - -/** - * Converts character to uppercase. - * @param c The integer character to convert. - * @return Uppercase character of c. - */ -PJ_INLINE(int) pj_toupper(int c) { return toupper(c); } - -/** @} */ - -#endif /* __PJ_CTYPE_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/ctype.h 4 10/14/05 12:26a Bennylp $ */ +#ifndef __PJ_CTYPE_H__ +#define __PJ_CTYPE_H__ + +/** + * @file ctype.h + * @brief C type helper macros. + */ + +#include + +/** + * @defgroup pj_ctype ctype - Character Type + * @ingroup PJ_MISC + * @{ + * + * This module contains several inline functions/macros for testing or + * manipulating character types. It is provided in PJLIB because PJLIB + * must not depend to LIBC. + */ + +/** + * Returns a non-zero value if either isalpha or isdigit is true for c. + * @param c The integer character to test. + * @return Non-zero value if either isalpha or isdigit is true for c. + */ +PJ_INLINE(int) pj_isalnum(int c) { return isalnum(c); } + +/** + * Returns a non-zero value if c is a particular representation of an + * alphabetic character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of an + * alphabetic character. + */ +PJ_INLINE(int) pj_isalpha(int c) { return isalpha(c); } + +/** + * Returns a non-zero value if c is a particular representation of an + * ASCII character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * an ASCII character. + */ +PJ_INLINE(int) pj_isascii(int c) { return isascii(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * a decimal-digit character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a decimal-digit character. + */ +PJ_INLINE(int) pj_isdigit(int c) { return isdigit(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * a space character (0x09 - 0x0D or 0x20). + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a space character (0x09 - 0x0D or 0x20). + */ +PJ_INLINE(int) pj_isspace(int c) { return isspace(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * a lowercase character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a lowercase character. + */ +PJ_INLINE(int) pj_islower(int c) { return islower(c); } + + +/** + * Returns a non-zero value if c is a particular representation of + * a uppercase character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * a uppercase character. + */ +PJ_INLINE(int) pj_isupper(int c) { return isupper(c); } + +/** + * Returns a non-zero value if c is a particular representation of + * an hexadecimal digit character. + * @param c The integer character to test. + * @return Non-zero value if c is a particular representation of + * an hexadecimal digit character. + */ +PJ_INLINE(int) pj_isxdigit(int c){ return isxdigit(c); } + +/** + * Returns a non-zero value if c is a either a space (' ') or horizontal + * tab ('\\t') character. + * @param c The integer character to test. + * @return Non-zero value if c is a either a space (' ') or horizontal + * tab ('\\t') character. + */ +PJ_INLINE(int) pj_isblank(int c) { return isblank(c); } + +/** + * Converts character to lowercase. + * @param c The integer character to convert. + * @return Lowercase character of c. + */ +PJ_INLINE(int) pj_tolower(int c) { return tolower(c); } + +/** + * Converts character to uppercase. + * @param c The integer character to convert. + * @return Uppercase character of c. + */ +PJ_INLINE(int) pj_toupper(int c) { return toupper(c); } + +/** @} */ + +#endif /* __PJ_CTYPE_H__ */ + diff --git a/pjlib/include/pj/doxygen.h b/pjlib/include/pj/doxygen.h index c67326532..cda0517b7 100644 --- a/pjlib/include/pj/doxygen.h +++ b/pjlib/include/pj/doxygen.h @@ -1,880 +1,880 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/doxygen.h 5 10/29/05 10:27p Bennylp $ */ -#ifndef __PJ_DOXYGEN_H__ -#define __PJ_DOXYGEN_H__ - -/** - * @file doxygen.h - * @brief Doxygen's mainpage. - */ - -/*//////////////////////////////////////////////////////////////////////////*/ -/* - INTRODUCTION PAGE - */ - -/** - * @mainpage Welcome to PJLIB! - * - * @section intro_sec What is PJLIB - * - * PJLIB is a small foundation library written in C for making scalable - * applications. Because of its small footprint, it can be used in embedded - * applications (we hope so!), but yet the library is also aimed for - * facilitating high performance protocol stacks. - * - * PJLIB is released under LGPL terms. - * - * @section download_sec Download - * - * PJLIB and all documentation can be downloaded from - * http://www.bulukucing.org. - * - * - * @section how_to_use_sec About This Documentation - * - * This document is generated directly from PJLIB source file using - * \a doxygen (http://www.doxygen.org). Doxygen is a great (and free!) - * tools for generating such documentation. - * - * @subsection doc_ver_subsec Version - * - * This document corresponds to PJLIB version 0.3-pre2. - * - * - * @subsection find_samples_subsec How to Read This Document - * - * This documentation is laid out more to be a reference guide instead - * of tutorial, therefore first time users may find it difficult to - * grasp PJLIB by reading this document alone. - * - * However, we've tried our best to make this document easy to follow. - * For first time users, we would suggest that you follow these steps - * when reading this documentation: - * - * - continue reading this introduction chapter. At the end of this - * chapter, you'll find section called \ref pjlib_fundamentals_sec - * which should guide you to understand basic things about PJLIB. - * - * - find information about specific features that you want to use - * in PJLIB. Use the Module Index to find out about all - * features in PJLIB (if you're browsing the HTML documentation, - * click on the \a Module link on top of the page, or if you're - * reading the PDF documentation, click on \a Module \a Documentation - * on the navigation pane on the left). - * - * @subsection doc_organize_sec How To's - * - * Please find below links to specific tasks that you probably - * want to do: - * - * - How to Build PJLIB - *\n - * Please refer to \ref pjlib_build_sys_pg page for more information. - * - * - How to Use PJLIB in My Application - *\n - * Please refer to \ref configure_app_sec for more information. - * - * - How to Port PJLIB - *\n - * Please refer to \ref porting_pjlib_pg page. - * - * - Where to Read Samples Documentation - *\n - * Most of the modules provide link to the corresponding sample file. - * Alternatively, to get the list of all examples, you can click on - * Related Pages on the top of HTML document or on - * PJLIB Page Documentation on navigation pane of your PDF reader. - * - * - * - * - * - * @section features_sec Features - * - * @subsection open_source_feat It's Open Source! - * - * PJLIB is currently released on LGPL license. We may release PJLIB under - * additional schemes in the future (such as GPL or MPL) to incorporate - * linking with specific application, however, one thing for sure is - * we will NEVER be able to make PJLIB a proprietary software. - * - * @subsection extreme_portable_feat Extreme Portability - * - * PJLIB is designed to be extremely portable. It can run on any kind - * of processors (16-bit, 32-bit, or 64-bit, big or little endian, single - * or multi-processors) and operating systems. Floating point or no - * floating point. Multi-threading or not. - * It can even run in environment where no ANSI LIBC is available. - * - * Currently PJLIB is being ported to: - * - x86, Win32 (Win95/98/ME, NT/2000/XP/2003, mingw). - * - x86, Linux (user mode and as kernel module(!)). - * - alpha, Linux - * And coming up: - * - x86, eCos - * - ultra-II, Solaris. - * - powerpc, MacOS - * - m68k, PalmOS. - * - arm, PocketPC - * - * No other library is known to have this extreme portability! - * - * @subsection small_size_feat Small in Size - * - * One of the primary objectives is to have library that is small in size for - * typical embedded applications. As a rough guidance, we aim to keep the - * library size below 100KB for it to be considered as small. - * As the result, most of the functionalities in the library can be tailored - * to meet the requirements; user can enable/disable specific functionalities - * to get the desired size/performance/functionality balance. - * - * For more info, please see @ref pj_config. - * - * @subsection no_dyn_mem No Dynamic Memory Allocations - * - * The central idea of PJLIB is that for applications to run as fast as it can, - * it should not use \a malloc() at all, but instead should get the memory - * from a preallocated storage pool. There are few things that can be - * optimized with this approach: - * - * - \a alloc() is a O(1) operation. - * - no mutex is used inside alloc(). It is assumed that synchronization - * will be used in higher abstraction by application anyway. - * - no \a free() is required. All chunks will be deleted when the pool is - * destroyed. - * - * The performance gained on some systems can be as high as 10x speed up - * against \a malloc() and \a free(). - * - * For more information, see \ref PJ_POOL_GROUP - * - * - * @subsection os_abstract_feat Operating System Abstraction - * - * PJLIB has abstractions for features that are normally not portable - * across operating systems: - * - @ref PJ_THREAD - *\n - * Portable thread manipulation. - * - @ref PJ_TLS - *\n - * Storing data in thread's private data. - * - @ref PJ_MUTEX - *\n - * Mutual exclusion protection. - * - @ref PJ_SEM - *\n - * Semaphores. - * - @ref PJ_ATOMIC - *\n - * Atomic variables and their operations. - * - @ref PJ_CRIT_SEC - *\n - * Fast locking of critical sections. - * - @ref PJ_LOCK - *\n - * High level abstraction for lock objects. - * - @ref PJ_EVENT - *\n - * Event object. - * - @ref PJ_TIME - *\n - * Portable time manipulation. - * - @ref PJ_TIMESTAMP - *\n - * High resolution time value. - * - etc. - * - * - * @subsection ll_network_io_sec Low-Level Network I/O - * - * PJLIB has very portable abstraction and fairly complete set of API for - * doing network I/O communications. At the lowest level, PJLIB provides: - * - * - @ref PJ_SOCK - *\n - * A highly portable socket abstraction, runs on all kind of - * network APIs such as standard BSD socket, Windows socket, Linux - * \b kernel socket, PalmOS networking API, etc. - * - * - @ref pj_addr_resolve - *\n - * Portable address resolution, which implements #pj_gethostbyname(). - * - * - @ref PJ_SOCK_SELECT - *\n - * A portable \a select() like API (#pj_sock_select()) which can be - * implemented with various back-end. - * - * - * @subsection hl_network_io_sec High-Level Network I/O - * - * At higher abstraction, PJLIB provides @ref PJ_IOQUEUE, - * which promotes creating high performance network - * applications by managing asynchronous I/O. This is a passive framework - * that utilizes the most effective way to manage asynchronous I/O - * on a given platform, such as: - * - IoCompletionPort on WinNT, - * - on Linux it can use either /dev/epoll or aio. - * - or to fall back to use @a select() - * - * At even a higher abstraction, PJLIB provides @ref PJ_EQUEUE, which - * combines asynchronous I/O with timer management and thread management - * to fasilitate creating trully high performance, event driven - * application. - * - * - * @subsection timer_mgmt_sec Timer Management - * - * A passive framework for managing timer, see @ref PJ_TIMER for more info. - * There is also function to retrieve high resolution timestamp - * from the system (see @ref PJ_TIMESTAMP). - * - * - * @subsection lexical_scanner_sec Lexical Scanner - * - * A fast, small, top-down lexical scanner to create fully optimized - * hand-written parser. See @ref PJ_SCAN for more info. - * - * @subsection data_struct_sec Various Data Structures - * - * Various data structures are provided in the library: - * - * - @ref PJ_PSTR - * - @ref PJ_ARRAY - * - @ref PJ_HASH - * - @ref PJ_LIST - * - @ref PJ_RBTREE - * - * - * @subsection exception_sec Exception Construct - * - * A convenient TRY/CATCH like construct to propagate errors, which by - * default are used by the @ref PJ_POOL_GROUP "memory pool" and - * the @ref PJ_SCAN "scanner". The exception - * construct can be used to write programs like below: - * - *
- *    #define SYNTAX_ERROR  1
- *
- *    PJ_TRY {
- *       msg = NULL;
- *       msg = parse_msg(buf, len);
- *    }
- *    PJ_CATCH ( SYNTAX_ERROR ) {
- *       .. handle error ..
- *    }
- *    PJ_END;
- * 
- * - * Please see @ref PJ_EXCEPT for more information. - * - * - * @subsection logging_sec Logging Facility - * - * PJLIB @ref PJ_LOG consists of macros to write logging information to - * some output device. Some of the features of the logging facility: - * - * - the verbosity can be fine-tuned both at compile time (to control - * the library size) or run-time (to control the verbosity of the - * information). - * - output device is configurable (e.g. stdout, printk, file, etc.) - * - log decoration is configurable. - * - * See @ref PJ_LOG for more information. - * - * - * @subsection guid_gen_sec Random and GUID Generation - * - * PJLIB provides facility to create random string - * (#pj_create_random_string()) or globally unique identifier - * (see @ref PJ_GUID). - * - * - * - * @section configure_app_sec Configuring Application to use PJLIB - * - * @subsection pjlib_compil_sec Building PJLIB - * - * Follow the instructions in \ref pjlib_build_sys_pg to build - * PJLIB. - * - * @subsection pjlib_compil_app_sec Building Applications with PJLIB - * - * Use the following settings when building applications with PJLIB. - * - * @subsubsection compil_inc_dir_sec Include Search Path - * - * Add this to your include search path ($PJLIB is PJLIB root directory): - *
- *   $PJLIB/include
- * 
- * - * @subsubsection compil_inc_file_sec Include PJLIB Header - * - * To include all PJLIB headers: - * \verbatim - #include - \endverbatim - * - * Alternatively, you can include individual PJLIB headers like this: - * \verbatim - #include - #include - \endverbatim - * - * - * @subsubsection compil_lib_dir_sec Library Path - * - * Add this to your library search path: - *
- *   $PJLIB/lib
- * 
- * - * Then add the appropriate PJLIB library to your link specification. For - * example, you would add \c libpj-i386-linux-gcc.a when you're building - * applications in Linux. - * - * - * @subsection pjlib_fundamentals_sec Principles in Using PJLIB - * - * Few things that you \b MUST do when using PJLIB, to make sure that - * you create trully portable applications. - * - * @subsubsection call_pjlib_init_sec Call pj_init() - * - * Before you do anything else, call \c pj_init(). This would make sure that - * PJLIB system is properly set up. - * - * @subsubsection no_ansi_subsec Do NOT Use ANSI C - * - * Contrary to popular teaching, ANSI C (and LIBC) is not the most portable - * library in the world, nor it's the most ubiquitous. For example, LIBC - * is not available in Linux kernel. Also normally LIBC will be excluded - * from compilation of RTOSes to reduce size. - * - * So for maximum portability, do NOT use ANSI C. Do not even try to include - * any other header files outside . Stick with the functionalities - * provided by PJLIB. - * - * - * @subsubsection string_rep_subsubsec Use pj_str_t instead of C Strings - * - * PJLIB uses pj_str_t instead of normal C strings. You SHOULD follow this - * convention too. Remember, ANSI string-h is not always available. And - * PJLIB string is faster! - * - * @subsubsection mem_alloc_subsubsec Use Pool for Memory Allocations - * - * You MUST NOT use \a malloc() or any other memory allocation functions. - * Use PJLIB pool instead! It's faster and most portable. - * - * @subsection logging_subsubsec Use Logging for Text Display - * - * DO NOT use for text output. Use PJLIB logging instead. - * - * - * @section porting_pjlib_sec0 Porting PJLIB - * - * Please see \ref porting_pjlib_pg page on more information to port - * PJLIB to new target. - * - * @section enjoy_sec Enjoy Using PJLIB! - * - * We hope that you find PJLIB usefull for your application. If you - * have any questions, suggestions, critics, bug fixes, or anything - * else, we would be happy to hear it. - * - * Enjoy using PJLIB! - * - * Benny Prijono < bennylp at bulukucing dot org > - */ - - - - -/*////////////////////////////////////////////////////////////////////////// */ -/* - BUILDING AND INSTALLING PJLIB - */ - - - -/** - * @page pjlib_build_sys_pg Building, and Installing PJLIB - * - * @section build_sys_install_sec Build and Installation - * - * @subsection build_sys_install_win32_sec Visual Studio - * - * The PJLIB Visual Studio workspace supports the building of PJLIB - * for Win32 target. Although currently only the Visual Studio 6 Workspace is - * actively maintained, developers with later version of Visual Studio - * can easily imports VS6 workspace into their IDE. - * - * To start building PJLIB projects with Visual Studio 6 or later, open - * the \a workspace file in the corresponding \b \c build directory. You have - * several choices on which \a dsw file to open: - \verbatim - $PJPROJECT/build/pjproject.dsw - $PJPROJECT/pjlib/build/pjlib.dsw - $PJPROJECT/pjsip/build/pjsip.dsw - ..etc - \endverbatim - * - * The easiest way is to open pjproject.dsw file in \b \c $PJPROJECT/build - * directory. However this will only build the required projects, not - * the complete projects. For example, the PJLIB test and samples projects - * are not included in this workspace. To build the complete projects, you must - * open and build each \a dsw file in \c build directory in each - * subprojects. For example, to open the complete PJLIB workspace, open - * pjlib.dsw in $PJPROJECT/pjlib/build directory. - * - * @subsection build_sys_install_unix_sec Make System - * - * For other targets, PJLIB provides a rather comprehensive build system - * that uses GNU \a make (and only GNU \a make will work). - * Currently, the build system supports building * PJLIB for these targets: - * - i386/Win32/mingw - * - i386/Linux - * - i386/Linux (kernel) - * - * Generally, steps required to build the projects are: - * - \verbatim - $ cd /home/user/pjproject-0.3 # <-- go to $PJPROJECT\n - $ vi build.mak # <-- set build target etc\n - $ cd pjlib/build # <-- go to projet's build dir\n - $ make # <-- build the project\n - \endverbatim - * - * For other project, \a cd to build directory in the project - * and execute \a make from there. - * - * \note For Linux kernel target, there are additional steps required, which - * will be explained in section \ref linux_kern_target_subsec. - * - * @subsubsection build_mak_sec Editing build.mak - * - * The \c build.mak file in \c $PJPROJECT root directory is used to - * specify the build configuration. This file is expected to export - * the following \a make variables: - * - * - \c MACHINE_NAME - *\n - * Target machine/processor, one of: { i386 }. - * - * - \c OS_NAME - *\n - * Target operating system, one of: { win32 | linux | linux-kernel }. - * - * - \c CC_NAME - *\n - * Compiler name: { gcc | vc } - * - * - \c HOST_NAME - *\n - * Build host: { unix | mingw } - * - * These variables will cause the correct configuration file in - * \c $PJPROJECT/build directory to be executed by \a make. For - * example, specifying \c OS_NAME=linux will cause file \c os-linux.mak - * in \c build directory to be executed. These files contain specific - * configuration for the option that is selected. - * - * For Linux kernel target, you are also required to declare the following - * variables in this file: - * - \c KERNEL_DIR: full path of kernel source tree. - * - \c KERNEL_ARCH: kernel ARCH options (e.g. "ARCH=um"), or leave blank - * for default. - * - \c PJPROJECT_DIR: full path of PJPROJECT source tree. - * - * Apart from these, there are also additional steps required to build - * Linux kernel target, which will be explained in \ref linux_kern_target_subsec. - * - * @subsubsection build_dir_sec Files in "build" Directory - * - * The *.mak files in \c $PJPROJECT/build directory are used to specify - * the configuration for the specified compiler, target machine target - * operating system, and host options. These files will be executed - * (included) by \a make during building process, depending on the values - * specified in $PJPROJECT/build.mak file. - * - * Normally you don't need to edit these files, except when you're porting - * PJLIB to new target. - * - * Below are the description of some files in this directory: - * - * - rules.mak: contains generic rules always included during make. - * - cc-gcc.mak: rules when gcc is used for compiler. - * - cc-vc.mak: rules when MSVC compiler is used. - * - host-mingw.mak: rules for building in mingw host. - * - host-unix.mak: rules for building in Unix/Posix host. - * - host-win32.mak: rules for building in Win32 command console - * (only valid when VC is used). - * - m-i386.mak: rules when target machine is an i386 processor. - * - m-m68k.mak: rules when target machine is an m68k processor. - * - os-linux.mak: rules when target OS is Linux. - * - os-linux-kernel.mak: rules when PJLIB is to be build as - * part of Linux kernel. - * - os-win32.mak: rules when target OS is Win32. - * - * @subsubsection invoking_make_sec Invoking make - * - * Normally, \a make is invoked in \c build directory under each project. - * For example, to build PJLIB, you would invoke \a make in - * \c $PJPROJECT/pjlib/build directory like below: - * - \verbatim - $ cd pjlib/build - $ make - \endverbatim - * - * - * - * @subsubsection linux_kern_target_subsec Linux Kernel Target - * - * \note - * BUILDING APPLICATIONS IN LINUX KERNEL MODE IS A VERY DANGEROUS BUSINESS. - * YOU MAY CRASH THE WHOLE OF YOUR SYSTEM, CORRUPT YOUR HARDISK, ETC. PJLIB - * KERNEL MODULES ARE STILL IN EXPERIMENTAL PHASE. DO NOT RUN IT IN PRODUCTION - * SYSTEMS OR OTHER SYSTEMS WHERE RISK OF LOSS OF DATA IS NOT ACCEPTABLE. - * YOU HAVE BEEN WARNED. - * - * \note - * User Mode Linux (UML) provides excellent way to experiment with Linux - * kernel without risking the stability of the host system. See - * http://user-mode-linux.sourceforge.net for details. - * - * \note - * I only use UML to experiment with PJLIB kernel modules. - * I wouldn't be so foolish to use my host Linux machine to experiment - * with this. - * - * \note - * You have been warned. - * - * For building PJLIB for Linux kernel target, there are additional steps required. - * In general, the additional tasks are: - * - Declare some more variables in build.mak file (this - * has been explained in \ref build_mak_sec above). - * - Perform these two small modifications in kernel source tree. - * - * There are two small modification need to be applied to the kernel tree. - * - * 1. Edit Makefile in kernel root source tree. - * - * Add the following lines at the end of the Makefile in your - * $KERNEL_SRC dir: - \verbatim -script: - $(SCRIPT) - \endverbatim - * - * \note Remember to replace spaces with tab in the Makefile. - * - * The modification above is needed to capture kernel's \c $CFLAGS and - * \c $CFLAGS_MODULE which will be used for PJLIB's compilation. - * - * 2. Add Additional Exports. - * - * We need the kernel to export some more symbols for our use. So we declare - * the additional symbols to be exported in extra-exports.c file, and add - * a this file to be compiled into the kernel: - * - * - Copy the file extra-exports.c from pjlib/src/pj - * directory to $KERNEL_SRC/kernel/ directory. - * - Edit Makefile in that directory, and add this line - * somewhere after the declaration of that variable: - \verbatim -obj-y += extra-exports.o - \endverbatim - * - * To illustrate what have been done in your kernel source tree, below - * is screenshot of my kernel source tree _after_ the modification. - * - \verbatim -[root@vpc-linux linux-2.6.7]# pwd -/usr/src/linux-2.6.7 -[root@vpc-linux linux-2.6.7]# -[root@vpc-linux linux-2.6.7]# -[root@vpc-linux linux-2.6.7]# tail Makefile - -endif # skip-makefile - -FORCE: - -.PHONY: script - -script: - $(SCRIPT) - -[root@vpc-linux linux-2.6.7]# -[root@vpc-linux linux-2.6.7]# -[root@vpc-linux linux-2.6.7]# head kernel/extra-exports.c -#include -#include - -EXPORT_SYMBOL(sys_select); - -EXPORT_SYMBOL(sys_epoll_create); -EXPORT_SYMBOL(sys_epoll_ctl); -EXPORT_SYMBOL(sys_epoll_wait); - -EXPORT_SYMBOL(sys_socket); -[root@vpc-linux linux-2.6.7]# -[root@vpc-linux linux-2.6.7]# -[root@vpc-linux linux-2.6.7]# head -15 kernel/Makefile -# -# Makefile for the linux kernel. -# - -obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ - exit.o itimer.o time.o softirq.o resource.o \ - sysctl.o capability.o ptrace.o timer.o user.o \ - signal.o sys.o kmod.o workqueue.o pid.o \ - rcupdate.o intermodule.o extable.o params.o posix-timers.o \ - kthread.o - -obj-y += extra-exports.o - -obj-$(CONFIG_FUTEX) += futex.o -obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o -[root@vpc-linux linux-2.6.7]# - - \endverbatim - * - * Then you must rebuild the kernel. - * If you fail to do this, you won't be able to insmod pjlib. - * - * \note You will see a lots of warning messages during pjlib-test compilation. - * The warning messages complain about unresolved symbols which are defined - * in pjlib module. You can safely ignore these warnings. However, you can not - * ignore warnings about non-pjlib unresolved symbols. - * - * - * @subsection makefile_explained_sec Makefile Explained - * - * The \a Makefile for each project (e.g. PJLIB, PJSIP, etc) should be - * very similar in the contents. The Makefile is located under \c build - * directory in each project subdir. - * - * @subsubsection pjlib_makefile_subsec PJLIB Makefile. - * - * Below is PJLIB's Makefile: - * - * \include build/Makefile - * - * @subsubsection pjlib_os_makefile_subsec PJLIB os-linux.mak. - * - * Below is file os-linux.mak file in - * $PJPROJECT/pjlib/build directory, - * which is OS specific configuration file for Linux target that is specific - * for PJLIB project. For \b global OS specific configuration, please see - * $PJPROJECT/build/os-*.mak. - * - * \include build/os-linux.mak - * - */ - - -/*////////////////////////////////////////////////////////////////////////// */ -/* - PORTING PJLIB - */ - - - -/** - * @page porting_pjlib_pg Porting PJLIB - * - * - * @section new_arch_sec Porting to New CPU Architecture - * - * Below is step-by-step guide to add support for new CPU architecture. - * This sample is based on porting to Alpha architecture; however steps for - * porting to other CPU architectures should be pretty similar. - * - * Also note that in this example, the operating system used is Linux. - * Should you wish to add support for new operating system, then follow - * the next section \ref porting_os_sec. - * - * Step-by-step guide to port to new CPU architecture: - * - decide the name for the new architecture. In this case, we choose - * alpha. - * - edit file $PJPROJECT/build.mak, and add new section for - * the new target: - *
- *      #
- *      # Linux alpha, gcc
- *      #
- *      export MACHINE_NAME := alpha
- *      export OS_NAME := linux
- *      export CC_NAME := gcc
- *      export HOST_NAME := unix
- *    
- * - * - create a new file $PJPROJECT/build/m-alpha.mak. - * Alternatively create a copy from other file in this directory. - * The contents of this file will look something like: - *
- *      export M_CFLAGS := $(CC_DEF)PJ_M_ALPHA=1
- *      export M_CXXFLAGS :=
- *      export M_LDFLAGS :=
- *      export M_SOURCES :=
- *    
- * - create a new file $PJPROJECT/pjlib/include/pj/compat/m_alpha.h. - * Alternatively create a copy from other header file in this directory. - * The contents of this file will look something like: - *
- *      #define PJ_HAS_PENTIUM          0
- *      #define PJ_IS_LITTLE_ENDIAN     1
- *      #define PJ_IS_BIG_ENDIAN        0
- *    
- * - edit pjlib/include/pj/config.h. Add new processor - * configuration in this header file, like follows: - *
- *      ...
- *      #elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
- *      #   include 
- *      ...
- *    
- * - done. Build PJLIB with: - *
- *      $ cd $PJPROJECT/pjlib/build
- *      $ make dep
- *      $ make clean
- *      $ make
- *    
- * - * @section porting_os_sec Porting to New Operating System Target - * - * This section will try to give you rough guideline on how to - * port PJLIB to a new target. As a sample, we give the target a name tag, - * for example xos (for X OS). - * - * @subsection new_compat_os_h_file_sec Create New Compat Header File - * - * You'll need to create a new header file - * include/pj/compat/os_xos.h. You can copy as a - * template other header file and edit it accordingly. - * - * @subsection modify_config_h_file_sec Modify config.h - * - * Then modify file include/pj/config.h to include - * this file accordingly (e.g. when macro PJ_XOS is - * defined): - * - \verbatim - ... - #elif defined(PJ_XOS) - # include - #else - #... - \endverbatim - * - * @subsection new_target_mak_file_sec Create New Global Make Config File - * - * Then you'll need to create global configuration file that - * is specific for this OS, i.e. os-xos.mak in - * $PJPROJECT/build directory. - * - * At very minimum, the file will normally need to define - * PJ_XOS=1 in the \c CFLAGS section: - * - \verbatim -# -# $PJPROJECT/build/os-xos.mak: -# -export OS_CFLAGS := $(CC_DEF)PJ_XOS=1 -export OS_CXXFLAGS := -export OS_LDFLAGS := -export OS_SOURCES := - \endverbatim - * - * - * @subsection new_target_prj_mak_file_sec Create New Project's Make Config File - * - * Then you'll need to create xos-specific configuration file - * for PJLIB. This file is also named os-xos.mak, - * but its located in pjlib/build directory. - * This file will specify source files that are specific to - * this OS to be included in the build process. - * - * Below is a sample: - \verbatim -# -# pjlib/build/os-xos.mak: -# XOS specific configuration for PJLIB. -# -export PJLIB_OBJS += os_core_xos.o \ - os_error_unix.o \ - os_time_ansi.o -export TEST_OBJS += main.o -export TARGETS = pjlib pjlib-test - \endverbatim - * - * @subsection new_target_src_sec Create and Edit Source Files - * - * You'll normally need to create at least these files: - * - os_core_xos.c: core OS specific - * functionality. - * - os_timestamp_xos.c: how to get timestamp - * in this OS. - * - * Depending on how things are done in your OS, you may need - * to create these files: - * - os_error_*.c: how to manipulate - * OS error codes. Alternatively you may use existing - * os_error_unix.c if the OS has \c errno and - * \c strerror() function. - * - ioqueue_*.c: if the OS has specific method - * to perform asynchronous I/O. Alternatively you may - * use existing ioqueue_select.c if the OS supports - * \c select() function call. - * - sock_*.c: if the OS has specific method - * to perform socket communication. Alternatively you may - * use existing sock_bsd.c if the OS supports - * BSD socket API, and edit include/pj/compat/socket.h - * file accordingly. - * - * You will also need to check various files in - * include/pj/compat/*.h, to see if they're - * compatible with your OS. - * - * @subsection new_target_build_file_sec Build The Project - * - * After basic building blocks have been created for the OS, then - * the easiest way to see which parts need to be fixed is by building - * the project and see the error messages. - * - * @subsection new_target_edit_vs_new_file_sec Editing Existing Files vs Creating New File - * - * When you encounter compatibility errors in PJLIB during porting, - * you have three options on how to fix the error: - * - edit the existing *.c file, and give it #ifdef - * switch for the new OS, or - * - edit include/pj/compat/*.h instead, or - * - create a totally new file. - * - * Basicly there is no strict rule on which approach is the best - * to use, however the following guidelines may be used: - * - if the file is expected to be completely different than - * any existing file, then perhaps you should create a completely - * new file. For example, file os_core_xxx.c will - * normally be different for each OS flavour. - * - if the difference can be localized in include/compat - * header file, and existing #ifdef switch is there, - * then preferably you should edit this include/compat - * header file. - * - if the existing *.c file has #ifdef switch, - * then you may add another #elif switch there. This - * normally is used for behaviors that are not totally - * different on each platform. - * - other than that above, use your own judgement on whether - * to edit the file or create new file etc. - */ - -#endif /* __PJ_DOXYGEN_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/doxygen.h 5 10/29/05 10:27p Bennylp $ */ +#ifndef __PJ_DOXYGEN_H__ +#define __PJ_DOXYGEN_H__ + +/** + * @file doxygen.h + * @brief Doxygen's mainpage. + */ + +/*//////////////////////////////////////////////////////////////////////////*/ +/* + INTRODUCTION PAGE + */ + +/** + * @mainpage Welcome to PJLIB! + * + * @section intro_sec What is PJLIB + * + * PJLIB is a small foundation library written in C for making scalable + * applications. Because of its small footprint, it can be used in embedded + * applications (we hope so!), but yet the library is also aimed for + * facilitating high performance protocol stacks. + * + * PJLIB is released under LGPL terms. + * + * @section download_sec Download + * + * PJLIB and all documentation can be downloaded from + * http://www.bulukucing.org. + * + * + * @section how_to_use_sec About This Documentation + * + * This document is generated directly from PJLIB source file using + * \a doxygen (http://www.doxygen.org). Doxygen is a great (and free!) + * tools for generating such documentation. + * + * @subsection doc_ver_subsec Version + * + * This document corresponds to PJLIB version 0.3-pre2. + * + * + * @subsection find_samples_subsec How to Read This Document + * + * This documentation is laid out more to be a reference guide instead + * of tutorial, therefore first time users may find it difficult to + * grasp PJLIB by reading this document alone. + * + * However, we've tried our best to make this document easy to follow. + * For first time users, we would suggest that you follow these steps + * when reading this documentation: + * + * - continue reading this introduction chapter. At the end of this + * chapter, you'll find section called \ref pjlib_fundamentals_sec + * which should guide you to understand basic things about PJLIB. + * + * - find information about specific features that you want to use + * in PJLIB. Use the Module Index to find out about all + * features in PJLIB (if you're browsing the HTML documentation, + * click on the \a Module link on top of the page, or if you're + * reading the PDF documentation, click on \a Module \a Documentation + * on the navigation pane on the left). + * + * @subsection doc_organize_sec How To's + * + * Please find below links to specific tasks that you probably + * want to do: + * + * - How to Build PJLIB + *\n + * Please refer to \ref pjlib_build_sys_pg page for more information. + * + * - How to Use PJLIB in My Application + *\n + * Please refer to \ref configure_app_sec for more information. + * + * - How to Port PJLIB + *\n + * Please refer to \ref porting_pjlib_pg page. + * + * - Where to Read Samples Documentation + *\n + * Most of the modules provide link to the corresponding sample file. + * Alternatively, to get the list of all examples, you can click on + * Related Pages on the top of HTML document or on + * PJLIB Page Documentation on navigation pane of your PDF reader. + * + * + * + * + * + * @section features_sec Features + * + * @subsection open_source_feat It's Open Source! + * + * PJLIB is currently released on LGPL license. We may release PJLIB under + * additional schemes in the future (such as GPL or MPL) to incorporate + * linking with specific application, however, one thing for sure is + * we will NEVER be able to make PJLIB a proprietary software. + * + * @subsection extreme_portable_feat Extreme Portability + * + * PJLIB is designed to be extremely portable. It can run on any kind + * of processors (16-bit, 32-bit, or 64-bit, big or little endian, single + * or multi-processors) and operating systems. Floating point or no + * floating point. Multi-threading or not. + * It can even run in environment where no ANSI LIBC is available. + * + * Currently PJLIB is being ported to: + * - x86, Win32 (Win95/98/ME, NT/2000/XP/2003, mingw). + * - x86, Linux (user mode and as kernel module(!)). + * - alpha, Linux + * And coming up: + * - x86, eCos + * - ultra-II, Solaris. + * - powerpc, MacOS + * - m68k, PalmOS. + * - arm, PocketPC + * + * No other library is known to have this extreme portability! + * + * @subsection small_size_feat Small in Size + * + * One of the primary objectives is to have library that is small in size for + * typical embedded applications. As a rough guidance, we aim to keep the + * library size below 100KB for it to be considered as small. + * As the result, most of the functionalities in the library can be tailored + * to meet the requirements; user can enable/disable specific functionalities + * to get the desired size/performance/functionality balance. + * + * For more info, please see @ref pj_config. + * + * @subsection no_dyn_mem No Dynamic Memory Allocations + * + * The central idea of PJLIB is that for applications to run as fast as it can, + * it should not use \a malloc() at all, but instead should get the memory + * from a preallocated storage pool. There are few things that can be + * optimized with this approach: + * + * - \a alloc() is a O(1) operation. + * - no mutex is used inside alloc(). It is assumed that synchronization + * will be used in higher abstraction by application anyway. + * - no \a free() is required. All chunks will be deleted when the pool is + * destroyed. + * + * The performance gained on some systems can be as high as 10x speed up + * against \a malloc() and \a free(). + * + * For more information, see \ref PJ_POOL_GROUP + * + * + * @subsection os_abstract_feat Operating System Abstraction + * + * PJLIB has abstractions for features that are normally not portable + * across operating systems: + * - @ref PJ_THREAD + *\n + * Portable thread manipulation. + * - @ref PJ_TLS + *\n + * Storing data in thread's private data. + * - @ref PJ_MUTEX + *\n + * Mutual exclusion protection. + * - @ref PJ_SEM + *\n + * Semaphores. + * - @ref PJ_ATOMIC + *\n + * Atomic variables and their operations. + * - @ref PJ_CRIT_SEC + *\n + * Fast locking of critical sections. + * - @ref PJ_LOCK + *\n + * High level abstraction for lock objects. + * - @ref PJ_EVENT + *\n + * Event object. + * - @ref PJ_TIME + *\n + * Portable time manipulation. + * - @ref PJ_TIMESTAMP + *\n + * High resolution time value. + * - etc. + * + * + * @subsection ll_network_io_sec Low-Level Network I/O + * + * PJLIB has very portable abstraction and fairly complete set of API for + * doing network I/O communications. At the lowest level, PJLIB provides: + * + * - @ref PJ_SOCK + *\n + * A highly portable socket abstraction, runs on all kind of + * network APIs such as standard BSD socket, Windows socket, Linux + * \b kernel socket, PalmOS networking API, etc. + * + * - @ref pj_addr_resolve + *\n + * Portable address resolution, which implements #pj_gethostbyname(). + * + * - @ref PJ_SOCK_SELECT + *\n + * A portable \a select() like API (#pj_sock_select()) which can be + * implemented with various back-end. + * + * + * @subsection hl_network_io_sec High-Level Network I/O + * + * At higher abstraction, PJLIB provides @ref PJ_IOQUEUE, + * which promotes creating high performance network + * applications by managing asynchronous I/O. This is a passive framework + * that utilizes the most effective way to manage asynchronous I/O + * on a given platform, such as: + * - IoCompletionPort on WinNT, + * - on Linux it can use either /dev/epoll or aio. + * - or to fall back to use @a select() + * + * At even a higher abstraction, PJLIB provides @ref PJ_EQUEUE, which + * combines asynchronous I/O with timer management and thread management + * to fasilitate creating trully high performance, event driven + * application. + * + * + * @subsection timer_mgmt_sec Timer Management + * + * A passive framework for managing timer, see @ref PJ_TIMER for more info. + * There is also function to retrieve high resolution timestamp + * from the system (see @ref PJ_TIMESTAMP). + * + * + * @subsection lexical_scanner_sec Lexical Scanner + * + * A fast, small, top-down lexical scanner to create fully optimized + * hand-written parser. See @ref PJ_SCAN for more info. + * + * @subsection data_struct_sec Various Data Structures + * + * Various data structures are provided in the library: + * + * - @ref PJ_PSTR + * - @ref PJ_ARRAY + * - @ref PJ_HASH + * - @ref PJ_LIST + * - @ref PJ_RBTREE + * + * + * @subsection exception_sec Exception Construct + * + * A convenient TRY/CATCH like construct to propagate errors, which by + * default are used by the @ref PJ_POOL_GROUP "memory pool" and + * the @ref PJ_SCAN "scanner". The exception + * construct can be used to write programs like below: + * + *
+ *    #define SYNTAX_ERROR  1
+ *
+ *    PJ_TRY {
+ *       msg = NULL;
+ *       msg = parse_msg(buf, len);
+ *    }
+ *    PJ_CATCH ( SYNTAX_ERROR ) {
+ *       .. handle error ..
+ *    }
+ *    PJ_END;
+ * 
+ * + * Please see @ref PJ_EXCEPT for more information. + * + * + * @subsection logging_sec Logging Facility + * + * PJLIB @ref PJ_LOG consists of macros to write logging information to + * some output device. Some of the features of the logging facility: + * + * - the verbosity can be fine-tuned both at compile time (to control + * the library size) or run-time (to control the verbosity of the + * information). + * - output device is configurable (e.g. stdout, printk, file, etc.) + * - log decoration is configurable. + * + * See @ref PJ_LOG for more information. + * + * + * @subsection guid_gen_sec Random and GUID Generation + * + * PJLIB provides facility to create random string + * (#pj_create_random_string()) or globally unique identifier + * (see @ref PJ_GUID). + * + * + * + * @section configure_app_sec Configuring Application to use PJLIB + * + * @subsection pjlib_compil_sec Building PJLIB + * + * Follow the instructions in \ref pjlib_build_sys_pg to build + * PJLIB. + * + * @subsection pjlib_compil_app_sec Building Applications with PJLIB + * + * Use the following settings when building applications with PJLIB. + * + * @subsubsection compil_inc_dir_sec Include Search Path + * + * Add this to your include search path ($PJLIB is PJLIB root directory): + *
+ *   $PJLIB/include
+ * 
+ * + * @subsubsection compil_inc_file_sec Include PJLIB Header + * + * To include all PJLIB headers: + * \verbatim + #include + \endverbatim + * + * Alternatively, you can include individual PJLIB headers like this: + * \verbatim + #include + #include + \endverbatim + * + * + * @subsubsection compil_lib_dir_sec Library Path + * + * Add this to your library search path: + *
+ *   $PJLIB/lib
+ * 
+ * + * Then add the appropriate PJLIB library to your link specification. For + * example, you would add \c libpj-i386-linux-gcc.a when you're building + * applications in Linux. + * + * + * @subsection pjlib_fundamentals_sec Principles in Using PJLIB + * + * Few things that you \b MUST do when using PJLIB, to make sure that + * you create trully portable applications. + * + * @subsubsection call_pjlib_init_sec Call pj_init() + * + * Before you do anything else, call \c pj_init(). This would make sure that + * PJLIB system is properly set up. + * + * @subsubsection no_ansi_subsec Do NOT Use ANSI C + * + * Contrary to popular teaching, ANSI C (and LIBC) is not the most portable + * library in the world, nor it's the most ubiquitous. For example, LIBC + * is not available in Linux kernel. Also normally LIBC will be excluded + * from compilation of RTOSes to reduce size. + * + * So for maximum portability, do NOT use ANSI C. Do not even try to include + * any other header files outside . Stick with the functionalities + * provided by PJLIB. + * + * + * @subsubsection string_rep_subsubsec Use pj_str_t instead of C Strings + * + * PJLIB uses pj_str_t instead of normal C strings. You SHOULD follow this + * convention too. Remember, ANSI string-h is not always available. And + * PJLIB string is faster! + * + * @subsubsection mem_alloc_subsubsec Use Pool for Memory Allocations + * + * You MUST NOT use \a malloc() or any other memory allocation functions. + * Use PJLIB pool instead! It's faster and most portable. + * + * @subsection logging_subsubsec Use Logging for Text Display + * + * DO NOT use for text output. Use PJLIB logging instead. + * + * + * @section porting_pjlib_sec0 Porting PJLIB + * + * Please see \ref porting_pjlib_pg page on more information to port + * PJLIB to new target. + * + * @section enjoy_sec Enjoy Using PJLIB! + * + * We hope that you find PJLIB usefull for your application. If you + * have any questions, suggestions, critics, bug fixes, or anything + * else, we would be happy to hear it. + * + * Enjoy using PJLIB! + * + * Benny Prijono < bennylp at bulukucing dot org > + */ + + + + +/*////////////////////////////////////////////////////////////////////////// */ +/* + BUILDING AND INSTALLING PJLIB + */ + + + +/** + * @page pjlib_build_sys_pg Building, and Installing PJLIB + * + * @section build_sys_install_sec Build and Installation + * + * @subsection build_sys_install_win32_sec Visual Studio + * + * The PJLIB Visual Studio workspace supports the building of PJLIB + * for Win32 target. Although currently only the Visual Studio 6 Workspace is + * actively maintained, developers with later version of Visual Studio + * can easily imports VS6 workspace into their IDE. + * + * To start building PJLIB projects with Visual Studio 6 or later, open + * the \a workspace file in the corresponding \b \c build directory. You have + * several choices on which \a dsw file to open: + \verbatim + $PJPROJECT/build/pjproject.dsw + $PJPROJECT/pjlib/build/pjlib.dsw + $PJPROJECT/pjsip/build/pjsip.dsw + ..etc + \endverbatim + * + * The easiest way is to open pjproject.dsw file in \b \c $PJPROJECT/build + * directory. However this will only build the required projects, not + * the complete projects. For example, the PJLIB test and samples projects + * are not included in this workspace. To build the complete projects, you must + * open and build each \a dsw file in \c build directory in each + * subprojects. For example, to open the complete PJLIB workspace, open + * pjlib.dsw in $PJPROJECT/pjlib/build directory. + * + * @subsection build_sys_install_unix_sec Make System + * + * For other targets, PJLIB provides a rather comprehensive build system + * that uses GNU \a make (and only GNU \a make will work). + * Currently, the build system supports building * PJLIB for these targets: + * - i386/Win32/mingw + * - i386/Linux + * - i386/Linux (kernel) + * + * Generally, steps required to build the projects are: + * + \verbatim + $ cd /home/user/pjproject-0.3 # <-- go to $PJPROJECT\n + $ vi build.mak # <-- set build target etc\n + $ cd pjlib/build # <-- go to projet's build dir\n + $ make # <-- build the project\n + \endverbatim + * + * For other project, \a cd to build directory in the project + * and execute \a make from there. + * + * \note For Linux kernel target, there are additional steps required, which + * will be explained in section \ref linux_kern_target_subsec. + * + * @subsubsection build_mak_sec Editing build.mak + * + * The \c build.mak file in \c $PJPROJECT root directory is used to + * specify the build configuration. This file is expected to export + * the following \a make variables: + * + * - \c MACHINE_NAME + *\n + * Target machine/processor, one of: { i386 }. + * + * - \c OS_NAME + *\n + * Target operating system, one of: { win32 | linux | linux-kernel }. + * + * - \c CC_NAME + *\n + * Compiler name: { gcc | vc } + * + * - \c HOST_NAME + *\n + * Build host: { unix | mingw } + * + * These variables will cause the correct configuration file in + * \c $PJPROJECT/build directory to be executed by \a make. For + * example, specifying \c OS_NAME=linux will cause file \c os-linux.mak + * in \c build directory to be executed. These files contain specific + * configuration for the option that is selected. + * + * For Linux kernel target, you are also required to declare the following + * variables in this file: + * - \c KERNEL_DIR: full path of kernel source tree. + * - \c KERNEL_ARCH: kernel ARCH options (e.g. "ARCH=um"), or leave blank + * for default. + * - \c PJPROJECT_DIR: full path of PJPROJECT source tree. + * + * Apart from these, there are also additional steps required to build + * Linux kernel target, which will be explained in \ref linux_kern_target_subsec. + * + * @subsubsection build_dir_sec Files in "build" Directory + * + * The *.mak files in \c $PJPROJECT/build directory are used to specify + * the configuration for the specified compiler, target machine target + * operating system, and host options. These files will be executed + * (included) by \a make during building process, depending on the values + * specified in $PJPROJECT/build.mak file. + * + * Normally you don't need to edit these files, except when you're porting + * PJLIB to new target. + * + * Below are the description of some files in this directory: + * + * - rules.mak: contains generic rules always included during make. + * - cc-gcc.mak: rules when gcc is used for compiler. + * - cc-vc.mak: rules when MSVC compiler is used. + * - host-mingw.mak: rules for building in mingw host. + * - host-unix.mak: rules for building in Unix/Posix host. + * - host-win32.mak: rules for building in Win32 command console + * (only valid when VC is used). + * - m-i386.mak: rules when target machine is an i386 processor. + * - m-m68k.mak: rules when target machine is an m68k processor. + * - os-linux.mak: rules when target OS is Linux. + * - os-linux-kernel.mak: rules when PJLIB is to be build as + * part of Linux kernel. + * - os-win32.mak: rules when target OS is Win32. + * + * @subsubsection invoking_make_sec Invoking make + * + * Normally, \a make is invoked in \c build directory under each project. + * For example, to build PJLIB, you would invoke \a make in + * \c $PJPROJECT/pjlib/build directory like below: + * + \verbatim + $ cd pjlib/build + $ make + \endverbatim + * + * + * + * @subsubsection linux_kern_target_subsec Linux Kernel Target + * + * \note + * BUILDING APPLICATIONS IN LINUX KERNEL MODE IS A VERY DANGEROUS BUSINESS. + * YOU MAY CRASH THE WHOLE OF YOUR SYSTEM, CORRUPT YOUR HARDISK, ETC. PJLIB + * KERNEL MODULES ARE STILL IN EXPERIMENTAL PHASE. DO NOT RUN IT IN PRODUCTION + * SYSTEMS OR OTHER SYSTEMS WHERE RISK OF LOSS OF DATA IS NOT ACCEPTABLE. + * YOU HAVE BEEN WARNED. + * + * \note + * User Mode Linux (UML) provides excellent way to experiment with Linux + * kernel without risking the stability of the host system. See + * http://user-mode-linux.sourceforge.net for details. + * + * \note + * I only use UML to experiment with PJLIB kernel modules. + * I wouldn't be so foolish to use my host Linux machine to experiment + * with this. + * + * \note + * You have been warned. + * + * For building PJLIB for Linux kernel target, there are additional steps required. + * In general, the additional tasks are: + * - Declare some more variables in build.mak file (this + * has been explained in \ref build_mak_sec above). + * - Perform these two small modifications in kernel source tree. + * + * There are two small modification need to be applied to the kernel tree. + * + * 1. Edit Makefile in kernel root source tree. + * + * Add the following lines at the end of the Makefile in your + * $KERNEL_SRC dir: + \verbatim +script: + $(SCRIPT) + \endverbatim + * + * \note Remember to replace spaces with tab in the Makefile. + * + * The modification above is needed to capture kernel's \c $CFLAGS and + * \c $CFLAGS_MODULE which will be used for PJLIB's compilation. + * + * 2. Add Additional Exports. + * + * We need the kernel to export some more symbols for our use. So we declare + * the additional symbols to be exported in extra-exports.c file, and add + * a this file to be compiled into the kernel: + * + * - Copy the file extra-exports.c from pjlib/src/pj + * directory to $KERNEL_SRC/kernel/ directory. + * - Edit Makefile in that directory, and add this line + * somewhere after the declaration of that variable: + \verbatim +obj-y += extra-exports.o + \endverbatim + * + * To illustrate what have been done in your kernel source tree, below + * is screenshot of my kernel source tree _after_ the modification. + * + \verbatim +[root@vpc-linux linux-2.6.7]# pwd +/usr/src/linux-2.6.7 +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# tail Makefile + +endif # skip-makefile + +FORCE: + +.PHONY: script + +script: + $(SCRIPT) + +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# head kernel/extra-exports.c +#include +#include + +EXPORT_SYMBOL(sys_select); + +EXPORT_SYMBOL(sys_epoll_create); +EXPORT_SYMBOL(sys_epoll_ctl); +EXPORT_SYMBOL(sys_epoll_wait); + +EXPORT_SYMBOL(sys_socket); +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# +[root@vpc-linux linux-2.6.7]# head -15 kernel/Makefile +# +# Makefile for the linux kernel. +# + +obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ + exit.o itimer.o time.o softirq.o resource.o \ + sysctl.o capability.o ptrace.o timer.o user.o \ + signal.o sys.o kmod.o workqueue.o pid.o \ + rcupdate.o intermodule.o extable.o params.o posix-timers.o \ + kthread.o + +obj-y += extra-exports.o + +obj-$(CONFIG_FUTEX) += futex.o +obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o +[root@vpc-linux linux-2.6.7]# + + \endverbatim + * + * Then you must rebuild the kernel. + * If you fail to do this, you won't be able to insmod pjlib. + * + * \note You will see a lots of warning messages during pjlib-test compilation. + * The warning messages complain about unresolved symbols which are defined + * in pjlib module. You can safely ignore these warnings. However, you can not + * ignore warnings about non-pjlib unresolved symbols. + * + * + * @subsection makefile_explained_sec Makefile Explained + * + * The \a Makefile for each project (e.g. PJLIB, PJSIP, etc) should be + * very similar in the contents. The Makefile is located under \c build + * directory in each project subdir. + * + * @subsubsection pjlib_makefile_subsec PJLIB Makefile. + * + * Below is PJLIB's Makefile: + * + * \include build/Makefile + * + * @subsubsection pjlib_os_makefile_subsec PJLIB os-linux.mak. + * + * Below is file os-linux.mak file in + * $PJPROJECT/pjlib/build directory, + * which is OS specific configuration file for Linux target that is specific + * for PJLIB project. For \b global OS specific configuration, please see + * $PJPROJECT/build/os-*.mak. + * + * \include build/os-linux.mak + * + */ + + +/*////////////////////////////////////////////////////////////////////////// */ +/* + PORTING PJLIB + */ + + + +/** + * @page porting_pjlib_pg Porting PJLIB + * + * + * @section new_arch_sec Porting to New CPU Architecture + * + * Below is step-by-step guide to add support for new CPU architecture. + * This sample is based on porting to Alpha architecture; however steps for + * porting to other CPU architectures should be pretty similar. + * + * Also note that in this example, the operating system used is Linux. + * Should you wish to add support for new operating system, then follow + * the next section \ref porting_os_sec. + * + * Step-by-step guide to port to new CPU architecture: + * - decide the name for the new architecture. In this case, we choose + * alpha. + * - edit file $PJPROJECT/build.mak, and add new section for + * the new target: + *
+ *      #
+ *      # Linux alpha, gcc
+ *      #
+ *      export MACHINE_NAME := alpha
+ *      export OS_NAME := linux
+ *      export CC_NAME := gcc
+ *      export HOST_NAME := unix
+ *    
+ * + * - create a new file $PJPROJECT/build/m-alpha.mak. + * Alternatively create a copy from other file in this directory. + * The contents of this file will look something like: + *
+ *      export M_CFLAGS := $(CC_DEF)PJ_M_ALPHA=1
+ *      export M_CXXFLAGS :=
+ *      export M_LDFLAGS :=
+ *      export M_SOURCES :=
+ *    
+ * - create a new file $PJPROJECT/pjlib/include/pj/compat/m_alpha.h. + * Alternatively create a copy from other header file in this directory. + * The contents of this file will look something like: + *
+ *      #define PJ_HAS_PENTIUM          0
+ *      #define PJ_IS_LITTLE_ENDIAN     1
+ *      #define PJ_IS_BIG_ENDIAN        0
+ *    
+ * - edit pjlib/include/pj/config.h. Add new processor + * configuration in this header file, like follows: + *
+ *      ...
+ *      #elif defined (PJ_M_ALPHA) && PJ_M_ALPHA != 0
+ *      #   include 
+ *      ...
+ *    
+ * - done. Build PJLIB with: + *
+ *      $ cd $PJPROJECT/pjlib/build
+ *      $ make dep
+ *      $ make clean
+ *      $ make
+ *    
+ * + * @section porting_os_sec Porting to New Operating System Target + * + * This section will try to give you rough guideline on how to + * port PJLIB to a new target. As a sample, we give the target a name tag, + * for example xos (for X OS). + * + * @subsection new_compat_os_h_file_sec Create New Compat Header File + * + * You'll need to create a new header file + * include/pj/compat/os_xos.h. You can copy as a + * template other header file and edit it accordingly. + * + * @subsection modify_config_h_file_sec Modify config.h + * + * Then modify file include/pj/config.h to include + * this file accordingly (e.g. when macro PJ_XOS is + * defined): + * + \verbatim + ... + #elif defined(PJ_XOS) + # include + #else + #... + \endverbatim + * + * @subsection new_target_mak_file_sec Create New Global Make Config File + * + * Then you'll need to create global configuration file that + * is specific for this OS, i.e. os-xos.mak in + * $PJPROJECT/build directory. + * + * At very minimum, the file will normally need to define + * PJ_XOS=1 in the \c CFLAGS section: + * + \verbatim +# +# $PJPROJECT/build/os-xos.mak: +# +export OS_CFLAGS := $(CC_DEF)PJ_XOS=1 +export OS_CXXFLAGS := +export OS_LDFLAGS := +export OS_SOURCES := + \endverbatim + * + * + * @subsection new_target_prj_mak_file_sec Create New Project's Make Config File + * + * Then you'll need to create xos-specific configuration file + * for PJLIB. This file is also named os-xos.mak, + * but its located in pjlib/build directory. + * This file will specify source files that are specific to + * this OS to be included in the build process. + * + * Below is a sample: + \verbatim +# +# pjlib/build/os-xos.mak: +# XOS specific configuration for PJLIB. +# +export PJLIB_OBJS += os_core_xos.o \ + os_error_unix.o \ + os_time_ansi.o +export TEST_OBJS += main.o +export TARGETS = pjlib pjlib-test + \endverbatim + * + * @subsection new_target_src_sec Create and Edit Source Files + * + * You'll normally need to create at least these files: + * - os_core_xos.c: core OS specific + * functionality. + * - os_timestamp_xos.c: how to get timestamp + * in this OS. + * + * Depending on how things are done in your OS, you may need + * to create these files: + * - os_error_*.c: how to manipulate + * OS error codes. Alternatively you may use existing + * os_error_unix.c if the OS has \c errno and + * \c strerror() function. + * - ioqueue_*.c: if the OS has specific method + * to perform asynchronous I/O. Alternatively you may + * use existing ioqueue_select.c if the OS supports + * \c select() function call. + * - sock_*.c: if the OS has specific method + * to perform socket communication. Alternatively you may + * use existing sock_bsd.c if the OS supports + * BSD socket API, and edit include/pj/compat/socket.h + * file accordingly. + * + * You will also need to check various files in + * include/pj/compat/*.h, to see if they're + * compatible with your OS. + * + * @subsection new_target_build_file_sec Build The Project + * + * After basic building blocks have been created for the OS, then + * the easiest way to see which parts need to be fixed is by building + * the project and see the error messages. + * + * @subsection new_target_edit_vs_new_file_sec Editing Existing Files vs Creating New File + * + * When you encounter compatibility errors in PJLIB during porting, + * you have three options on how to fix the error: + * - edit the existing *.c file, and give it #ifdef + * switch for the new OS, or + * - edit include/pj/compat/*.h instead, or + * - create a totally new file. + * + * Basicly there is no strict rule on which approach is the best + * to use, however the following guidelines may be used: + * - if the file is expected to be completely different than + * any existing file, then perhaps you should create a completely + * new file. For example, file os_core_xxx.c will + * normally be different for each OS flavour. + * - if the difference can be localized in include/compat + * header file, and existing #ifdef switch is there, + * then preferably you should edit this include/compat + * header file. + * - if the existing *.c file has #ifdef switch, + * then you may add another #elif switch there. This + * normally is used for behaviors that are not totally + * different on each platform. + * - other than that above, use your own judgement on whether + * to edit the file or create new file etc. + */ + +#endif /* __PJ_DOXYGEN_H__ */ + diff --git a/pjlib/include/pj/equeue.h b/pjlib/include/pj/equeue.h index 294304d00..1accad541 100644 --- a/pjlib/include/pj/equeue.h +++ b/pjlib/include/pj/equeue.h @@ -1,319 +1,319 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/equeue.h 2 10/14/05 12:26a Bennylp $ */ -#ifndef __PJ_EQUEUE_H__ -#define __PJ_EQUEUE_H__ - -/** - * @file equeue.h - * @brief Event Queue - */ -#include - - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_EQUEUE Event Queue - * @brief Event Queue - * @ingroup PJ_OS - * @{ - */ - - -/** - * Opaque data type for Event Queue. - */ -typedef struct pj_equeue_t pj_equeue_t; - -/** - * Opaque data type for Event Queue key. - */ -typedef struct pj_equeue_key_t pj_equeue_key_t; - - -/** - * This structure describes the callbacks to be called when I/O operation - * completes. - */ -typedef struct pj_io_callback -{ - /** - * This callback is called when #pj_equeue_read, #pj_equeue_recv or - * #pj_equeue_recvfrom completes. - * - * @param key The key. - * @param bytes_read The size of data that has just been read. - */ - void (*on_read_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_read); - - /** - * This callback is called when #pj_equeue_write, #pj_equeue_send, or - * #pj_equeue_sendto completes. - * - * @param key The key. - * @param bytes_read The size of data that has just been written. - */ - void (*on_write_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_sent); - - /** - * This callback is called when #pj_equeue_accept completes. - * - * @param key The key. - * @param status Zero if the operation completes successfully. - */ - void (*on_accept_complete)(pj_equeue_key_t *key, int status); - - /** - * This callback is called when #pj_equeue_connect completes. - * - * @param key The key. - * @param status Zero if the operation completes successfully. - */ - void (*on_connect_complete)(pj_equeue_key_t *key, int status); - -} pj_io_callback; - -/** - * Event Queue options. - */ -typedef struct pj_equeue_options -{ - /** Maximum number of threads that are allowed to access Event Queue - * simulteneously. - */ - unsigned nb_threads; - - /** If non-zero, then no mutex protection will be used. */ - pj_bool_t no_lock; - - /** Interval of the busy loop inside the event queue. - * The time resolution here determines the accuracy of the - * timer in the Event Queue. - */ - pj_time_val poll_interval; - -} pj_equeue_options; - - -/** - * Error value returned by I/O operations to indicate that the operation - * can't complete immediately and will complete later. - */ -#define PJ_EQUEUE_PENDING (-2) - -/** - * Types of Event Queue operation. - */ -typedef enum pj_equeue_op -{ - PJ_EQUEUE_OP_NONE = 0, /**< No operation. */ - PJ_EQUEUE_OP_READ = 1, /**< read() operation. */ - PJ_EQUEUE_OP_RECV_FROM = 2, /**< recvfrom() operation. */ - PJ_EQUEUE_OP_WRITE = 4, /**< write() operation. */ - PJ_EQUEUE_OP_SEND_TO = 8, /**< sendto() operation. */ -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 - PJ_EQUEUE_OP_ACCEPT = 16, /**< accept() operation. */ - PJ_EQUEUE_OP_CONNECT = 32, /**< connect() operation. */ -#endif /* PJ_HAS_TCP */ -} pj_equeue_op; - - - -/** - * Initialize Event Queue options with default values. - * - * @param options Event Queue options. - */ -PJ_DECL(void) pj_equeue_options_init(pj_equeue_options *options); - -/** - * Create a new Event Queue framework. - * - * @param pool The pool to allocate the event queue structure. - * @param options Event queue options, or if NULL is given, then - * default options will be used. - * @param equeue Pointer to receive event queue structure. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pj_equeue_create( pj_pool_t *pool, - const pj_equeue_options *options, - pj_equeue_t **equeue); - -/** - * Get the first instance of Event Queue, or NULL if no Event Queue - * instance has been created in the application. - * - * @return The first instance of Event Queue created, or NULL. - */ -PJ_DECL(pj_equeue_t*) pj_equeue_instance(void); - -/** - * Destroy the Event Queue. - * - * @param equeue The Event Queue instance to be destroyed. - */ -PJ_DECL(pj_status_t) pj_equeue_destroy( pj_equeue_t *equeue ); - -/** - * Customize the lock object that is used by the Event Queue. - * - * @param equeue The Event Queue instance. - * @param lock The lock object. - * @param auto_del If non-zero, the lock will be destroyed by - * Event Queue. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_equeue_set_lock( pj_equeue_t *equeue, - pj_lock_t *lock, - pj_bool_t auto_del); - -/** - * Associate an Event Queue key to particular handle. The key is also - * associated with the callback and user data, which will be used by - * the Event Queue framework when signalling event back to application. - * - * @param pool To allocate the resource for the specified handle, which - * must be valid until the handle/key is unregistered - * from Event Queue. - * @param equeue The Event Queue. - * @param hnd The OS handle to be registered, which can be a socket - * descriptor (pj_sock_t), file descriptor, etc. - * @param cb Callback to be called when I/O operation completes. - * @param user_data User data to be associated with the key. - * @param key Pointer to receive the key. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_equeue_register( pj_pool_t *pool, - pj_equeue_t *equeue, - pj_oshandle_t hnd, - pj_io_callback *cb, - void *user_data, - pj_equeue_key_t **key); - -/** - * Retrieve user data associated with a key. - * - * @param key The Event Queue key. - * - * @return User data associated with the key. - */ -PJ_DECL(void*) pj_equeue_get_user_data( pj_equeue_key_t *key ); - - -/** - * Unregister Event Queue key from the Event Queue. - * - * @param equeue The Event Queue. - * @param key The key. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_equeue_unregister( pj_equeue_t *equeue, - pj_equeue_key_t *key); - -/** - * Instruct the Event Queue to read from the specified handle. This function - * returns immediately (i.e. non-blocking) regardless whether some data has - * been transfered. If the operation can't complete immediately, caller will - * be notified about the completion when it calls pj_equeue_poll(). - * - * @param key The key that uniquely identifies the handle. - * @param buffer The buffer to hold the read data. The caller MUST make sure - * that this buffer remain valid until the framework completes - * reading the handle. - * @param size The maximum size to be read. - * - * @return - * - zero or positive number to indicate the number of bytes has been - * read, and in this case the operation was not queued. - * - (-1) on error, which in this case operation was not queued. - * - PJ_EQUEUE_PENDING if the operation has been queued. - */ -PJ_DECL(pj_ssize_t) pj_equeue_read( pj_equeue_key_t *key, - void *buffer, - pj_size_t size); - -/** - * Start recv() operation on the specified handle. - * - * @see ::pj_ioqueue_read - */ -PJ_DECL(pj_ssize_t) pj_equeue_recv( pj_equeue_key_t *key, - void *buf, - pj_size_t size, - unsigned flags); - -/** - * Start recvfrom() operation on the specified handle. - * - * @see ::pj_equeue_read - */ -PJ_DECL(pj_ssize_t) pj_equeue_recvfrom( pj_equeue_key_t *key, - void *buf, - pj_size_t size, - unsigned flags, - pj_sockaddr_t *addr, - int *addrlen ); - -/** - * Write. - */ -PJ_DECL(pj_ssize_t) pj_equeue_write( pj_equeue_key_t *key, - const void *buf, - pj_size_t size); - -/** - * Send. - */ -PJ_DECL(pj_ssize_t) pj_equeue_send( pj_equeue_key_t *key, - const void *buf, - pj_size_t size, - unsigned flags); - -/** - * Sendto. - */ -PJ_DECL(pj_ssize_t) pj_equeue_sendto( pj_equeue_key_t *key, - const void *buf, - pj_size_t size, - unsigned flags, - const pj_sockaddr_t *addr, - int addrlen); - -/** - * Schedule timer. - */ -PJ_DECL(pj_status_t) pj_equeue_schedule_timer( pj_equeue_t *equeue, - const pj_time_val *timeout, - pj_timer_entry *entry); - -/** - * Cancel timer. - */ -PJ_DECL(pj_status_t) pj_equeue_cancel_timer( pj_equeue_t *equeue, - pj_timer_entry *entry); - -/** - * Poll for events. - */ -PJ_DECL(pj_status_t) pj_equeue_poll( pj_equeue_t *equeue, - const pj_time_val *timeout ); - -/** - * Run. - */ -PJ_DECL(pj_status_t) pj_equeue_run( pj_equeue_t *equeue ); - -/** - * Stop all running threads. - */ -PJ_DECL(pj_status_t) pj_equeue_stop( pj_equeue_t *equeue ); - - -/** @} */ - -PJ_END_DECL - -#endif /* __PJ_EQUEUE_H__ */ +/* $Header: /pjproject-0.3/pjlib/include/pj/equeue.h 2 10/14/05 12:26a Bennylp $ */ +#ifndef __PJ_EQUEUE_H__ +#define __PJ_EQUEUE_H__ + +/** + * @file equeue.h + * @brief Event Queue + */ +#include + + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_EQUEUE Event Queue + * @brief Event Queue + * @ingroup PJ_OS + * @{ + */ + + +/** + * Opaque data type for Event Queue. + */ +typedef struct pj_equeue_t pj_equeue_t; + +/** + * Opaque data type for Event Queue key. + */ +typedef struct pj_equeue_key_t pj_equeue_key_t; + + +/** + * This structure describes the callbacks to be called when I/O operation + * completes. + */ +typedef struct pj_io_callback +{ + /** + * This callback is called when #pj_equeue_read, #pj_equeue_recv or + * #pj_equeue_recvfrom completes. + * + * @param key The key. + * @param bytes_read The size of data that has just been read. + */ + void (*on_read_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_read); + + /** + * This callback is called when #pj_equeue_write, #pj_equeue_send, or + * #pj_equeue_sendto completes. + * + * @param key The key. + * @param bytes_read The size of data that has just been written. + */ + void (*on_write_complete)(pj_equeue_key_t *key, pj_ssize_t bytes_sent); + + /** + * This callback is called when #pj_equeue_accept completes. + * + * @param key The key. + * @param status Zero if the operation completes successfully. + */ + void (*on_accept_complete)(pj_equeue_key_t *key, int status); + + /** + * This callback is called when #pj_equeue_connect completes. + * + * @param key The key. + * @param status Zero if the operation completes successfully. + */ + void (*on_connect_complete)(pj_equeue_key_t *key, int status); + +} pj_io_callback; + +/** + * Event Queue options. + */ +typedef struct pj_equeue_options +{ + /** Maximum number of threads that are allowed to access Event Queue + * simulteneously. + */ + unsigned nb_threads; + + /** If non-zero, then no mutex protection will be used. */ + pj_bool_t no_lock; + + /** Interval of the busy loop inside the event queue. + * The time resolution here determines the accuracy of the + * timer in the Event Queue. + */ + pj_time_val poll_interval; + +} pj_equeue_options; + + +/** + * Error value returned by I/O operations to indicate that the operation + * can't complete immediately and will complete later. + */ +#define PJ_EQUEUE_PENDING (-2) + +/** + * Types of Event Queue operation. + */ +typedef enum pj_equeue_op +{ + PJ_EQUEUE_OP_NONE = 0, /**< No operation. */ + PJ_EQUEUE_OP_READ = 1, /**< read() operation. */ + PJ_EQUEUE_OP_RECV_FROM = 2, /**< recvfrom() operation. */ + PJ_EQUEUE_OP_WRITE = 4, /**< write() operation. */ + PJ_EQUEUE_OP_SEND_TO = 8, /**< sendto() operation. */ +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + PJ_EQUEUE_OP_ACCEPT = 16, /**< accept() operation. */ + PJ_EQUEUE_OP_CONNECT = 32, /**< connect() operation. */ +#endif /* PJ_HAS_TCP */ +} pj_equeue_op; + + + +/** + * Initialize Event Queue options with default values. + * + * @param options Event Queue options. + */ +PJ_DECL(void) pj_equeue_options_init(pj_equeue_options *options); + +/** + * Create a new Event Queue framework. + * + * @param pool The pool to allocate the event queue structure. + * @param options Event queue options, or if NULL is given, then + * default options will be used. + * @param equeue Pointer to receive event queue structure. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_create( pj_pool_t *pool, + const pj_equeue_options *options, + pj_equeue_t **equeue); + +/** + * Get the first instance of Event Queue, or NULL if no Event Queue + * instance has been created in the application. + * + * @return The first instance of Event Queue created, or NULL. + */ +PJ_DECL(pj_equeue_t*) pj_equeue_instance(void); + +/** + * Destroy the Event Queue. + * + * @param equeue The Event Queue instance to be destroyed. + */ +PJ_DECL(pj_status_t) pj_equeue_destroy( pj_equeue_t *equeue ); + +/** + * Customize the lock object that is used by the Event Queue. + * + * @param equeue The Event Queue instance. + * @param lock The lock object. + * @param auto_del If non-zero, the lock will be destroyed by + * Event Queue. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_set_lock( pj_equeue_t *equeue, + pj_lock_t *lock, + pj_bool_t auto_del); + +/** + * Associate an Event Queue key to particular handle. The key is also + * associated with the callback and user data, which will be used by + * the Event Queue framework when signalling event back to application. + * + * @param pool To allocate the resource for the specified handle, which + * must be valid until the handle/key is unregistered + * from Event Queue. + * @param equeue The Event Queue. + * @param hnd The OS handle to be registered, which can be a socket + * descriptor (pj_sock_t), file descriptor, etc. + * @param cb Callback to be called when I/O operation completes. + * @param user_data User data to be associated with the key. + * @param key Pointer to receive the key. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_register( pj_pool_t *pool, + pj_equeue_t *equeue, + pj_oshandle_t hnd, + pj_io_callback *cb, + void *user_data, + pj_equeue_key_t **key); + +/** + * Retrieve user data associated with a key. + * + * @param key The Event Queue key. + * + * @return User data associated with the key. + */ +PJ_DECL(void*) pj_equeue_get_user_data( pj_equeue_key_t *key ); + + +/** + * Unregister Event Queue key from the Event Queue. + * + * @param equeue The Event Queue. + * @param key The key. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_equeue_unregister( pj_equeue_t *equeue, + pj_equeue_key_t *key); + +/** + * Instruct the Event Queue to read from the specified handle. This function + * returns immediately (i.e. non-blocking) regardless whether some data has + * been transfered. If the operation can't complete immediately, caller will + * be notified about the completion when it calls pj_equeue_poll(). + * + * @param key The key that uniquely identifies the handle. + * @param buffer The buffer to hold the read data. The caller MUST make sure + * that this buffer remain valid until the framework completes + * reading the handle. + * @param size The maximum size to be read. + * + * @return + * - zero or positive number to indicate the number of bytes has been + * read, and in this case the operation was not queued. + * - (-1) on error, which in this case operation was not queued. + * - PJ_EQUEUE_PENDING if the operation has been queued. + */ +PJ_DECL(pj_ssize_t) pj_equeue_read( pj_equeue_key_t *key, + void *buffer, + pj_size_t size); + +/** + * Start recv() operation on the specified handle. + * + * @see ::pj_ioqueue_read + */ +PJ_DECL(pj_ssize_t) pj_equeue_recv( pj_equeue_key_t *key, + void *buf, + pj_size_t size, + unsigned flags); + +/** + * Start recvfrom() operation on the specified handle. + * + * @see ::pj_equeue_read + */ +PJ_DECL(pj_ssize_t) pj_equeue_recvfrom( pj_equeue_key_t *key, + void *buf, + pj_size_t size, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen ); + +/** + * Write. + */ +PJ_DECL(pj_ssize_t) pj_equeue_write( pj_equeue_key_t *key, + const void *buf, + pj_size_t size); + +/** + * Send. + */ +PJ_DECL(pj_ssize_t) pj_equeue_send( pj_equeue_key_t *key, + const void *buf, + pj_size_t size, + unsigned flags); + +/** + * Sendto. + */ +PJ_DECL(pj_ssize_t) pj_equeue_sendto( pj_equeue_key_t *key, + const void *buf, + pj_size_t size, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen); + +/** + * Schedule timer. + */ +PJ_DECL(pj_status_t) pj_equeue_schedule_timer( pj_equeue_t *equeue, + const pj_time_val *timeout, + pj_timer_entry *entry); + +/** + * Cancel timer. + */ +PJ_DECL(pj_status_t) pj_equeue_cancel_timer( pj_equeue_t *equeue, + pj_timer_entry *entry); + +/** + * Poll for events. + */ +PJ_DECL(pj_status_t) pj_equeue_poll( pj_equeue_t *equeue, + const pj_time_val *timeout ); + +/** + * Run. + */ +PJ_DECL(pj_status_t) pj_equeue_run( pj_equeue_t *equeue ); + +/** + * Stop all running threads. + */ +PJ_DECL(pj_status_t) pj_equeue_stop( pj_equeue_t *equeue ); + + +/** @} */ + +PJ_END_DECL + +#endif /* __PJ_EQUEUE_H__ */ diff --git a/pjlib/include/pj/errno.h b/pjlib/include/pj/errno.h index 9cc9328d2..fee645f6b 100644 --- a/pjlib/include/pj/errno.h +++ b/pjlib/include/pj/errno.h @@ -1,249 +1,249 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/errno.h 2 10/14/05 12:26a Bennylp $ */ -#ifndef __PJ_ERRNO_H__ -#define __PJ_ERRNO_H__ - -/** - * @file errno.h - * @brief PJLIB Error Codes - */ -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup pj_errno Error Codes - * @ingroup PJ - * @{ - * - * In PJLIB, error/status codes from operating system are translated - * into PJLIB error namespace, and stored in @a pj_status_t. All functions - * that work with @a pj_status_t expect to get PJLIB error code instead - * of native codes. - * - * @section pj_errno_retval Return Values - * - * All functions that returns @a pj_status_t returns @a PJ_SUCCESS if the - * operation was completed successfully, or non-zero value to indicate - * error. If the error came from operating system, then the native error - * code is translated/folded into PJLIB's error namespace by using - * #PJ_STATUS_FROM_OS() macro. The function will do this automatically - * before returning the error to caller. - * - * @section pj_errno_errmsg Error Message - * - * To get the error message corresponding to a particular code, use function - * #pj_strerror(). This function expects error code in PJLIB error namespace, - * not the native error code. Application can pass the value from the - * following sources to this function: - * - #pj_get_os_error() - * - #pj_get_netos_error() - * - any return value from function returning @a pj_status_t. - * - * Application MUST NOT pass native error code (such as error code from - * functions like GetLastError() or errno) to PJLIB functions expecting - * @a pj_status_t. - * - */ - -/** - * Get the last platform error/status, folded into pj_status_t. - * @return OS dependent error code, folded into pj_status_t. - * @remark This function gets errno, or calls GetLastError() function and - * convert the code into pj_status_t with PJ_STATUS_FROM_OS. Do - * not call this for socket functions! - * @see pj_get_netos_error() - */ -PJ_DECL(pj_status_t) pj_get_os_error(void); - -/** - * Set last error. - * @param code pj_status_t - */ -PJ_DECL(void) pj_set_os_error(pj_status_t code); - -/** - * Get the last error from socket operations. - * @return Last socket error, folded into pj_status_t. - */ -PJ_DECL(pj_status_t) pj_get_netos_error(void); - -/** - * Set error code. - * @param code pj_status_t. - */ -PJ_DECL(void) pj_set_netos_error(pj_status_t code); - - -/** - * Get the error message for the specified error code. The message - * string will be NULL terminated. - * - * @param statcode The error code. - * @param buf Buffer to hold the error message string. - * @param bufsize Size of the buffer. - * - * @return The error message as NULL terminated string, - * wrapped with pj_str_t. - */ -PJ_DECL(pj_str_t) pj_strerror( pj_status_t statcode, - char *buf, pj_size_t bufsize); - - -/** - * @hideinitializer - * Return platform os error code folded into pj_status_t code. This is - * the macro that is used throughout the library for all PJLIB's functions - * that returns error from operating system. Application may override - * this macro to reduce size (e.g. by defining it to always return - * #PJ_EUNKNOWN). - * - * Note: - * This macro MUST return non-zero value regardless whether zero is - * passed as the argument. The reason is to protect logic error when - * the operating system doesn't report error codes properly. - * - * @param os_code Platform OS error code. This value may be evaluated - * more than once. - * @return The platform os error code folded into pj_status_t. - */ -#ifndef PJ_RETURN_OS_ERROR -# define PJ_RETURN_OS_ERROR(os_code) (os_code ? \ - PJ_STATUS_FROM_OS(os_code) : -1) -#endif - - -/** - * @hideinitializer - * Fold a platform specific error into an pj_status_t code. - * - * @param e The platform os error code. - * @return pj_status_t - * @warning Macro implementation; the syserr argument may be evaluated - * multiple times. - */ -#define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS) - -/** - * @hideinitializer - * Fold an pj_status_t code back to the native platform defined error. - * - * @param e The pj_status_t folded platform os error code. - * @return pj_os_err_type - * @warning macro implementation; the statcode argument may be evaluated - * multiple times. If the statcode was not created by - * pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined. - */ -#define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS) - - -/** - * @defgroup pj_errnum PJLIB's Own Error Codes - * @ingroup pj_errno - * @{ - */ - -/** - * @hideinitializer - * Unknown error has been reported. - */ -#define PJ_EUNKNOWN (PJ_ERRNO_START_STATUS + 1) -/** - * @hideinitializer - * The operation is pending and will be completed later. - */ -#define PJ_EPENDING (PJ_ERRNO_START_STATUS + 2) -/** - * @hideinitializer - * Too many connecting sockets. - */ -#define PJ_ETOOMANYCONN (PJ_ERRNO_START_STATUS + 3) -/** - * @hideinitializer - * Invalid argument. - */ -#define PJ_EINVAL (PJ_ERRNO_START_STATUS + 4) -/** - * @hideinitializer - * Name too long (eg. hostname too long). - */ -#define PJ_ENAMETOOLONG (PJ_ERRNO_START_STATUS + 5) -/** - * @hideinitializer - * Not found. - */ -#define PJ_ENOTFOUND (PJ_ERRNO_START_STATUS + 6) -/** - * @hideinitializer - * Not enough memory. - */ -#define PJ_ENOMEM (PJ_ERRNO_START_STATUS + 7) -/** - * @hideinitializer - * Bug detected! - */ -#define PJ_EBUG (PJ_ERRNO_START_STATUS + 8) -/** - * @hideinitializer - * Operation timed out. - */ -#define PJ_ETIMEDOUT (PJ_ERRNO_START_STATUS + 9) -/** - * @hideinitializer - * Too many objects. - */ -#define PJ_ETOOMANY (PJ_ERRNO_START_STATUS + 10) -/** - * @hideinitializer - * Object is busy. - */ -#define PJ_EBUSY (PJ_ERRNO_START_STATUS + 11) -/** - * @hideinitializer - * The specified option is not supported. - */ -#define PJ_ENOTSUP (PJ_ERRNO_START_STATUS + 12) -/** - * @hideinitializer - * Invalid operation. - */ -#define PJ_EINVALIDOP (PJ_ERRNO_START_STATUS + 13) - -/** @} */ /* pj_errnum */ - -/** @} */ /* pj_errno */ - - -/** - * PJ_ERRNO_START is where PJLIB specific error values start. - */ -#define PJ_ERRNO_START 20000 - -/** - * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of - * the error/status range below. - */ -#define PJ_ERRNO_SPACE_SIZE 50000 - -/** - * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start. - */ -#define PJ_ERRNO_START_STATUS (PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) - -/** - * PJ_ERRNO_START_SYS converts platform specific error codes into - * pj_status_t values. - */ -#define PJ_ERRNO_START_SYS (PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) - -/** - * PJ_ERRNO_START_USER are reserved for applications that use error - * codes along with PJLIB codes. - */ -#define PJ_ERRNO_START_USER (PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) - - -PJ_END_DECL - -#endif /* __PJ_ERRNO_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/errno.h 2 10/14/05 12:26a Bennylp $ */ +#ifndef __PJ_ERRNO_H__ +#define __PJ_ERRNO_H__ + +/** + * @file errno.h + * @brief PJLIB Error Codes + */ +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup pj_errno Error Codes + * @ingroup PJ + * @{ + * + * In PJLIB, error/status codes from operating system are translated + * into PJLIB error namespace, and stored in @a pj_status_t. All functions + * that work with @a pj_status_t expect to get PJLIB error code instead + * of native codes. + * + * @section pj_errno_retval Return Values + * + * All functions that returns @a pj_status_t returns @a PJ_SUCCESS if the + * operation was completed successfully, or non-zero value to indicate + * error. If the error came from operating system, then the native error + * code is translated/folded into PJLIB's error namespace by using + * #PJ_STATUS_FROM_OS() macro. The function will do this automatically + * before returning the error to caller. + * + * @section pj_errno_errmsg Error Message + * + * To get the error message corresponding to a particular code, use function + * #pj_strerror(). This function expects error code in PJLIB error namespace, + * not the native error code. Application can pass the value from the + * following sources to this function: + * - #pj_get_os_error() + * - #pj_get_netos_error() + * - any return value from function returning @a pj_status_t. + * + * Application MUST NOT pass native error code (such as error code from + * functions like GetLastError() or errno) to PJLIB functions expecting + * @a pj_status_t. + * + */ + +/** + * Get the last platform error/status, folded into pj_status_t. + * @return OS dependent error code, folded into pj_status_t. + * @remark This function gets errno, or calls GetLastError() function and + * convert the code into pj_status_t with PJ_STATUS_FROM_OS. Do + * not call this for socket functions! + * @see pj_get_netos_error() + */ +PJ_DECL(pj_status_t) pj_get_os_error(void); + +/** + * Set last error. + * @param code pj_status_t + */ +PJ_DECL(void) pj_set_os_error(pj_status_t code); + +/** + * Get the last error from socket operations. + * @return Last socket error, folded into pj_status_t. + */ +PJ_DECL(pj_status_t) pj_get_netos_error(void); + +/** + * Set error code. + * @param code pj_status_t. + */ +PJ_DECL(void) pj_set_netos_error(pj_status_t code); + + +/** + * Get the error message for the specified error code. The message + * string will be NULL terminated. + * + * @param statcode The error code. + * @param buf Buffer to hold the error message string. + * @param bufsize Size of the buffer. + * + * @return The error message as NULL terminated string, + * wrapped with pj_str_t. + */ +PJ_DECL(pj_str_t) pj_strerror( pj_status_t statcode, + char *buf, pj_size_t bufsize); + + +/** + * @hideinitializer + * Return platform os error code folded into pj_status_t code. This is + * the macro that is used throughout the library for all PJLIB's functions + * that returns error from operating system. Application may override + * this macro to reduce size (e.g. by defining it to always return + * #PJ_EUNKNOWN). + * + * Note: + * This macro MUST return non-zero value regardless whether zero is + * passed as the argument. The reason is to protect logic error when + * the operating system doesn't report error codes properly. + * + * @param os_code Platform OS error code. This value may be evaluated + * more than once. + * @return The platform os error code folded into pj_status_t. + */ +#ifndef PJ_RETURN_OS_ERROR +# define PJ_RETURN_OS_ERROR(os_code) (os_code ? \ + PJ_STATUS_FROM_OS(os_code) : -1) +#endif + + +/** + * @hideinitializer + * Fold a platform specific error into an pj_status_t code. + * + * @param e The platform os error code. + * @return pj_status_t + * @warning Macro implementation; the syserr argument may be evaluated + * multiple times. + */ +#define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS) + +/** + * @hideinitializer + * Fold an pj_status_t code back to the native platform defined error. + * + * @param e The pj_status_t folded platform os error code. + * @return pj_os_err_type + * @warning macro implementation; the statcode argument may be evaluated + * multiple times. If the statcode was not created by + * pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined. + */ +#define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS) + + +/** + * @defgroup pj_errnum PJLIB's Own Error Codes + * @ingroup pj_errno + * @{ + */ + +/** + * @hideinitializer + * Unknown error has been reported. + */ +#define PJ_EUNKNOWN (PJ_ERRNO_START_STATUS + 1) +/** + * @hideinitializer + * The operation is pending and will be completed later. + */ +#define PJ_EPENDING (PJ_ERRNO_START_STATUS + 2) +/** + * @hideinitializer + * Too many connecting sockets. + */ +#define PJ_ETOOMANYCONN (PJ_ERRNO_START_STATUS + 3) +/** + * @hideinitializer + * Invalid argument. + */ +#define PJ_EINVAL (PJ_ERRNO_START_STATUS + 4) +/** + * @hideinitializer + * Name too long (eg. hostname too long). + */ +#define PJ_ENAMETOOLONG (PJ_ERRNO_START_STATUS + 5) +/** + * @hideinitializer + * Not found. + */ +#define PJ_ENOTFOUND (PJ_ERRNO_START_STATUS + 6) +/** + * @hideinitializer + * Not enough memory. + */ +#define PJ_ENOMEM (PJ_ERRNO_START_STATUS + 7) +/** + * @hideinitializer + * Bug detected! + */ +#define PJ_EBUG (PJ_ERRNO_START_STATUS + 8) +/** + * @hideinitializer + * Operation timed out. + */ +#define PJ_ETIMEDOUT (PJ_ERRNO_START_STATUS + 9) +/** + * @hideinitializer + * Too many objects. + */ +#define PJ_ETOOMANY (PJ_ERRNO_START_STATUS + 10) +/** + * @hideinitializer + * Object is busy. + */ +#define PJ_EBUSY (PJ_ERRNO_START_STATUS + 11) +/** + * @hideinitializer + * The specified option is not supported. + */ +#define PJ_ENOTSUP (PJ_ERRNO_START_STATUS + 12) +/** + * @hideinitializer + * Invalid operation. + */ +#define PJ_EINVALIDOP (PJ_ERRNO_START_STATUS + 13) + +/** @} */ /* pj_errnum */ + +/** @} */ /* pj_errno */ + + +/** + * PJ_ERRNO_START is where PJLIB specific error values start. + */ +#define PJ_ERRNO_START 20000 + +/** + * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of + * the error/status range below. + */ +#define PJ_ERRNO_SPACE_SIZE 50000 + +/** + * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start. + */ +#define PJ_ERRNO_START_STATUS (PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) + +/** + * PJ_ERRNO_START_SYS converts platform specific error codes into + * pj_status_t values. + */ +#define PJ_ERRNO_START_SYS (PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) + +/** + * PJ_ERRNO_START_USER are reserved for applications that use error + * codes along with PJLIB codes. + */ +#define PJ_ERRNO_START_USER (PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) + + +PJ_END_DECL + +#endif /* __PJ_ERRNO_H__ */ + diff --git a/pjlib/include/pj/except.h b/pjlib/include/pj/except.h index b325b47c8..4a2505bb1 100644 --- a/pjlib/include/pj/except.h +++ b/pjlib/include/pj/except.h @@ -1,267 +1,267 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/except.h 9 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_EXCEPTION_H__ -#define __PJ_EXCEPTION_H__ - -/** - * @file except.h - * @brief Exception Handling in C. - */ - -#include -#include - - -PJ_BEGIN_DECL - - -/** - * @defgroup PJ_EXCEPT Exception Handling - * @ingroup PJ_MISC - * @{ - * - * \section pj_except_sample_sec Quick Example - * - * For the impatient, take a look at some examples: - * - @ref page_pjlib_samples_except_c - * - @ref page_pjlib_exception_test - * - * \section pj_except_except Exception Handling - * - * This module provides exception handling syntactically similar to C++ in - * C language. The underlying mechanism use setjmp() and longjmp(), and since - * these constructs are ANSI standard, the mechanism here should be available - * on most platforms/compilers which are ANSI compliant. - * - * If ANSI libc is not available, then setjmp()/longjmp() implementation will - * be provided. See for compatibility. - * - * The exception handling mechanism is completely thread safe, so the exception - * thrown by one thread will not interfere with other thread. - * - * CAVEATS: - * - unlike C++ exception, the scheme here won't call destructors of local - * objects if exception is thrown. Care must be taken when a function - * hold some resorce such as pool or mutex etc. - * - You CAN NOT make nested exception in one single function without using - * a nested PJ_USE_EXCEPTION. - * - Exceptions will always be caught by the first handle (unlike C++ where - * exception is only caught if the type matches. - * - * The exception handling constructs are similar to C++. The blocks will be - * constructed similar to the following sample: - * - * \verbatim - #define NO_MEMORY 1 - #define SYNTAX_ERROR 2 - - int main() - { - PJ_USE_EXCEPTION; // declare local exception stack. - - PJ_TRY { - ...// do something.. - } - PJ_CATCH(NO_MEMORY) { - ... // handle exception 1 - } - PJ_CATCH(SYNTAX_ERROR) { - ... // handle exception 2 - } - PJ_DEFAULT { - ... // handle other exceptions. - } - PJ_END; - } - \endverbatim - * - * The above sample uses hard coded exception ID. It is @b strongly - * recommended that applications request a unique exception ID instead - * of hard coded value like above. - * - * \section pj_except_reg Exception ID Allocation - * - * To ensure that exception ID (number) are used consistently and to - * prevent ID collisions in an application, it is strongly suggested that - * applications allocate an exception ID for each possible exception - * type. As a bonus of this process, the application can identify - * the name of the exception when the particular exception is thrown. - * - * Exception ID management are performed with the following APIs: - * - #pj_exception_id_alloc(). - * - #pj_exception_id_free(). - * - #pj_exception_id_name(). - * - * - * PJLIB itself automatically allocates one exception id, i.e. - * #PJ_NO_MEMORY_EXCEPTION which is declared in . This exception - * ID is raised by default pool policy when it fails to allocate memory. - * - * \section PJ_EX_KEYWORDS Keywords - * - * \subsection PJ_THROW PJ_THROW(expression) - * Throw an exception. The expression thrown is an integer as the result of - * the \a expression. This keyword can be specified anywhere within the - * program. - * - * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION - * Specify this in the variable definition section of the function block - * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception - * block. - * Actually, this is just a macro to declare local variable which is used to - * push the exception state to the exception stack. - * - * \subsection PJ_TRY PJ_TRY - * The \a PJ_TRY keyword is typically followed by a block. If an exception is - * thrown in this block, then the execution will resume to the \a PJ_CATCH - * handler. - * - * \subsection PJ_CATCH PJ_CATCH(expression) - * The \a PJ_CATCH is normally followed by a block. This block will be executed - * if the exception being thrown is equal to the expression specified in the - * \a PJ_CATCH. - * - * \subsection PJ_DEFAULT PJ_DEFAULT - * The \a PJ_DEFAULT keyword is normally followed by a block. This block will - * be executed if the exception being thrown doesn't match any of the \a - * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the - * last block of the handlers. - * - * \subsection PJ_END PJ_END - * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks. - * - * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void) - * Get the last exception thrown. This macro is normally called inside the - * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where - * the \a PJ_USE_EXCEPTION definition is in scope. - * - * - * \section pj_except_examples_sec Examples - * - * For some examples on how to use the exception construct, please see: - * - @ref page_pjlib_samples_except_c - * - @ref page_pjlib_exception_test - */ - -/** - * Allocate a unique exception id. - * Applications don't have to allocate a unique exception ID before using - * the exception construct. However, by doing so it ensures that there is - * no collisions of exception ID. - * - * As a bonus, when exception number is acquired through this function, - * the library can assign name to the exception (only if - * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the - * exception name when it catches an exception. - * - * @param name Name to be associated with the exception ID. - * @param id Pointer to receive the ID. - * - * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library - * is running out out ids. - */ -PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name, - pj_exception_id_t *id); - -/** - * Free an exception id. - * - * @param id The exception ID. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id); - -/** - * Retrieve name associated with the exception id. - * - * @param id The exception ID. - * - * @return The name associated with the specified ID. - */ -PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id); - -/** @} */ - -/** - * This structure (which should be invisible to user) manages the TRY handler - * stack. - */ -struct pj_exception_state_t -{ - struct pj_exception_state_t *prev; /**< Previous state in the list. */ - pj_jmp_buf state; /**< jmp_buf. */ -}; - -/** - * Throw exception. - * @param id Exception Id. - */ -PJ_DECL_NO_RETURN(void) -pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN; - -/** - * Push exception handler. - */ -PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec); - -/** - * Pop exception handler. - */ -PJ_DECL(void) pj_pop_exception_handler_(void); - -/** - * Declare that the function will use exception. - * @hideinitializer - */ -#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__ - -/** - * Start exception specification block. - * @hideinitializer - */ -#define PJ_TRY if (1) { \ - pj_push_exception_handler_(&pj_x_except__); \ - pj_x_code__ = pj_setjmp(pj_x_except__.state); \ - if (pj_x_code__ == 0) -/** - * Catch the specified exception Id. - * @param id The exception number to catch. - * @hideinitializer - */ -#define PJ_CATCH(id) else if (pj_x_code__ == (id)) - -/** - * Catch any exception number. - * @hideinitializer - */ -#define PJ_DEFAULT else - -/** - * End of exception specification block. - * @hideinitializer - */ -#define PJ_END pj_pop_exception_handler_(); \ - } else {} - -/** - * Throw exception. - * @param exception_id The exception number. - * @hideinitializer - */ -#define PJ_THROW(exception_id) pj_throw_exception_(exception_id) - -/** - * Get current exception. - * @return Current exception code. - * @hideinitializer - */ -#define PJ_GET_EXCEPTION() (pj_x_code__) - -PJ_END_DECL - - - -#endif /* __PJ_EXCEPTION_H__ */ - - +/* $Header: /pjproject-0.3/pjlib/include/pj/except.h 9 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_EXCEPTION_H__ +#define __PJ_EXCEPTION_H__ + +/** + * @file except.h + * @brief Exception Handling in C. + */ + +#include +#include + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_EXCEPT Exception Handling + * @ingroup PJ_MISC + * @{ + * + * \section pj_except_sample_sec Quick Example + * + * For the impatient, take a look at some examples: + * - @ref page_pjlib_samples_except_c + * - @ref page_pjlib_exception_test + * + * \section pj_except_except Exception Handling + * + * This module provides exception handling syntactically similar to C++ in + * C language. The underlying mechanism use setjmp() and longjmp(), and since + * these constructs are ANSI standard, the mechanism here should be available + * on most platforms/compilers which are ANSI compliant. + * + * If ANSI libc is not available, then setjmp()/longjmp() implementation will + * be provided. See for compatibility. + * + * The exception handling mechanism is completely thread safe, so the exception + * thrown by one thread will not interfere with other thread. + * + * CAVEATS: + * - unlike C++ exception, the scheme here won't call destructors of local + * objects if exception is thrown. Care must be taken when a function + * hold some resorce such as pool or mutex etc. + * - You CAN NOT make nested exception in one single function without using + * a nested PJ_USE_EXCEPTION. + * - Exceptions will always be caught by the first handle (unlike C++ where + * exception is only caught if the type matches. + * + * The exception handling constructs are similar to C++. The blocks will be + * constructed similar to the following sample: + * + * \verbatim + #define NO_MEMORY 1 + #define SYNTAX_ERROR 2 + + int main() + { + PJ_USE_EXCEPTION; // declare local exception stack. + + PJ_TRY { + ...// do something.. + } + PJ_CATCH(NO_MEMORY) { + ... // handle exception 1 + } + PJ_CATCH(SYNTAX_ERROR) { + ... // handle exception 2 + } + PJ_DEFAULT { + ... // handle other exceptions. + } + PJ_END; + } + \endverbatim + * + * The above sample uses hard coded exception ID. It is @b strongly + * recommended that applications request a unique exception ID instead + * of hard coded value like above. + * + * \section pj_except_reg Exception ID Allocation + * + * To ensure that exception ID (number) are used consistently and to + * prevent ID collisions in an application, it is strongly suggested that + * applications allocate an exception ID for each possible exception + * type. As a bonus of this process, the application can identify + * the name of the exception when the particular exception is thrown. + * + * Exception ID management are performed with the following APIs: + * - #pj_exception_id_alloc(). + * - #pj_exception_id_free(). + * - #pj_exception_id_name(). + * + * + * PJLIB itself automatically allocates one exception id, i.e. + * #PJ_NO_MEMORY_EXCEPTION which is declared in . This exception + * ID is raised by default pool policy when it fails to allocate memory. + * + * \section PJ_EX_KEYWORDS Keywords + * + * \subsection PJ_THROW PJ_THROW(expression) + * Throw an exception. The expression thrown is an integer as the result of + * the \a expression. This keyword can be specified anywhere within the + * program. + * + * \subsection PJ_USE_EXCEPTION PJ_USE_EXCEPTION + * Specify this in the variable definition section of the function block + * (or any blocks) to specify that the block has \a PJ_TRY/PJ_CATCH exception + * block. + * Actually, this is just a macro to declare local variable which is used to + * push the exception state to the exception stack. + * + * \subsection PJ_TRY PJ_TRY + * The \a PJ_TRY keyword is typically followed by a block. If an exception is + * thrown in this block, then the execution will resume to the \a PJ_CATCH + * handler. + * + * \subsection PJ_CATCH PJ_CATCH(expression) + * The \a PJ_CATCH is normally followed by a block. This block will be executed + * if the exception being thrown is equal to the expression specified in the + * \a PJ_CATCH. + * + * \subsection PJ_DEFAULT PJ_DEFAULT + * The \a PJ_DEFAULT keyword is normally followed by a block. This block will + * be executed if the exception being thrown doesn't match any of the \a + * PJ_CATCH specification. The \a PJ_DEFAULT block \b MUST be placed as the + * last block of the handlers. + * + * \subsection PJ_END PJ_END + * Specify this keyword to mark the end of \a PJ_TRY / \a PJ_CATCH blocks. + * + * \subsection PJ_GET_EXCEPTION PJ_GET_EXCEPTION(void) + * Get the last exception thrown. This macro is normally called inside the + * \a PJ_CATCH or \a PJ_DEFAULT block, altough it can be used anywhere where + * the \a PJ_USE_EXCEPTION definition is in scope. + * + * + * \section pj_except_examples_sec Examples + * + * For some examples on how to use the exception construct, please see: + * - @ref page_pjlib_samples_except_c + * - @ref page_pjlib_exception_test + */ + +/** + * Allocate a unique exception id. + * Applications don't have to allocate a unique exception ID before using + * the exception construct. However, by doing so it ensures that there is + * no collisions of exception ID. + * + * As a bonus, when exception number is acquired through this function, + * the library can assign name to the exception (only if + * PJ_HAS_EXCEPTION_NAMES is enabled (default is yes)) and find out the + * exception name when it catches an exception. + * + * @param name Name to be associated with the exception ID. + * @param id Pointer to receive the ID. + * + * @return PJ_SUCCESS on success or PJ_ETOOMANY if the library + * is running out out ids. + */ +PJ_DECL(pj_status_t) pj_exception_id_alloc(const char *name, + pj_exception_id_t *id); + +/** + * Free an exception id. + * + * @param id The exception ID. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_exception_id_free(pj_exception_id_t id); + +/** + * Retrieve name associated with the exception id. + * + * @param id The exception ID. + * + * @return The name associated with the specified ID. + */ +PJ_DECL(const char*) pj_exception_id_name(pj_exception_id_t id); + +/** @} */ + +/** + * This structure (which should be invisible to user) manages the TRY handler + * stack. + */ +struct pj_exception_state_t +{ + struct pj_exception_state_t *prev; /**< Previous state in the list. */ + pj_jmp_buf state; /**< jmp_buf. */ +}; + +/** + * Throw exception. + * @param id Exception Id. + */ +PJ_DECL_NO_RETURN(void) +pj_throw_exception_(pj_exception_id_t id) PJ_ATTR_NORETURN; + +/** + * Push exception handler. + */ +PJ_DECL(void) pj_push_exception_handler_(struct pj_exception_state_t *rec); + +/** + * Pop exception handler. + */ +PJ_DECL(void) pj_pop_exception_handler_(void); + +/** + * Declare that the function will use exception. + * @hideinitializer + */ +#define PJ_USE_EXCEPTION struct pj_exception_state_t pj_x_except__; int pj_x_code__ + +/** + * Start exception specification block. + * @hideinitializer + */ +#define PJ_TRY if (1) { \ + pj_push_exception_handler_(&pj_x_except__); \ + pj_x_code__ = pj_setjmp(pj_x_except__.state); \ + if (pj_x_code__ == 0) +/** + * Catch the specified exception Id. + * @param id The exception number to catch. + * @hideinitializer + */ +#define PJ_CATCH(id) else if (pj_x_code__ == (id)) + +/** + * Catch any exception number. + * @hideinitializer + */ +#define PJ_DEFAULT else + +/** + * End of exception specification block. + * @hideinitializer + */ +#define PJ_END pj_pop_exception_handler_(); \ + } else {} + +/** + * Throw exception. + * @param exception_id The exception number. + * @hideinitializer + */ +#define PJ_THROW(exception_id) pj_throw_exception_(exception_id) + +/** + * Get current exception. + * @return Current exception code. + * @hideinitializer + */ +#define PJ_GET_EXCEPTION() (pj_x_code__) + +PJ_END_DECL + + + +#endif /* __PJ_EXCEPTION_H__ */ + + diff --git a/pjlib/include/pj/fifobuf.h b/pjlib/include/pj/fifobuf.h index 45f91b172..5c4c78d7d 100644 --- a/pjlib/include/pj/fifobuf.h +++ b/pjlib/include/pj/fifobuf.h @@ -1,27 +1,27 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/fifobuf.h 4 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_FIFOBUF_H__ -#define __PJ_FIFOBUF_H__ - -#include - -PJ_BEGIN_DECL - -typedef struct pj_fifobuf_t pj_fifobuf_t; -struct pj_fifobuf_t -{ - char *first, *last; - char *ubegin, *uend; - int full; -}; - -PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size); -PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb); -PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size); -PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf); -PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf); - -PJ_END_DECL - -#endif /* __PJ_FIFOBUF_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/fifobuf.h 4 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_FIFOBUF_H__ +#define __PJ_FIFOBUF_H__ + +#include + +PJ_BEGIN_DECL + +typedef struct pj_fifobuf_t pj_fifobuf_t; +struct pj_fifobuf_t +{ + char *first, *last; + char *ubegin, *uend; + int full; +}; + +PJ_DECL(void) pj_fifobuf_init (pj_fifobuf_t *fb, void *buffer, unsigned size); +PJ_DECL(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fb); +PJ_DECL(void*) pj_fifobuf_alloc (pj_fifobuf_t *fb, unsigned size); +PJ_DECL(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fb, void *buf); +PJ_DECL(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fb, void *buf); + +PJ_END_DECL + +#endif /* __PJ_FIFOBUF_H__ */ + diff --git a/pjlib/include/pj/guid.h b/pjlib/include/pj/guid.h index 7dccf7fb7..652b5474f 100644 --- a/pjlib/include/pj/guid.h +++ b/pjlib/include/pj/guid.h @@ -1,75 +1,75 @@ -/* $header: $ */ - -#ifndef __PJ_GUID_H__ -#define __PJ_GUID_H__ - - -/** - * @file guid.h - * @brief GUID Globally Unique Identifier. - */ -#include - - -PJ_BEGIN_DECL - - -/** - * @defgroup PJ_DS Data Structure. - * @ingroup PJ - */ -/** - * @defgroup PJ_GUID Globally Unique Identifier - * @ingroup PJ_DS - * @{ - * - * This module provides API to create string that is globally unique. - * If application doesn't require that strong requirement, it can just - * use #pj_create_random_string() instead. - */ - - -/** - * PJ_GUID_STRING_LENGTH specifies length of GUID string. The value is - * dependent on the algorithm used internally to generate the GUID string. - * If real GUID generator is used, then the length will be 128bit or - * 32 bytes. If shadow GUID generator is used, then the length - * will be 20 bytes. Application should not assume which algorithm will - * be used by GUID generator. - */ -extern const unsigned PJ_GUID_STRING_LENGTH; - -/** - * PJ_GUID_MAX_LENGTH specifies the maximum length of GUID string, - * regardless of which algorithm to use. - */ -#define PJ_GUID_MAX_LENGTH 32 - -/** - * Create a globally unique string, which length is PJ_GUID_STRING_LENGTH - * characters. Caller is responsible for preallocating the storage used - * in the string. - * - * @param str The string to store the result. - * - * @return The string. - */ -PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str); - -/** - * Generate a unique string. - * - * @param pool Pool to allocate memory from. - * @param str The string. - */ -PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str); - - -/** - * @} - */ - -PJ_END_DECL - -#endif/* __PJ_GUID_H__ */ - +/* $header: $ */ + +#ifndef __PJ_GUID_H__ +#define __PJ_GUID_H__ + + +/** + * @file guid.h + * @brief GUID Globally Unique Identifier. + */ +#include + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_DS Data Structure. + * @ingroup PJ + */ +/** + * @defgroup PJ_GUID Globally Unique Identifier + * @ingroup PJ_DS + * @{ + * + * This module provides API to create string that is globally unique. + * If application doesn't require that strong requirement, it can just + * use #pj_create_random_string() instead. + */ + + +/** + * PJ_GUID_STRING_LENGTH specifies length of GUID string. The value is + * dependent on the algorithm used internally to generate the GUID string. + * If real GUID generator is used, then the length will be 128bit or + * 32 bytes. If shadow GUID generator is used, then the length + * will be 20 bytes. Application should not assume which algorithm will + * be used by GUID generator. + */ +extern const unsigned PJ_GUID_STRING_LENGTH; + +/** + * PJ_GUID_MAX_LENGTH specifies the maximum length of GUID string, + * regardless of which algorithm to use. + */ +#define PJ_GUID_MAX_LENGTH 32 + +/** + * Create a globally unique string, which length is PJ_GUID_STRING_LENGTH + * characters. Caller is responsible for preallocating the storage used + * in the string. + * + * @param str The string to store the result. + * + * @return The string. + */ +PJ_DECL(pj_str_t*) pj_generate_unique_string(pj_str_t *str); + +/** + * Generate a unique string. + * + * @param pool Pool to allocate memory from. + * @param str The string. + */ +PJ_DECL(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str); + + +/** + * @} + */ + +PJ_END_DECL + +#endif/* __PJ_GUID_H__ */ + diff --git a/pjlib/include/pj/hash.h b/pjlib/include/pj/hash.h index f475dcc09..e0555c37f 100644 --- a/pjlib/include/pj/hash.h +++ b/pjlib/include/pj/hash.h @@ -1,140 +1,140 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/hash.h 6 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_HASH_H__ -#define __PJ_HASH_H__ - -/** - * @file hash.h - * @brief Hash Table. - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_HASH Hash Table - * @ingroup PJ_DS - * @{ - * A hash table is a dictionary in which keys are mapped to array positions by - * hash functions. Having the keys of more than one item map to the same - * position is called a collision. In this library, we will chain the nodes - * that have the same key in a list. - */ - -/** - * If this constant is used as keylen, then the key is interpreted as - * NULL terminated string. - */ -#define PJ_HASH_KEY_STRING ((unsigned)-1) - -/** - * This is the function that is used by the hash table to calculate hash value - * of the specified key. - * - * @param hval the initial hash value, or zero. - * @param key the key to calculate. - * @param keylen the length of the key, or PJ_HASH_KEY_STRING to treat - * the key as null terminated string. - * - * @return the hash value. - */ -PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval, - const void *key, unsigned keylen); - - -/** - * Create a hash table with the specified 'bucket' size. - * - * @param pool the pool from which the hash table will be allocated from. - * @param size the bucket size, which will be round-up to the nearest 2^n+1 - * - * @return the hash table. - */ -PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size); - - -/** - * Get the value associated with the specified key. - * - * @param ht the hash table. - * @param key the key to look for. - * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the - * string length of the key. - * - * @return the value associated with the key, or NULL if the key is not found. - */ -PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht, - const void *key, unsigned keylen ); - - -/** - * Associate/disassociate a value with the specified key. - * - * @param pool the pool to allocate the new entry if a new entry has to be - * created. - * @param ht the hash table. - * @param key the key. - * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the - * string length of the key. - * @param value value to be associated, or NULL to delete the entry with - * the specified key. - */ -PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, - const void *key, unsigned keylen, - void *value ); - -/** - * Get the total number of entries in the hash table. - * - * @param ht the hash table. - * - * @return the number of entries in the hash table. - */ -PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht ); - - -/** - * Get the iterator to the first element in the hash table. - * - * @param ht the hash table. - * @param it the iterator for iterating hash elements. - * - * @return the iterator to the hash element, or NULL if no element presents. - */ -PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, - pj_hash_iterator_t *it ); - - -/** - * Get the next element from the iterator. - * - * @param ht the hash table. - * @param it the hash iterator. - * - * @return the next iterator, or NULL if there's no more element. - */ -PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, - pj_hash_iterator_t *it ); - -/** - * Get the value associated with a hash iterator. - * - * @param ht the hash table. - * @param it the hash iterator. - * - * @return the value associated with the current element in iterator. - */ -PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht, - pj_hash_iterator_t *it ); - - -/** - * @} - */ - -PJ_END_DECL - -#endif - - +/* $Header: /pjproject-0.3/pjlib/include/pj/hash.h 6 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_HASH_H__ +#define __PJ_HASH_H__ + +/** + * @file hash.h + * @brief Hash Table. + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_HASH Hash Table + * @ingroup PJ_DS + * @{ + * A hash table is a dictionary in which keys are mapped to array positions by + * hash functions. Having the keys of more than one item map to the same + * position is called a collision. In this library, we will chain the nodes + * that have the same key in a list. + */ + +/** + * If this constant is used as keylen, then the key is interpreted as + * NULL terminated string. + */ +#define PJ_HASH_KEY_STRING ((unsigned)-1) + +/** + * This is the function that is used by the hash table to calculate hash value + * of the specified key. + * + * @param hval the initial hash value, or zero. + * @param key the key to calculate. + * @param keylen the length of the key, or PJ_HASH_KEY_STRING to treat + * the key as null terminated string. + * + * @return the hash value. + */ +PJ_DECL(pj_uint32_t) pj_hash_calc(pj_uint32_t hval, + const void *key, unsigned keylen); + + +/** + * Create a hash table with the specified 'bucket' size. + * + * @param pool the pool from which the hash table will be allocated from. + * @param size the bucket size, which will be round-up to the nearest 2^n+1 + * + * @return the hash table. + */ +PJ_DECL(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size); + + +/** + * Get the value associated with the specified key. + * + * @param ht the hash table. + * @param key the key to look for. + * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the + * string length of the key. + * + * @return the value associated with the key, or NULL if the key is not found. + */ +PJ_DECL(void *) pj_hash_get( pj_hash_table_t *ht, + const void *key, unsigned keylen ); + + +/** + * Associate/disassociate a value with the specified key. + * + * @param pool the pool to allocate the new entry if a new entry has to be + * created. + * @param ht the hash table. + * @param key the key. + * @param keylen the length of the key, or PJ_HASH_KEY_STRING to use the + * string length of the key. + * @param value value to be associated, or NULL to delete the entry with + * the specified key. + */ +PJ_DECL(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, + void *value ); + +/** + * Get the total number of entries in the hash table. + * + * @param ht the hash table. + * + * @return the number of entries in the hash table. + */ +PJ_DECL(unsigned) pj_hash_count( pj_hash_table_t *ht ); + + +/** + * Get the iterator to the first element in the hash table. + * + * @param ht the hash table. + * @param it the iterator for iterating hash elements. + * + * @return the iterator to the hash element, or NULL if no element presents. + */ +PJ_DECL(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, + pj_hash_iterator_t *it ); + + +/** + * Get the next element from the iterator. + * + * @param ht the hash table. + * @param it the hash iterator. + * + * @return the next iterator, or NULL if there's no more element. + */ +PJ_DECL(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, + pj_hash_iterator_t *it ); + +/** + * Get the value associated with a hash iterator. + * + * @param ht the hash table. + * @param it the hash iterator. + * + * @return the value associated with the current element in iterator. + */ +PJ_DECL(void*) pj_hash_this( pj_hash_table_t *ht, + pj_hash_iterator_t *it ); + + +/** + * @} + */ + +PJ_END_DECL + +#endif + + diff --git a/pjlib/include/pj/ioqueue.h b/pjlib/include/pj/ioqueue.h index 4262ac7e3..cf83e9df1 100644 --- a/pjlib/include/pj/ioqueue.h +++ b/pjlib/include/pj/ioqueue.h @@ -1,473 +1,473 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/ioqueue.h 10 10/29/05 11:29a Bennylp $ */ - -#ifndef __PJ_IOQUEUE_H__ -#define __PJ_IOQUEUE_H__ - -/** - * @file ioqueue.h - * @brief I/O Dispatching Mechanism - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_IO Network I/O - * @brief Network I/O - * @ingroup PJ_OS - * - * This section contains API building blocks to perform network I/O and - * communications. If provides: - * - @ref PJ_SOCK - *\n - * A highly portable socket abstraction, runs on all kind of - * network APIs such as standard BSD socket, Windows socket, Linux - * \b kernel socket, PalmOS networking API, etc. - * - * - @ref pj_addr_resolve - *\n - * Portable address resolution, which implements #pj_gethostbyname(). - * - * - @ref PJ_SOCK_SELECT - *\n - * A portable \a select() like API (#pj_sock_select()) which can be - * implemented with various back-ends. - * - * - @ref PJ_IOQUEUE - *\n - * Framework for dispatching network events. - * - * For more information see the modules below. - */ - -/** - * @defgroup PJ_IOQUEUE I/O Event Dispatching Queue - * @ingroup PJ_IO - * @{ - * - * This file provides abstraction for various event dispatching mechanisms. - * The interfaces for event dispatching vary alot, even in a single - * operating system. The abstraction here hopefully is suitable for most of - * the event dispatching available. - * - * Currently, the I/O Queue supports: - * - select(), as the common denominator, but the least efficient. - * - I/O Completion ports in Windows NT/2000/XP, which is the most efficient - * way to dispatch events in Windows NT based OSes, and most importantly, - * it doesn't have the limit on how many handles to monitor. And it works - * with files (not only sockets) as well. - * - * \section pj_ioqeuue_examples_sec Examples - * - * For some examples on how to use the I/O Queue, please see: - * - * - \ref page_pjlib_ioqueue_tcp_test - * - \ref page_pjlib_ioqueue_udp_test - * - \ref page_pjlib_ioqueue_perf_test - */ - - /** - * This structure describes the callbacks to be called when I/O operation - * completes. - */ -typedef struct pj_ioqueue_callback -{ - /** - * This callback is called when #pj_ioqueue_read or #pj_ioqueue_recvfrom - * completes. - * - * @param key The key. - * @param bytes_read The size of data that has just been read. - */ - void (*on_read_complete)(pj_ioqueue_key_t *key, pj_ssize_t bytes_read); - - /** - * This callback is called when #pj_ioqueue_write or #pj_ioqueue_sendto - * completes. - * - * @param key The key. - * @param bytes_read The size of data that has just been read. - */ - void (*on_write_complete)(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent); - - /** - * This callback is called when #pj_ioqueue_accept completes. - * - * @param key The key. - * @param sock Newly connected socket. - * @param status Zero if the operation completes successfully. - */ - void (*on_accept_complete)(pj_ioqueue_key_t *key, pj_sock_t sock, - int status); - - /** - * This callback is called when #pj_ioqueue_connect completes. - * - * @param key The key. - * @param status Zero if the operation completes successfully. - */ - void (*on_connect_complete)(pj_ioqueue_key_t *key, int status); -} pj_ioqueue_callback; - - -/** - * Types of I/O Queue operation. - */ -typedef enum pj_ioqueue_operation_e -{ - PJ_IOQUEUE_OP_NONE = 0, /**< No operation. */ - PJ_IOQUEUE_OP_READ = 1, /**< read() operation. */ - PJ_IOQUEUE_OP_RECV = 2, /**< recv() operation. */ - PJ_IOQUEUE_OP_RECV_FROM = 4, /**< recvfrom() operation. */ - PJ_IOQUEUE_OP_WRITE = 8, /**< write() operation. */ - PJ_IOQUEUE_OP_SEND = 16, /**< send() operation. */ - PJ_IOQUEUE_OP_SEND_TO = 32, /**< sendto() operation. */ -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 - PJ_IOQUEUE_OP_ACCEPT = 64, /**< accept() operation. */ - PJ_IOQUEUE_OP_CONNECT = 128, /**< connect() operation. */ -#endif /* PJ_HAS_TCP */ -} pj_ioqueue_operation_e; - - -/** - * Indicates that the I/O Queue should be created to handle reasonable - * number of threads. - */ -#define PJ_IOQUEUE_DEFAULT_THREADS 0 - - -/** - * Create a new I/O Queue framework. - * - * @param pool The pool to allocate the I/O queue structure. - * @param max_fd The maximum number of handles to be supported, which - * should not exceed PJ_IOQUEUE_MAX_HANDLES. - * @param max_threads The maximum number of threads that are allowed to - * operate on a single descriptor simultaneously. If - * the value is zero, the framework will set it - * to a reasonable value. - * @param ioqueue Pointer to hold the newly created I/O Queue. - * - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, - pj_size_t max_fd, - int max_threads, - pj_ioqueue_t **ioqueue); - -/** - * Destroy the I/O queue. - * - * @param ioque The I/O Queue to be destroyed. - * - * @return PJ_SUCCESS if success. - */ -PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque ); - -/** - * Set the lock object to be used by the I/O Queue. This function can only - * be called right after the I/O queue is created, before any handle is - * registered to the I/O queue. - * - * Initially the I/O queue is created with non-recursive mutex protection. - * Applications can supply alternative lock to be used by calling this - * function. - * - * @param ioque The ioqueue instance. - * @param lock The lock to be used by the ioqueue. - * @param auto_delete In non-zero, the lock will be deleted by the ioqueue. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, - pj_lock_t *lock, - pj_bool_t auto_delete ); - -/** - * Register a socket to the I/O queue framework. - * When a socket is registered to the IOQueue, it may be modified to use - * non-blocking IO. If it is modified, there is no guarantee that this - * modification will be restored after the socket is unregistered. - * - * @param pool To allocate the resource for the specified handle, - * which must be valid until the handle/key is unregistered - * from I/O Queue. - * @param ioque The I/O Queue. - * @param sock The socket. - * @param user_data User data to be associated with the key, which can be - * retrieved later. - * @param cb Callback to be called when I/O operation completes. - * @param key Pointer to receive the returned key. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, - pj_ioqueue_t *ioque, - pj_sock_t sock, - void *user_data, - const pj_ioqueue_callback *cb, - pj_ioqueue_key_t **key); - -/** - * Unregister a handle from the I/O Queue framework. - * - * @param ioque The I/O Queue. - * @param key The key that uniquely identifies the handle, which is - * returned from the function #pj_ioqueue_register_sock() - * or other registration functions. - * - * @return PJ_SUCCESS on success or the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key ); - - -/** - * Get user data associated with the I/O Queue key. - * - * @param key The key previously associated with the socket/handle with - * #pj_ioqueue_register_sock() (or other registration - * functions). - * - * @return The user data associated with the key, or NULL on error - * of if no data is associated with the key during - * registration. - */ -PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ); - - -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 -/** - * Instruct I/O Queue to wait for incoming connections on the specified - * listening socket. This function will return - * immediately (i.e. non-blocking) regardless whether some data has been - * transfered. If the function can't complete immediately, and the caller will - * be notified about the completion when it calls pj_ioqueue_poll(). - * - * @param ioqueue The I/O Queue - * @param key The key which registered to the server socket. - * @param sock Argument which contain pointer to receive - * the socket for the incoming connection. - * @param local Optional argument which contain pointer to variable to - * receive local address. - * @param remote Optional argument which contain pointer to variable to - * receive the remote address. - * @param addrlen On input, contains the length of the buffer for the - * address, and on output, contains the actual length of the - * address. This argument is optional. - * @return - * - PJ_SUCCESS If there's a connection available immediately, which - * in this case the callback should have been called before - * the function returns. - * - PJ_EPENDING If accept is queued, or - * - non-zero which indicates the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - pj_sock_t *sock, - pj_sockaddr_t *local, - pj_sockaddr_t *remote, - int *addrlen ); - -/** - * Initiate non-blocking socket connect. If the socket can NOT be connected - * immediately, the result will be reported during poll. - * - * @param ioqueue The ioqueue - * @param key The key associated with TCP socket - * @param addr The remote address. - * @param addrlen The remote address length. - * - * @return - * - PJ_SUCCESS If socket is connected immediately, which in this case - * the callback should have been called. - * - PJ_EPENDING If operation is queued, or - * - non-zero Indicates the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - const pj_sockaddr_t *addr, - int addrlen ); - -#endif /* PJ_HAS_TCP */ - -/** - * Poll the I/O Queue for completed events. - * - * @param ioque the I/O Queue. - * @param timeout polling timeout, or NULL if the thread wishes to wait - * indefinetely for the event. - * - * @return - * - zero if timed out (no event). - * - (<0) if error occured during polling. Callback will NOT be called. - * - (>1) to indicate numbers of events. Callbacks have been called. - */ -PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, - const pj_time_val *timeout); - -/** - * Instruct the I/O Queue to read from the specified handle. This function - * returns immediately (i.e. non-blocking) regardless whether some data has - * been transfered. If the operation can't complete immediately, caller will - * be notified about the completion when it calls pj_ioqueue_poll(). - * - * @param ioque The I/O Queue. - * @param key The key that uniquely identifies the handle. - * @param buffer The buffer to hold the read data. The caller MUST make sure - * that this buffer remain valid until the framework completes - * reading the handle. - * @param buflen The maximum size to be read. - * - * @return - * - PJ_SUCCESS If immediate data has been received. In this case, the - * callback must have been called before this function - * returns, and no pending operation is scheduled. - * - PJ_EPENDING If the operation has been queued. - * - non-zero The return value indicates the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen); - - -/** - * This function behaves similarly as #pj_ioqueue_read(), except that it is - * normally called for socket. - * - * @param ioque The I/O Queue. - * @param key The key that uniquely identifies the handle. - * @param buffer The buffer to hold the read data. The caller MUST make sure - * that this buffer remain valid until the framework completes - * reading the handle. - * @param buflen The maximum size to be read. - * @param flags Recv flag. - * - * @return - * - PJ_SUCCESS If immediate data has been received. In this case, the - * callback must have been called before this function - * returns, and no pending operation is scheduled. - * - PJ_EPENDING If the operation has been queued. - * - non-zero The return value indicates the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags ); - -/** - * This function behaves similarly as #pj_ioqueue_read(), except that it is - * normally called for socket, and the remote address will also be returned - * along with the data. Caller MUST make sure that both buffer and addr - * remain valid until the framework completes reading the data. - * - * @param ioque The I/O Queue. - * @param key The key that uniquely identifies the handle. - * @param buffer The buffer to hold the read data. The caller MUST make sure - * that this buffer remain valid until the framework completes - * reading the handle. - * @param buflen The maximum size to be read. - * @param flags Recv flag. - * @param addr Pointer to buffer to receive the address, or NULL. - * @param addrlen On input, specifies the length of the address buffer. - * On output, it will be filled with the actual length of - * the address. - * - * @return - * - PJ_SUCCESS If immediate data has been received. In this case, the - * callback must have been called before this function - * returns, and no pending operation is scheduled. - * - PJ_EPENDING If the operation has been queued. - * - non-zero The return value indicates the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags, - pj_sockaddr_t *addr, - int *addrlen); - -/** - * Instruct the I/O Queue to write to the handle. This function will return - * immediately (i.e. non-blocking) regardless whether some data has been - * transfered. If the function can't complete immediately, and the caller will - * be notified about the completion when it calls pj_ioqueue_poll(). - * - * @param ioque the I/O Queue. - * @param key the key that identifies the handle. - * @param data the data to send. Caller MUST make sure that this buffer - * remains valid until the write operation completes. - * @param datalen the length of the data. - * - * @return - * - PJ_SUCCESS If data was immediately written. - * - PJ_EPENDING If the operation has been queued. - * - non-zero The return value indicates the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen); - -/** - * This function behaves similarly as #pj_ioqueue_write(), except that - * pj_sock_send() (or equivalent) will be called to send the data. - * - * @param ioque the I/O Queue. - * @param key the key that identifies the handle. - * @param data the data to send. Caller MUST make sure that this buffer - * remains valid until the write operation completes. - * @param datalen the length of the data. - * @param flags send flags. - * - * @return - * - PJ_SUCCESS If data was immediately written. - * - PJ_EPENDING If the operation has been queued. - * - non-zero The return value indicates the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags ); - - -/** - * This function behaves similarly as #pj_ioqueue_write(), except that - * pj_sock_sendto() (or equivalent) will be called to send the data. - * - * @param ioque the I/O Queue. - * @param key the key that identifies the handle. - * @param data the data to send. Caller MUST make sure that this buffer - * remains valid until the write operation completes. - * @param datalen the length of the data. - * @param flags send flags. - * @param addr remote address. - * @param addrlen remote address length. - * - * @return - * - PJ_SUCCESS If data was immediately written. - * - PJ_EPENDING If the operation has been queued. - * - non-zero The return value indicates the error code. - */ -PJ_DECL(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags, - const pj_sockaddr_t *addr, - int addrlen); - - -/** - * !} - */ - -PJ_END_DECL - -#endif /* __PJ_IOQUEUE_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/ioqueue.h 10 10/29/05 11:29a Bennylp $ */ + +#ifndef __PJ_IOQUEUE_H__ +#define __PJ_IOQUEUE_H__ + +/** + * @file ioqueue.h + * @brief I/O Dispatching Mechanism + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_IO Network I/O + * @brief Network I/O + * @ingroup PJ_OS + * + * This section contains API building blocks to perform network I/O and + * communications. If provides: + * - @ref PJ_SOCK + *\n + * A highly portable socket abstraction, runs on all kind of + * network APIs such as standard BSD socket, Windows socket, Linux + * \b kernel socket, PalmOS networking API, etc. + * + * - @ref pj_addr_resolve + *\n + * Portable address resolution, which implements #pj_gethostbyname(). + * + * - @ref PJ_SOCK_SELECT + *\n + * A portable \a select() like API (#pj_sock_select()) which can be + * implemented with various back-ends. + * + * - @ref PJ_IOQUEUE + *\n + * Framework for dispatching network events. + * + * For more information see the modules below. + */ + +/** + * @defgroup PJ_IOQUEUE I/O Event Dispatching Queue + * @ingroup PJ_IO + * @{ + * + * This file provides abstraction for various event dispatching mechanisms. + * The interfaces for event dispatching vary alot, even in a single + * operating system. The abstraction here hopefully is suitable for most of + * the event dispatching available. + * + * Currently, the I/O Queue supports: + * - select(), as the common denominator, but the least efficient. + * - I/O Completion ports in Windows NT/2000/XP, which is the most efficient + * way to dispatch events in Windows NT based OSes, and most importantly, + * it doesn't have the limit on how many handles to monitor. And it works + * with files (not only sockets) as well. + * + * \section pj_ioqeuue_examples_sec Examples + * + * For some examples on how to use the I/O Queue, please see: + * + * - \ref page_pjlib_ioqueue_tcp_test + * - \ref page_pjlib_ioqueue_udp_test + * - \ref page_pjlib_ioqueue_perf_test + */ + + /** + * This structure describes the callbacks to be called when I/O operation + * completes. + */ +typedef struct pj_ioqueue_callback +{ + /** + * This callback is called when #pj_ioqueue_read or #pj_ioqueue_recvfrom + * completes. + * + * @param key The key. + * @param bytes_read The size of data that has just been read. + */ + void (*on_read_complete)(pj_ioqueue_key_t *key, pj_ssize_t bytes_read); + + /** + * This callback is called when #pj_ioqueue_write or #pj_ioqueue_sendto + * completes. + * + * @param key The key. + * @param bytes_read The size of data that has just been read. + */ + void (*on_write_complete)(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent); + + /** + * This callback is called when #pj_ioqueue_accept completes. + * + * @param key The key. + * @param sock Newly connected socket. + * @param status Zero if the operation completes successfully. + */ + void (*on_accept_complete)(pj_ioqueue_key_t *key, pj_sock_t sock, + int status); + + /** + * This callback is called when #pj_ioqueue_connect completes. + * + * @param key The key. + * @param status Zero if the operation completes successfully. + */ + void (*on_connect_complete)(pj_ioqueue_key_t *key, int status); +} pj_ioqueue_callback; + + +/** + * Types of I/O Queue operation. + */ +typedef enum pj_ioqueue_operation_e +{ + PJ_IOQUEUE_OP_NONE = 0, /**< No operation. */ + PJ_IOQUEUE_OP_READ = 1, /**< read() operation. */ + PJ_IOQUEUE_OP_RECV = 2, /**< recv() operation. */ + PJ_IOQUEUE_OP_RECV_FROM = 4, /**< recvfrom() operation. */ + PJ_IOQUEUE_OP_WRITE = 8, /**< write() operation. */ + PJ_IOQUEUE_OP_SEND = 16, /**< send() operation. */ + PJ_IOQUEUE_OP_SEND_TO = 32, /**< sendto() operation. */ +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + PJ_IOQUEUE_OP_ACCEPT = 64, /**< accept() operation. */ + PJ_IOQUEUE_OP_CONNECT = 128, /**< connect() operation. */ +#endif /* PJ_HAS_TCP */ +} pj_ioqueue_operation_e; + + +/** + * Indicates that the I/O Queue should be created to handle reasonable + * number of threads. + */ +#define PJ_IOQUEUE_DEFAULT_THREADS 0 + + +/** + * Create a new I/O Queue framework. + * + * @param pool The pool to allocate the I/O queue structure. + * @param max_fd The maximum number of handles to be supported, which + * should not exceed PJ_IOQUEUE_MAX_HANDLES. + * @param max_threads The maximum number of threads that are allowed to + * operate on a single descriptor simultaneously. If + * the value is zero, the framework will set it + * to a reasonable value. + * @param ioqueue Pointer to hold the newly created I/O Queue. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + int max_threads, + pj_ioqueue_t **ioqueue); + +/** + * Destroy the I/O queue. + * + * @param ioque The I/O Queue to be destroyed. + * + * @return PJ_SUCCESS if success. + */ +PJ_DECL(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque ); + +/** + * Set the lock object to be used by the I/O Queue. This function can only + * be called right after the I/O queue is created, before any handle is + * registered to the I/O queue. + * + * Initially the I/O queue is created with non-recursive mutex protection. + * Applications can supply alternative lock to be used by calling this + * function. + * + * @param ioque The ioqueue instance. + * @param lock The lock to be used by the ioqueue. + * @param auto_delete In non-zero, the lock will be deleted by the ioqueue. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ); + +/** + * Register a socket to the I/O queue framework. + * When a socket is registered to the IOQueue, it may be modified to use + * non-blocking IO. If it is modified, there is no guarantee that this + * modification will be restored after the socket is unregistered. + * + * @param pool To allocate the resource for the specified handle, + * which must be valid until the handle/key is unregistered + * from I/O Queue. + * @param ioque The I/O Queue. + * @param sock The socket. + * @param user_data User data to be associated with the key, which can be + * retrieved later. + * @param cb Callback to be called when I/O operation completes. + * @param key Pointer to receive the returned key. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **key); + +/** + * Unregister a handle from the I/O Queue framework. + * + * @param ioque The I/O Queue. + * @param key The key that uniquely identifies the handle, which is + * returned from the function #pj_ioqueue_register_sock() + * or other registration functions. + * + * @return PJ_SUCCESS on success or the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key ); + + +/** + * Get user data associated with the I/O Queue key. + * + * @param key The key previously associated with the socket/handle with + * #pj_ioqueue_register_sock() (or other registration + * functions). + * + * @return The user data associated with the key, or NULL on error + * of if no data is associated with the key during + * registration. + */ +PJ_DECL(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ); + + +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +/** + * Instruct I/O Queue to wait for incoming connections on the specified + * listening socket. This function will return + * immediately (i.e. non-blocking) regardless whether some data has been + * transfered. If the function can't complete immediately, and the caller will + * be notified about the completion when it calls pj_ioqueue_poll(). + * + * @param ioqueue The I/O Queue + * @param key The key which registered to the server socket. + * @param sock Argument which contain pointer to receive + * the socket for the incoming connection. + * @param local Optional argument which contain pointer to variable to + * receive local address. + * @param remote Optional argument which contain pointer to variable to + * receive the remote address. + * @param addrlen On input, contains the length of the buffer for the + * address, and on output, contains the actual length of the + * address. This argument is optional. + * @return + * - PJ_SUCCESS If there's a connection available immediately, which + * in this case the callback should have been called before + * the function returns. + * - PJ_EPENDING If accept is queued, or + * - non-zero which indicates the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen ); + +/** + * Initiate non-blocking socket connect. If the socket can NOT be connected + * immediately, the result will be reported during poll. + * + * @param ioqueue The ioqueue + * @param key The key associated with TCP socket + * @param addr The remote address. + * @param addrlen The remote address length. + * + * @return + * - PJ_SUCCESS If socket is connected immediately, which in this case + * the callback should have been called. + * - PJ_EPENDING If operation is queued, or + * - non-zero Indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ); + +#endif /* PJ_HAS_TCP */ + +/** + * Poll the I/O Queue for completed events. + * + * @param ioque the I/O Queue. + * @param timeout polling timeout, or NULL if the thread wishes to wait + * indefinetely for the event. + * + * @return + * - zero if timed out (no event). + * - (<0) if error occured during polling. Callback will NOT be called. + * - (>1) to indicate numbers of events. Callbacks have been called. + */ +PJ_DECL(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, + const pj_time_val *timeout); + +/** + * Instruct the I/O Queue to read from the specified handle. This function + * returns immediately (i.e. non-blocking) regardless whether some data has + * been transfered. If the operation can't complete immediately, caller will + * be notified about the completion when it calls pj_ioqueue_poll(). + * + * @param ioque The I/O Queue. + * @param key The key that uniquely identifies the handle. + * @param buffer The buffer to hold the read data. The caller MUST make sure + * that this buffer remain valid until the framework completes + * reading the handle. + * @param buflen The maximum size to be read. + * + * @return + * - PJ_SUCCESS If immediate data has been received. In this case, the + * callback must have been called before this function + * returns, and no pending operation is scheduled. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen); + + +/** + * This function behaves similarly as #pj_ioqueue_read(), except that it is + * normally called for socket. + * + * @param ioque The I/O Queue. + * @param key The key that uniquely identifies the handle. + * @param buffer The buffer to hold the read data. The caller MUST make sure + * that this buffer remain valid until the framework completes + * reading the handle. + * @param buflen The maximum size to be read. + * @param flags Recv flag. + * + * @return + * - PJ_SUCCESS If immediate data has been received. In this case, the + * callback must have been called before this function + * returns, and no pending operation is scheduled. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags ); + +/** + * This function behaves similarly as #pj_ioqueue_read(), except that it is + * normally called for socket, and the remote address will also be returned + * along with the data. Caller MUST make sure that both buffer and addr + * remain valid until the framework completes reading the data. + * + * @param ioque The I/O Queue. + * @param key The key that uniquely identifies the handle. + * @param buffer The buffer to hold the read data. The caller MUST make sure + * that this buffer remain valid until the framework completes + * reading the handle. + * @param buflen The maximum size to be read. + * @param flags Recv flag. + * @param addr Pointer to buffer to receive the address, or NULL. + * @param addrlen On input, specifies the length of the address buffer. + * On output, it will be filled with the actual length of + * the address. + * + * @return + * - PJ_SUCCESS If immediate data has been received. In this case, the + * callback must have been called before this function + * returns, and no pending operation is scheduled. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen); + +/** + * Instruct the I/O Queue to write to the handle. This function will return + * immediately (i.e. non-blocking) regardless whether some data has been + * transfered. If the function can't complete immediately, and the caller will + * be notified about the completion when it calls pj_ioqueue_poll(). + * + * @param ioque the I/O Queue. + * @param key the key that identifies the handle. + * @param data the data to send. Caller MUST make sure that this buffer + * remains valid until the write operation completes. + * @param datalen the length of the data. + * + * @return + * - PJ_SUCCESS If data was immediately written. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen); + +/** + * This function behaves similarly as #pj_ioqueue_write(), except that + * pj_sock_send() (or equivalent) will be called to send the data. + * + * @param ioque the I/O Queue. + * @param key the key that identifies the handle. + * @param data the data to send. Caller MUST make sure that this buffer + * remains valid until the write operation completes. + * @param datalen the length of the data. + * @param flags send flags. + * + * @return + * - PJ_SUCCESS If data was immediately written. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags ); + + +/** + * This function behaves similarly as #pj_ioqueue_write(), except that + * pj_sock_sendto() (or equivalent) will be called to send the data. + * + * @param ioque the I/O Queue. + * @param key the key that identifies the handle. + * @param data the data to send. Caller MUST make sure that this buffer + * remains valid until the write operation completes. + * @param datalen the length of the data. + * @param flags send flags. + * @param addr remote address. + * @param addrlen remote address length. + * + * @return + * - PJ_SUCCESS If data was immediately written. + * - PJ_EPENDING If the operation has been queued. + * - non-zero The return value indicates the error code. + */ +PJ_DECL(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen); + + +/** + * !} + */ + +PJ_END_DECL + +#endif /* __PJ_IOQUEUE_H__ */ + diff --git a/pjlib/include/pj/list.h b/pjlib/include/pj/list.h index 0b18f89c4..dc7e5c6cf 100644 --- a/pjlib/include/pj/list.h +++ b/pjlib/include/pj/list.h @@ -1,217 +1,217 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/list.h 8 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_LIST_H__ -#define __PJ_LIST_H__ - -/** - * @file list.h - * @brief Linked List data structure. - */ - -#include - -PJ_BEGIN_DECL - -/* - * @defgroup PJ_DS Data Structure. - * @ingroup PJ - */ - -/** - * @defgroup PJ_LIST Linked List - * @ingroup PJ_DS - * @{ - * - * List in PJLIB is implemented as doubly-linked list, and it won't require - * dynamic memory allocation (just as all PJLIB data structures). The list here - * should be viewed more like a low level C list instead of high level C++ list - * (which normally are easier to use but require dynamic memory allocations), - * therefore all caveats with C list apply here too (such as you can NOT put - * a node in more than one lists). - * - * \section pj_list_example_sec Examples - * - * See below for examples on how to manipulate linked list: - * - @ref page_pjlib_samples_list_c - * - @ref page_pjlib_list_test - */ - - -/** - * Use this macro in the start of the structure declaration to declare that - * the structure can be used in the linked list operation. This macro simply - * declares additional member @a prev and @a next to the structure. - * @hideinitializer - */ -#define PJ_DECL_LIST_MEMBER(type) type *prev; /** List @a prev. */ \ - type *next; /** List @a next. */ - - -/** - * This structure describes generic list node and list. The owner of this list - * must initialize the 'value' member to an appropriate value (typically the - * owner itself). - */ -struct pj_list -{ - PJ_DECL_LIST_MEMBER(void) -}; - - -/** - * Initialize the list. - * Initially, the list will have no member, and function pj_list_empty() will - * always return nonzero (which indicates TRUE) for the newly initialized - * list. - * - * @param node The list head. - */ -PJ_INLINE(void) pj_list_init(pj_list_type * node) -{ - ((pj_list*)node)->next = ((pj_list*)node)->prev = node; -} - - -/** - * Check that the list is empty. - * - * @param node The list head. - * - * @return Non-zero if the list is not-empty, or zero if it is empty. - * - */ -PJ_INLINE(int) pj_list_empty(const pj_list_type * node) -{ - return ((pj_list*)node)->next == node; -} - - -/** - * Insert the node to the list before the specified element position. - * - * @param pos The element to which the node will be inserted before. - * @param node The element to be inserted. - * - * @return void. - */ -PJ_IDECL(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node); - - -/** - * Inserts all nodes in \a nodes to the target list. - * - * @param lst The target list. - * @param nodes Nodes list. - */ -PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst, - pj_list_type *nodes); - -/** - * Insert a node to the list after the specified element position. - * - * @param pos The element in the list which will precede the inserted - * element. - * @param node The element to be inserted after the position element. - * - * @return void. - */ -PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node); - -/** - * Insert all nodes in \a nodes to the target list. - * - * @param lst The target list. - * @param nodes Nodes list. - */ -PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst, - pj_list_type *nodes); - - -/** - * Remove elements from the source list, and insert them to the destination - * list. The elements of the source list will occupy the - * front elements of the target list. Note that the node pointed by \a list2 - * itself is not considered as a node, but rather as the list descriptor, so - * it will not be inserted to the \a list1. The elements to be inserted starts - * at \a list2->next. If \a list2 is to be included in the operation, use - * \a pj_list_insert_nodes_before. - * - * @param list1 The destination list. - * @param list2 The source list. - * - * @return void. - */ -PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2); - - -/** - * Remove elements from the second list argument, and insert them to the list - * in the first argument. The elements from the second list will be appended - * to the first list. Note that the node pointed by \a list2 - * itself is not considered as a node, but rather as the list descriptor, so - * it will not be inserted to the \a list1. The elements to be inserted starts - * at \a list2->next. If \a list2 is to be included in the operation, use - * \a pj_list_insert_nodes_before. - * - * @param list1 The element in the list which will precede the inserted - * element. - * @param list2 The element in the list to be inserted. - * - * @return void. - */ -PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2); - - -/** - * Erase the node from the list it currently belongs. - * - * @param node The element to be erased. - */ -PJ_IDECL(void) pj_list_erase(pj_list_type *node); - - -/** - * Find node in the list. - * - * @param list The list head. - * @param node The node element to be searched. - * - * @return The node itself if it is found in the list, or NULL if it is not - * found in the list. - */ -PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list, - pj_list_type *node); - - -/** - * Search the list for the specified value, using the specified comparison - * function. This function iterates on nodes in the list, started with the - * first node, and call the user supplied comparison function until the - * comparison function returns ZERO. - * - * @param list The list head. - * @param value The user defined value to be passed in the comparison - * function - * @param comp The comparison function, which should return ZERO to - * indicate that the searched value is found. - * - * @return The first node that matched, or NULL if it is not found. - */ -PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value, - int (*comp)(void *value, - const pj_list_type *node) - ); - - -/** - * @} - */ - -#if PJ_FUNCTIONS_ARE_INLINED -# include "list_i.h" -#endif - -PJ_END_DECL - -#endif /* __PJ_LIST_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/list.h 8 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_LIST_H__ +#define __PJ_LIST_H__ + +/** + * @file list.h + * @brief Linked List data structure. + */ + +#include + +PJ_BEGIN_DECL + +/* + * @defgroup PJ_DS Data Structure. + * @ingroup PJ + */ + +/** + * @defgroup PJ_LIST Linked List + * @ingroup PJ_DS + * @{ + * + * List in PJLIB is implemented as doubly-linked list, and it won't require + * dynamic memory allocation (just as all PJLIB data structures). The list here + * should be viewed more like a low level C list instead of high level C++ list + * (which normally are easier to use but require dynamic memory allocations), + * therefore all caveats with C list apply here too (such as you can NOT put + * a node in more than one lists). + * + * \section pj_list_example_sec Examples + * + * See below for examples on how to manipulate linked list: + * - @ref page_pjlib_samples_list_c + * - @ref page_pjlib_list_test + */ + + +/** + * Use this macro in the start of the structure declaration to declare that + * the structure can be used in the linked list operation. This macro simply + * declares additional member @a prev and @a next to the structure. + * @hideinitializer + */ +#define PJ_DECL_LIST_MEMBER(type) type *prev; /** List @a prev. */ \ + type *next; /** List @a next. */ + + +/** + * This structure describes generic list node and list. The owner of this list + * must initialize the 'value' member to an appropriate value (typically the + * owner itself). + */ +struct pj_list +{ + PJ_DECL_LIST_MEMBER(void) +}; + + +/** + * Initialize the list. + * Initially, the list will have no member, and function pj_list_empty() will + * always return nonzero (which indicates TRUE) for the newly initialized + * list. + * + * @param node The list head. + */ +PJ_INLINE(void) pj_list_init(pj_list_type * node) +{ + ((pj_list*)node)->next = ((pj_list*)node)->prev = node; +} + + +/** + * Check that the list is empty. + * + * @param node The list head. + * + * @return Non-zero if the list is not-empty, or zero if it is empty. + * + */ +PJ_INLINE(int) pj_list_empty(const pj_list_type * node) +{ + return ((pj_list*)node)->next == node; +} + + +/** + * Insert the node to the list before the specified element position. + * + * @param pos The element to which the node will be inserted before. + * @param node The element to be inserted. + * + * @return void. + */ +PJ_IDECL(void) pj_list_insert_before(pj_list_type *pos, pj_list_type *node); + + +/** + * Inserts all nodes in \a nodes to the target list. + * + * @param lst The target list. + * @param nodes Nodes list. + */ +PJ_IDECL(void) pj_list_insert_nodes_before(pj_list_type *lst, + pj_list_type *nodes); + +/** + * Insert a node to the list after the specified element position. + * + * @param pos The element in the list which will precede the inserted + * element. + * @param node The element to be inserted after the position element. + * + * @return void. + */ +PJ_IDECL(void) pj_list_insert_after(pj_list_type *pos, pj_list_type *node); + +/** + * Insert all nodes in \a nodes to the target list. + * + * @param lst The target list. + * @param nodes Nodes list. + */ +PJ_IDECL(void) pj_list_insert_nodes_after(pj_list_type *lst, + pj_list_type *nodes); + + +/** + * Remove elements from the source list, and insert them to the destination + * list. The elements of the source list will occupy the + * front elements of the target list. Note that the node pointed by \a list2 + * itself is not considered as a node, but rather as the list descriptor, so + * it will not be inserted to the \a list1. The elements to be inserted starts + * at \a list2->next. If \a list2 is to be included in the operation, use + * \a pj_list_insert_nodes_before. + * + * @param list1 The destination list. + * @param list2 The source list. + * + * @return void. + */ +PJ_IDECL(void) pj_list_merge_first(pj_list_type *list1, pj_list_type *list2); + + +/** + * Remove elements from the second list argument, and insert them to the list + * in the first argument. The elements from the second list will be appended + * to the first list. Note that the node pointed by \a list2 + * itself is not considered as a node, but rather as the list descriptor, so + * it will not be inserted to the \a list1. The elements to be inserted starts + * at \a list2->next. If \a list2 is to be included in the operation, use + * \a pj_list_insert_nodes_before. + * + * @param list1 The element in the list which will precede the inserted + * element. + * @param list2 The element in the list to be inserted. + * + * @return void. + */ +PJ_IDECL(void) pj_list_merge_last( pj_list_type *list1, pj_list_type *list2); + + +/** + * Erase the node from the list it currently belongs. + * + * @param node The element to be erased. + */ +PJ_IDECL(void) pj_list_erase(pj_list_type *node); + + +/** + * Find node in the list. + * + * @param list The list head. + * @param node The node element to be searched. + * + * @return The node itself if it is found in the list, or NULL if it is not + * found in the list. + */ +PJ_IDECL(pj_list_type*) pj_list_find_node(pj_list_type *list, + pj_list_type *node); + + +/** + * Search the list for the specified value, using the specified comparison + * function. This function iterates on nodes in the list, started with the + * first node, and call the user supplied comparison function until the + * comparison function returns ZERO. + * + * @param list The list head. + * @param value The user defined value to be passed in the comparison + * function + * @param comp The comparison function, which should return ZERO to + * indicate that the searched value is found. + * + * @return The first node that matched, or NULL if it is not found. + */ +PJ_IDECL(pj_list_type*) pj_list_search(pj_list_type *list, void *value, + int (*comp)(void *value, + const pj_list_type *node) + ); + + +/** + * @} + */ + +#if PJ_FUNCTIONS_ARE_INLINED +# include "list_i.h" +#endif + +PJ_END_DECL + +#endif /* __PJ_LIST_H__ */ + diff --git a/pjlib/include/pj/list_i.h b/pjlib/include/pj/list_i.h index 5f995979d..108a0b8e1 100644 --- a/pjlib/include/pj/list_i.h +++ b/pjlib/include/pj/list_i.h @@ -1,101 +1,101 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/list_i.h 4 10/14/05 12:26a Bennylp $ */ - - -/* Internal */ -PJ_IDEF(void) pj_link_node(pj_list_type *prev, pj_list_type *next) -{ - ((pj_list*)prev)->next = next; - ((pj_list*)next)->prev = prev; -} - -/* -PJ_IDEF(void) -pj_list_init(pj_list_type * node) -{ - ((pj_list*)node)->next = ((pj_list*)node)->prev = node; -} - -PJ_IDEF(int) pj_list_empty(const pj_list_type * node) -{ - return ((pj_list*)node)->next == node; -} -*/ - -PJ_IDEF(void) -pj_list_insert_after(pj_list_type *pos, pj_list_type *node) -{ - ((pj_list*)node)->prev = pos; - ((pj_list*)node)->next = ((pj_list*)pos)->next; - ((pj_list*) ((pj_list*)pos)->next) ->prev = node; - ((pj_list*)pos)->next = node; -} - - -PJ_IDEF(void) -pj_list_insert_before(pj_list_type *pos, pj_list_type *node) -{ - pj_list_insert_after(((pj_list*)pos)->prev, node); -} - - -PJ_IDEF(void) -pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst) -{ - pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev; - pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next; - - pj_link_node(pos, lst); - pj_link_node(lst_last, pos_next); -} - -PJ_IDEF(void) -pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst) -{ - pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst); -} - -PJ_IDEF(void) -pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2) -{ - pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next); - pj_link_node(((pj_list*)lst2)->prev, lst1); - pj_list_init(lst2); -} - -PJ_IDEF(void) -pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2) -{ - pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next); - pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next); - pj_list_init(lst2); -} - -PJ_IDEF(void) -pj_list_erase(pj_list_type *node) -{ - pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next); -} - - -PJ_IDEF(pj_list_type*) -pj_list_find_node(pj_list_type *list, pj_list_type *node) -{ - pj_list *p = (pj_list *) ((pj_list*)list)->next; - while (p != list && p != node) - p = (pj_list *) p->next; - - return p==node ? p : NULL; -} - - -PJ_IDEF(pj_list_type*) -pj_list_search(pj_list_type *list, void *value, - int (*comp)(void *value, const pj_list_type *node)) -{ - pj_list *p = (pj_list *) ((pj_list*)list)->next; - while (p != list && (*comp)(value, p) != 0) - p = (pj_list *) p->next; - - return p==list ? NULL : p; -} - +/* $Header: /pjproject-0.3/pjlib/include/pj/list_i.h 4 10/14/05 12:26a Bennylp $ */ + + +/* Internal */ +PJ_IDEF(void) pj_link_node(pj_list_type *prev, pj_list_type *next) +{ + ((pj_list*)prev)->next = next; + ((pj_list*)next)->prev = prev; +} + +/* +PJ_IDEF(void) +pj_list_init(pj_list_type * node) +{ + ((pj_list*)node)->next = ((pj_list*)node)->prev = node; +} + +PJ_IDEF(int) pj_list_empty(const pj_list_type * node) +{ + return ((pj_list*)node)->next == node; +} +*/ + +PJ_IDEF(void) +pj_list_insert_after(pj_list_type *pos, pj_list_type *node) +{ + ((pj_list*)node)->prev = pos; + ((pj_list*)node)->next = ((pj_list*)pos)->next; + ((pj_list*) ((pj_list*)pos)->next) ->prev = node; + ((pj_list*)pos)->next = node; +} + + +PJ_IDEF(void) +pj_list_insert_before(pj_list_type *pos, pj_list_type *node) +{ + pj_list_insert_after(((pj_list*)pos)->prev, node); +} + + +PJ_IDEF(void) +pj_list_insert_nodes_after(pj_list_type *pos, pj_list_type *lst) +{ + pj_list *lst_last = (pj_list *) ((pj_list*)lst)->prev; + pj_list *pos_next = (pj_list *) ((pj_list*)pos)->next; + + pj_link_node(pos, lst); + pj_link_node(lst_last, pos_next); +} + +PJ_IDEF(void) +pj_list_insert_nodes_before(pj_list_type *pos, pj_list_type *lst) +{ + pj_list_insert_nodes_after(((pj_list*)pos)->prev, lst); +} + +PJ_IDEF(void) +pj_list_merge_last(pj_list_type *lst1, pj_list_type *lst2) +{ + pj_link_node(((pj_list*)lst1)->prev, ((pj_list*)lst2)->next); + pj_link_node(((pj_list*)lst2)->prev, lst1); + pj_list_init(lst2); +} + +PJ_IDEF(void) +pj_list_merge_first(pj_list_type *lst1, pj_list_type *lst2) +{ + pj_link_node(((pj_list*)lst2)->prev, ((pj_list*)lst1)->next); + pj_link_node(((pj_list*)lst1), ((pj_list*)lst2)->next); + pj_list_init(lst2); +} + +PJ_IDEF(void) +pj_list_erase(pj_list_type *node) +{ + pj_link_node( ((pj_list*)node)->prev, ((pj_list*)node)->next); +} + + +PJ_IDEF(pj_list_type*) +pj_list_find_node(pj_list_type *list, pj_list_type *node) +{ + pj_list *p = (pj_list *) ((pj_list*)list)->next; + while (p != list && p != node) + p = (pj_list *) p->next; + + return p==node ? p : NULL; +} + + +PJ_IDEF(pj_list_type*) +pj_list_search(pj_list_type *list, void *value, + int (*comp)(void *value, const pj_list_type *node)) +{ + pj_list *p = (pj_list *) ((pj_list*)list)->next; + while (p != list && (*comp)(value, p) != 0) + p = (pj_list *) p->next; + + return p==list ? NULL : p; +} + diff --git a/pjlib/include/pj/lock.h b/pjlib/include/pj/lock.h index c487019ac..26ee2e779 100644 --- a/pjlib/include/pj/lock.h +++ b/pjlib/include/pj/lock.h @@ -1,136 +1,136 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/lock.h 2 10/14/05 12:26a Bennylp $ */ -#ifndef __PJ_LOCK_H__ -#define __PJ_LOCK_H__ - -/** - * @file lock.h - * @brief Higher abstraction for locking objects. - */ -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_LOCK Lock Objects - * @ingroup PJ_OS - * @{ - * - * Lock Objects are higher abstraction for different lock mechanisms. - * It offers the same API for manipulating different lock types (e.g. - * @ref PJ_MUTEX "mutex", @ref PJ_SEM "semaphores", or null locks). - * Because Lock Objects have the same API for different types of lock - * implementation, it can be passed around in function arguments. As the - * result, it can be used to control locking policy for a particular - * feature. - */ - - -/** - * Create simple, non recursive mutex lock object. - * - * @param pool Memory pool. - * @param name Lock object's name. - * @param lock Pointer to store the returned handle. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, - const char *name, - pj_lock_t **lock ); - -/** - * Create recursive mutex lock object. - * - * @param pool Memory pool. - * @param name Lock object's name. - * @param lock Pointer to store the returned handle. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, - const char *name, - pj_lock_t **lock ); - - -/** - * Create NULL mutex. A NULL mutex doesn't actually have any synchronization - * object attached to it. - * - * @param pool Memory pool. - * @param name Lock object's name. - * @param lock Pointer to store the returned handle. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, - const char *name, - pj_lock_t **lock ); - - -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 -/** - * Create semaphore lock object. - * - * @param pool Memory pool. - * @param name Lock object's name. - * @param initial Initial value of the semaphore. - * @param max Maximum value of the semaphore. - * @param lock Pointer to store the returned handle. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, - const char *name, - unsigned initial, - unsigned max, - pj_lock_t **lock ); - -#endif /* PJ_HAS_SEMAPHORE */ - -/** - * Acquire lock on the specified lock object. - * - * @param lock The lock object. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_acquire( pj_lock_t *lock ); - - -/** - * Try to acquire lock on the specified lock object. - * - * @param lock The lock object. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ); - - -/** - * Release lock on the specified lock object. - * - * @param lock The lock object. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_release( pj_lock_t *lock ); - - -/** - * Destroy the lock object. - * - * @param lock The lock object. - * - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock ); - - -/** @} */ - -PJ_END_DECL - - -#endif /* __PJ_LOCK_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/lock.h 2 10/14/05 12:26a Bennylp $ */ +#ifndef __PJ_LOCK_H__ +#define __PJ_LOCK_H__ + +/** + * @file lock.h + * @brief Higher abstraction for locking objects. + */ +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_LOCK Lock Objects + * @ingroup PJ_OS + * @{ + * + * Lock Objects are higher abstraction for different lock mechanisms. + * It offers the same API for manipulating different lock types (e.g. + * @ref PJ_MUTEX "mutex", @ref PJ_SEM "semaphores", or null locks). + * Because Lock Objects have the same API for different types of lock + * implementation, it can be passed around in function arguments. As the + * result, it can be used to control locking policy for a particular + * feature. + */ + + +/** + * Create simple, non recursive mutex lock object. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ); + +/** + * Create recursive mutex lock object. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ); + + +/** + * Create NULL mutex. A NULL mutex doesn't actually have any synchronization + * object attached to it. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ); + + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +/** + * Create semaphore lock object. + * + * @param pool Memory pool. + * @param name Lock object's name. + * @param initial Initial value of the semaphore. + * @param max Maximum value of the semaphore. + * @param lock Pointer to store the returned handle. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_lock_t **lock ); + +#endif /* PJ_HAS_SEMAPHORE */ + +/** + * Acquire lock on the specified lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_acquire( pj_lock_t *lock ); + + +/** + * Try to acquire lock on the specified lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ); + + +/** + * Release lock on the specified lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_release( pj_lock_t *lock ); + + +/** + * Destroy the lock object. + * + * @param lock The lock object. + * + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_lock_destroy( pj_lock_t *lock ); + + +/** @} */ + +PJ_END_DECL + + +#endif /* __PJ_LOCK_H__ */ + diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h index 5d24f56c5..fb79a0e7a 100644 --- a/pjlib/include/pj/log.h +++ b/pjlib/include/pj/log.h @@ -1,304 +1,304 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/log.h 7 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_LOG_H__ -#define __PJ_LOG_H__ - -/** - * @file log.h - * @brief Logging Utility. - */ - -#include - - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_MISC Miscelaneous - * @ingroup PJ - */ - -/** - * @defgroup PJ_LOG Logging Facility - * @ingroup PJ_MISC - * @{ - * - * The PJLIB logging facility is a configurable, flexible, and convenient - * way to write logging or trace information. - * - * To write to the log, one uses construct like below: - * - *
- *   ...
- *   PJ_LOG(3, ("main.c", "Starting hello..."));
- *   ...
- *   PJ_LOG(3, ("main.c", "Hello world from process %d", pj_getpid()));
- *   ...
- * 
- * - * In the above example, the number @b 3 controls the verbosity level of - * the information (which means "information", by convention). The string - * "main.c" specifies the source or sender of the message. - * - * - * \section pj_log_quick_sample_sec Examples - * - * For examples, see: - * - @ref page_pjlib_samples_log_c. - * - */ - -/** - * Log decoration flag, to be specified with #pj_log_set_decor(). - */ -enum pj_log_decoration -{ - PJ_LOG_HAS_DAY_NAME = 1, /**< Include day name [default: no]. */ - PJ_LOG_HAS_YEAR = 2, /**< Include year digit [default: no] */ - PJ_LOG_HAS_MONTH = 4, /**< Include month [default: no] */ - PJ_LOG_HAS_DAY_OF_MON = 8, /**< Include day of month [default: no] */ - PJ_LOG_HAS_TIME = 16, /**< Include time [default: yes]. */ - PJ_LOG_HAS_MICRO_SEC = 32, /**< Include microseconds [yes] */ - PJ_LOG_HAS_SENDER = 64, /**< Include sender in the log [yes]. */ - PJ_LOG_HAS_NEWLINE = 128, /**< Terminate each call with newline [yes].*/ -}; - -/** - * Write log message. - * This is the main macro used to write text to the logging backend. - * - * @param level The logging verbosity level. Lower number indicates higher - * importance, with level zero indicates fatal error. Only - * numeral argument is permitted (e.g. not variable). - * @param arg Enclosed 'printf' like arguments, with the first - * argument is the sender, the second argument is format - * string and the following arguments are variable number of - * arguments suitable for the format string. - * - * Sample: - * \verbatim - PJ_LOG(2, (__FILE__, "current value is %d", value)); - \endverbatim - * @hideinitializer - */ -#define PJ_LOG(level,arg) pj_log_wrapper_##level(arg) - -/** - * Signature for function to be registered to the logging subsystem to - * write the actual log message to some output device. - * - * @param level Log level. - * @param data Log message. - * @param len Message length. - */ -typedef void pj_log_func(int level, const char *data, int len); - -/** - * Default logging writer function used by front end logger function. - * Application normally should NOT need to call this function, but - * rather use the PJ_LOG macro. - * - * @param level Log level. - * @param buffer Log message. - * @param len Message length. - */ -PJ_DECL(void) pj_log_write(int level, const char *buffer, int len); - - -#if PJ_LOG_MAX_LEVEL >= 1 - -/** - * Change log output function. The front-end logging functions will call - * this function to write the actual message to the desired device. - * By default, the front-end functions use pj_log_write() to write - * the messages, unless it's changed by calling this function. - * - * @param func The function that will be called to write the log - * messages to the desired device. - */ -PJ_DECL(void) pj_log_set_log_func( pj_log_func *func ); - -/** - * Get the current log output function that is used to write log messages. - * - * @return Current log output function. - */ -PJ_DECL(pj_log_func*) pj_log_get_log_func(void); - -/** - * Set maximum log level. Application can call this function to set - * the desired level of verbosity of the logging messages. The bigger the - * value, the more verbose the logging messages will be printed. However, - * the maximum level of verbosity can not exceed compile time value of - * PJ_LOG_MAX_LEVEL. - * - * @param level The maximum level of verbosity of the logging - * messages (6=very detailed..1=error only, 0=disabled) - */ -PJ_DECL(void) pj_log_set_level(int level); - -/** - * Get current maximum log verbositylevel. - * - * @return Current log maximum level. - */ -PJ_DECL(int) pj_log_get_level(void); - -/** - * Set log decoration. The log decoration flag controls what are printed - * to output device alongside the actual message. For example, application - * can specify that date/time information should be displayed with each - * log message. - * - * @param decor Bitmask combination of #pj_log_decoration to control - * the layout of the log message. - */ -PJ_DECL(void) pj_log_set_decor(unsigned decor); - -/** - * Get current log decoration flag. - * - * @return Log decoration flag. - */ -PJ_DECL(unsigned) pj_log_get_decor(void); - - -#else /* #if PJ_LOG_MAX_LEVEL >= 1 */ - -/** - * Change log output function. The front-end logging functions will call - * this function to write the actual message to the desired device. - * By default, the front-end functions use pj_log_write() to write - * the messages, unless it's changed by calling this function. - * - * @param func The function that will be called to write the log - * messages to the desired device. - */ -# define pj_log_set_log_func(func) - -/** - * Set maximum log level. Application can call this function to set - * the desired level of verbosity of the logging messages. The bigger the - * value, the more verbose the logging messages will be printed. However, - * the maximum level of verbosity can not exceed compile time value of - * PJ_LOG_MAX_LEVEL. - * - * @param level The maximum level of verbosity of the logging - * messages (6=very detailed..1=error only, 0=disabled) - */ -# define pj_log_set_level(level) - -/** - * Set log decoration. The log decoration flag controls what are printed - * to output device alongside the actual message. For example, application - * can specify that date/time information should be displayed with each - * log message. - * - * @param decor Bitmask combination of #pj_log_decoration to control - * the layout of the log message. - */ -# define pj_log_set_decor(decor) - -#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */ - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/* - * Log functions implementation prototypes. - * These functions are called by PJ_LOG macros according to verbosity - * level specified when calling the macro. Applications should not normally - * need to call these functions directly. - */ - -/** - * @def pj_log_wrapper_1(arg) - * Internal function to write log with verbosity 1. Will evaluate to - * empty expression if PJ_LOG_MAX_LEVEL is below 1. - * @param arg Log expression. - */ -#if PJ_LOG_MAX_LEVEL >= 1 - #define pj_log_wrapper_1(arg) pj_log_1 arg - /** Internal function. */ - PJ_DECL(void) pj_log_1(const char *src, const char *format, ...); -#else - #define pj_log_wrapper_1(arg) -#endif - -/** - * @def pj_log_wrapper_2(arg) - * Internal function to write log with verbosity 2. Will evaluate to - * empty expression if PJ_LOG_MAX_LEVEL is below 2. - * @param arg Log expression. - */ -#if PJ_LOG_MAX_LEVEL >= 2 - #define pj_log_wrapper_2(arg) pj_log_2 arg - /** Internal function. */ - PJ_DECL(void) pj_log_2(const char *src, const char *format, ...); -#else - #define pj_log_wrapper_2(arg) -#endif - -/** - * @def pj_log_wrapper_3(arg) - * Internal function to write log with verbosity 3. Will evaluate to - * empty expression if PJ_LOG_MAX_LEVEL is below 3. - * @param arg Log expression. - */ -#if PJ_LOG_MAX_LEVEL >= 3 - #define pj_log_wrapper_3(arg) pj_log_3 arg - /** Internal function. */ - PJ_DECL(void) pj_log_3(const char *src, const char *format, ...); -#else - #define pj_log_wrapper_3(arg) -#endif - -/** - * @def pj_log_wrapper_4(arg) - * Internal function to write log with verbosity 4. Will evaluate to - * empty expression if PJ_LOG_MAX_LEVEL is below 4. - * @param arg Log expression. - */ -#if PJ_LOG_MAX_LEVEL >= 4 - #define pj_log_wrapper_4(arg) pj_log_4 arg - /** Internal function. */ - PJ_DECL(void) pj_log_4(const char *src, const char *format, ...); -#else - #define pj_log_wrapper_4(arg) -#endif - -/** - * @def pj_log_wrapper_5(arg) - * Internal function to write log with verbosity 5. Will evaluate to - * empty expression if PJ_LOG_MAX_LEVEL is below 5. - * @param arg Log expression. - */ -#if PJ_LOG_MAX_LEVEL >= 5 - #define pj_log_wrapper_5(arg) pj_log_5 arg - /** Internal function. */ - PJ_DECL(void) pj_log_5(const char *src, const char *format, ...); -#else - #define pj_log_wrapper_5(arg) -#endif - -/** - * @def pj_log_wrapper_6(arg) - * Internal function to write log with verbosity 6. Will evaluate to - * empty expression if PJ_LOG_MAX_LEVEL is below 6. - * @param arg Log expression. - */ -#if PJ_LOG_MAX_LEVEL >= 6 - #define pj_log_wrapper_6(arg) pj_log_6 arg - /** Internal function. */ - PJ_DECL(void) pj_log_6(const char *src, const char *format, ...); -#else - #define pj_log_wrapper_6(arg) -#endif - - -PJ_END_DECL - -#endif /* __PJ_LOG_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/log.h 7 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_LOG_H__ +#define __PJ_LOG_H__ + +/** + * @file log.h + * @brief Logging Utility. + */ + +#include + + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_MISC Miscelaneous + * @ingroup PJ + */ + +/** + * @defgroup PJ_LOG Logging Facility + * @ingroup PJ_MISC + * @{ + * + * The PJLIB logging facility is a configurable, flexible, and convenient + * way to write logging or trace information. + * + * To write to the log, one uses construct like below: + * + *
+ *   ...
+ *   PJ_LOG(3, ("main.c", "Starting hello..."));
+ *   ...
+ *   PJ_LOG(3, ("main.c", "Hello world from process %d", pj_getpid()));
+ *   ...
+ * 
+ * + * In the above example, the number @b 3 controls the verbosity level of + * the information (which means "information", by convention). The string + * "main.c" specifies the source or sender of the message. + * + * + * \section pj_log_quick_sample_sec Examples + * + * For examples, see: + * - @ref page_pjlib_samples_log_c. + * + */ + +/** + * Log decoration flag, to be specified with #pj_log_set_decor(). + */ +enum pj_log_decoration +{ + PJ_LOG_HAS_DAY_NAME = 1, /**< Include day name [default: no]. */ + PJ_LOG_HAS_YEAR = 2, /**< Include year digit [default: no] */ + PJ_LOG_HAS_MONTH = 4, /**< Include month [default: no] */ + PJ_LOG_HAS_DAY_OF_MON = 8, /**< Include day of month [default: no] */ + PJ_LOG_HAS_TIME = 16, /**< Include time [default: yes]. */ + PJ_LOG_HAS_MICRO_SEC = 32, /**< Include microseconds [yes] */ + PJ_LOG_HAS_SENDER = 64, /**< Include sender in the log [yes]. */ + PJ_LOG_HAS_NEWLINE = 128, /**< Terminate each call with newline [yes].*/ +}; + +/** + * Write log message. + * This is the main macro used to write text to the logging backend. + * + * @param level The logging verbosity level. Lower number indicates higher + * importance, with level zero indicates fatal error. Only + * numeral argument is permitted (e.g. not variable). + * @param arg Enclosed 'printf' like arguments, with the first + * argument is the sender, the second argument is format + * string and the following arguments are variable number of + * arguments suitable for the format string. + * + * Sample: + * \verbatim + PJ_LOG(2, (__FILE__, "current value is %d", value)); + \endverbatim + * @hideinitializer + */ +#define PJ_LOG(level,arg) pj_log_wrapper_##level(arg) + +/** + * Signature for function to be registered to the logging subsystem to + * write the actual log message to some output device. + * + * @param level Log level. + * @param data Log message. + * @param len Message length. + */ +typedef void pj_log_func(int level, const char *data, int len); + +/** + * Default logging writer function used by front end logger function. + * Application normally should NOT need to call this function, but + * rather use the PJ_LOG macro. + * + * @param level Log level. + * @param buffer Log message. + * @param len Message length. + */ +PJ_DECL(void) pj_log_write(int level, const char *buffer, int len); + + +#if PJ_LOG_MAX_LEVEL >= 1 + +/** + * Change log output function. The front-end logging functions will call + * this function to write the actual message to the desired device. + * By default, the front-end functions use pj_log_write() to write + * the messages, unless it's changed by calling this function. + * + * @param func The function that will be called to write the log + * messages to the desired device. + */ +PJ_DECL(void) pj_log_set_log_func( pj_log_func *func ); + +/** + * Get the current log output function that is used to write log messages. + * + * @return Current log output function. + */ +PJ_DECL(pj_log_func*) pj_log_get_log_func(void); + +/** + * Set maximum log level. Application can call this function to set + * the desired level of verbosity of the logging messages. The bigger the + * value, the more verbose the logging messages will be printed. However, + * the maximum level of verbosity can not exceed compile time value of + * PJ_LOG_MAX_LEVEL. + * + * @param level The maximum level of verbosity of the logging + * messages (6=very detailed..1=error only, 0=disabled) + */ +PJ_DECL(void) pj_log_set_level(int level); + +/** + * Get current maximum log verbositylevel. + * + * @return Current log maximum level. + */ +PJ_DECL(int) pj_log_get_level(void); + +/** + * Set log decoration. The log decoration flag controls what are printed + * to output device alongside the actual message. For example, application + * can specify that date/time information should be displayed with each + * log message. + * + * @param decor Bitmask combination of #pj_log_decoration to control + * the layout of the log message. + */ +PJ_DECL(void) pj_log_set_decor(unsigned decor); + +/** + * Get current log decoration flag. + * + * @return Log decoration flag. + */ +PJ_DECL(unsigned) pj_log_get_decor(void); + + +#else /* #if PJ_LOG_MAX_LEVEL >= 1 */ + +/** + * Change log output function. The front-end logging functions will call + * this function to write the actual message to the desired device. + * By default, the front-end functions use pj_log_write() to write + * the messages, unless it's changed by calling this function. + * + * @param func The function that will be called to write the log + * messages to the desired device. + */ +# define pj_log_set_log_func(func) + +/** + * Set maximum log level. Application can call this function to set + * the desired level of verbosity of the logging messages. The bigger the + * value, the more verbose the logging messages will be printed. However, + * the maximum level of verbosity can not exceed compile time value of + * PJ_LOG_MAX_LEVEL. + * + * @param level The maximum level of verbosity of the logging + * messages (6=very detailed..1=error only, 0=disabled) + */ +# define pj_log_set_level(level) + +/** + * Set log decoration. The log decoration flag controls what are printed + * to output device alongside the actual message. For example, application + * can specify that date/time information should be displayed with each + * log message. + * + * @param decor Bitmask combination of #pj_log_decoration to control + * the layout of the log message. + */ +# define pj_log_set_decor(decor) + +#endif /* #if PJ_LOG_MAX_LEVEL >= 1 */ + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * Log functions implementation prototypes. + * These functions are called by PJ_LOG macros according to verbosity + * level specified when calling the macro. Applications should not normally + * need to call these functions directly. + */ + +/** + * @def pj_log_wrapper_1(arg) + * Internal function to write log with verbosity 1. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 1. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 1 + #define pj_log_wrapper_1(arg) pj_log_1 arg + /** Internal function. */ + PJ_DECL(void) pj_log_1(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_1(arg) +#endif + +/** + * @def pj_log_wrapper_2(arg) + * Internal function to write log with verbosity 2. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 2. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 2 + #define pj_log_wrapper_2(arg) pj_log_2 arg + /** Internal function. */ + PJ_DECL(void) pj_log_2(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_2(arg) +#endif + +/** + * @def pj_log_wrapper_3(arg) + * Internal function to write log with verbosity 3. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 3. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 3 + #define pj_log_wrapper_3(arg) pj_log_3 arg + /** Internal function. */ + PJ_DECL(void) pj_log_3(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_3(arg) +#endif + +/** + * @def pj_log_wrapper_4(arg) + * Internal function to write log with verbosity 4. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 4. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 4 + #define pj_log_wrapper_4(arg) pj_log_4 arg + /** Internal function. */ + PJ_DECL(void) pj_log_4(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_4(arg) +#endif + +/** + * @def pj_log_wrapper_5(arg) + * Internal function to write log with verbosity 5. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 5. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 5 + #define pj_log_wrapper_5(arg) pj_log_5 arg + /** Internal function. */ + PJ_DECL(void) pj_log_5(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_5(arg) +#endif + +/** + * @def pj_log_wrapper_6(arg) + * Internal function to write log with verbosity 6. Will evaluate to + * empty expression if PJ_LOG_MAX_LEVEL is below 6. + * @param arg Log expression. + */ +#if PJ_LOG_MAX_LEVEL >= 6 + #define pj_log_wrapper_6(arg) pj_log_6 arg + /** Internal function. */ + PJ_DECL(void) pj_log_6(const char *src, const char *format, ...); +#else + #define pj_log_wrapper_6(arg) +#endif + + +PJ_END_DECL + +#endif /* __PJ_LOG_H__ */ + diff --git a/pjlib/include/pj/md5.h b/pjlib/include/pj/md5.h index 979d8584e..d3f939950 100644 --- a/pjlib/include/pj/md5.h +++ b/pjlib/include/pj/md5.h @@ -1,92 +1,92 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/md5.h 5 9/17/05 10:37a Bennylp $ */ -/* - Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.h is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Removed support for non-ANSI compilers; removed - references to Ghostscript; clarified derivation from RFC 1321; - now handles byte order either statically or dynamically. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); - added conditionalization for C++ compilation from Martin - Purschke . - 1999-05-03 lpd Original version. - */ - -#ifndef md5_INCLUDED -# define md5_INCLUDED - -/* - * This package supports both compile-time and run-time determination of CPU - * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be - * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is - * defined as non-zero, the code will be compiled to run only on big-endian - * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to - * run on either big- or little-endian CPUs, but will run slightly less - * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. - */ - -typedef unsigned char md5_byte_t; /* 8-bit byte */ -typedef unsigned long md5_word_t; /* 32-bit word */ - -/** Define the state of the MD5 Algorithm. */ -typedef struct md5_state_s { - md5_word_t count[2]; /**< message length in bits, lsw first */ - md5_word_t abcd[4]; /**< digest buffer */ - md5_byte_t buf[64]; /**< accumulate block */ -} md5_state_t; - -#ifdef __cplusplus -extern "C" -{ -#endif - -/** Initialize the algorithm. */ -void md5_init(md5_state_t *pms); - -/** Append a string to the message. */ -void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); - -/** Finish the message and return the digest. */ -void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); - -#ifdef __cplusplus -} /* end extern "C" */ -#endif - -#endif /* md5_INCLUDED */ +/* $Header: /pjproject-0.3/pjlib/src/pj/md5.h 5 9/17/05 10:37a Bennylp $ */ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned long md5_word_t; /* 32-bit word */ + +/** Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /**< message length in bits, lsw first */ + md5_word_t abcd[4]; /**< digest buffer */ + md5_byte_t buf[64]; /**< accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/** Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/** Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h index 845a26bc0..f37ca7022 100644 --- a/pjlib/include/pj/os.h +++ b/pjlib/include/pj/os.h @@ -1,904 +1,909 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/os.h 12 10/29/05 11:30a Bennylp $ */ - -#ifndef __PJ_OS_H__ -#define __PJ_OS_H__ - -/** - * @file os.h - * @brief OS dependent functions - */ -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_OS Operating System Dependent Functionality. - * @ingroup PJ - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJ_THREAD Threads - * @ingroup PJ_OS - * @{ - * This module provides multithreading API. - * - * \section pj_thread_examples_sec Examples - * - * For examples, please see: - * - \ref page_pjlib_thread_test - * - \ref page_pjlib_sleep_test - * - */ - -/** - * Thread creation flags: - * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended. - */ -typedef enum pj_thread_create_flags -{ - PJ_THREAD_SUSPENDED = 1 -} pj_thread_create_flags; - - -/** - * Specify this as \a stack_size argument in #pj_thread_create() to specify - * that thread should use default stack size for the current platform. - */ -#define PJ_THREAD_DEFAULT_STACK_SIZE 0 - -/** - * Type of thread entry function. - */ -typedef int (PJ_THREAD_FUNC pj_thread_proc)(void*); - -/** - * Size of thread struct. - */ -#if !defined(PJ_THREAD_DESC_SIZE) -# define PJ_THREAD_DESC_SIZE (PJ_MAX_OBJ_NAME + 10*sizeof(long)) -#endif - -/** - * Thread structure, to thread's state when the thread is created by external - * or native API. - */ -typedef pj_uint8_t pj_thread_desc[PJ_THREAD_DESC_SIZE]; - -/** - * Get process ID. - * @return process ID. - */ -PJ_DECL(pj_uint32_t) pj_getpid(void); - -/** - * Create a new thread. - * - * @param pool The memory pool from which the thread record - * will be allocated from. - * @param thread_name The optional name to be assigned to the thread. - * @param proc Thread entry function. - * @param arg Argument to be passed to the thread entry function. - * @param stack_size The size of the stack for the new thread, or ZERO or - * PJ_THREAD_DEFAULT_STACK_SIZE to let the - * library choose the reasonable size for the stack. - * For some systems, the stack will be allocated from - * the pool, so the pool must have suitable capacity. - * @param flags Flags for thread creation, which is bitmask combination - * from enum pj_thread_create_flags. - * @param thread Pointer to hold the newly created thread. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_thread_create( pj_pool_t *pool, - const char *thread_name, - pj_thread_proc *proc, - void *arg, - pj_size_t stack_size, - unsigned flags, - pj_thread_t **thread ); - -/** - * Register a thread that was created by external or native API to PJLIB. - * This function must be called in the context of the thread being registered. - * When the thread is created by external function or API call, - * it must be 'registered' to PJLIB using pj_thread_register(), so that it can - * cooperate with PJLIB's framework. During registration, some data needs to - * be maintained, and this data must remain available during the thread's - * lifetime. - * - * @param thread_name The optional name to be assigned to the thread. - * @param desc Thread descriptor, which must be available throughout - * the lifetime of the thread. - * @param thread Pointer to hold the created thread handle. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_thread_register ( const char *thread_name, - pj_thread_desc desc, - pj_thread_t **thread); - -/** - * Get thread name. - * - * @param thread The thread handle. - * - * @return Thread name as null terminated string. - */ -PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread); - -/** - * Resume a suspended thread. - * - * @param thread The thread handle. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread); - -/** - * Get the current thread. - * - * @return Thread handle of current thread. - */ -PJ_DECL(pj_thread_t*) pj_thread_this(void); - -/** - * Join thread. - * This function will block the caller thread until the specified thread exits. - * - * @param thread The thread handle. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread); - - -/** - * Destroy thread and release resources allocated for the thread. - * However, the memory allocated for the pj_thread_t itself will only be released - * when the pool used to create the thread is destroyed. - * - * @param thread The thread handle. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread); - - -/** - * Put the current thread to sleep for the specified miliseconds. - * - * @param msec Miliseconds delay. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec); - -/** - * @def PJ_CHECK_STACK() - * PJ_CHECK_STACK() macro is used to check the sanity of the stack. - * The OS implementation may check that no stack overflow occurs, and - * it also may collect statistic about stack usage. - */ -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - -# define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__) - -/** @internal - * The implementation of stack checking. - */ -PJ_DECL(void) pj_thread_check_stack(const char *file, int line); - -/** @internal - * Get maximum stack usage statistic. - */ -PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread); - -/** @internal - * Dump thread stack status. - */ -PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, - const char **file, - int *line); -#else - -# define PJ_CHECK_STACK() -/** pj_thread_get_stack_max_usage() for the thread */ -# define pj_thread_get_stack_max_usage(thread) 0 -/** pj_thread_get_stack_info() for the thread */ -# define pj_thread_get_stack_info(thread,f,l) (*(f)="",*(l)=0) -#endif /* PJ_OS_HAS_CHECK_STACK */ - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJ_TLS Thread Local Storage. - * @ingroup PJ_OS - * @{ - */ - -/** - * Allocate thread local storage index. The initial value of the variable at - * the index is zero. - * - * @param index Pointer to hold the return value. - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index); - -/** - * Deallocate thread local variable. - * - * @param index The variable index. - */ -PJ_DECL(void) pj_thread_local_free(long index); - -/** - * Set the value of thread local variable. - * - * @param index The index of the variable. - * @param value The value. - */ -PJ_DECL(void) pj_thread_local_set(long index, void *value); - -/** - * Get the value of thread local variable. - * - * @param index The index of the variable. - * @return The value. - */ -PJ_DECL(void*) pj_thread_local_get(long index); - - -/** - * @} - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJ_ATOMIC Atomic Variables - * @ingroup PJ_OS - * @{ - * - * This module provides API to manipulate atomic variables. - * - * \section pj_atomic_examples_sec Examples - * - * For some example codes, please see: - * - @ref page_pjlib_atomic_test - */ - - -/** - * Create atomic variable. - * - * @param pool The pool. - * @param initial The initial value of the atomic variable. - * @param atomic Pointer to hold the atomic variable upon return. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_atomic_create( pj_pool_t *pool, - pj_atomic_value_t initial, - pj_atomic_t **atomic ); - -/** - * Destroy atomic variable. - * - * @param atomic_var the atomic variable. - * - * @return PJ_SUCCESS if success. - */ -PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ); - -/** - * Set the value of an atomic type, and return the previous value. - * - * @param atomic_var the atomic variable. - * @param value value to be set to the variable. - * - * @return the previous value of the variable. - */ -PJ_DECL(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *atomic_var, - pj_atomic_value_t value); - -/** - * Get the value of an atomic type. - * - * @param atomic_var the atomic variable. - * - * @return the value of the atomic variable. - */ -PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var); - -/** - * Increment the value of an atomic type. - * - * @param atomic_var the atomic variable. - * - * @return the result. - */ -PJ_DECL(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *atomic_var); - -/** - * Decrement the value of an atomic type. - * - * @param atomic_var the atomic variable. - * - * @return the result. - */ -PJ_DECL(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *atomic_var); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJ_MUTEX Mutexes. - * @ingroup PJ_OS - * @{ - * - * Mutex manipulation. Alternatively, application can use higher abstraction - * for lock objects, which provides uniform API for all kinds of lock - * mechanisms, including mutex. See @ref PJ_LOCK for more information. - */ - -/** - * Mutex types: - * - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent. - * - PJ_MUTEX_SIMPLE: non-recursive mutex. - * - PJ_MUTEX_RECURSIVE: recursive mutex. - */ -typedef enum pj_mutex_type_e -{ - PJ_MUTEX_DEFAULT, - PJ_MUTEX_SIMPLE, - PJ_MUTEX_RECURSE, -} pj_mutex_type_e; - - -/** - * Create mutex of the specified type. - * - * @param pool The pool. - * @param name Name to be associated with the mutex (for debugging). - * @param type The type of the mutex, of type #pj_mutex_type_e. - * @param mutex Pointer to hold the returned mutex instance. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool, - const char *name, - int type, - pj_mutex_t **mutex); - -/** - * Create simple, non-recursive mutex. - * This function is a simple wrapper for #pj_mutex_create to create - * non-recursive mutex. - * - * @param pool The pool. - * @param name Mutex name. - * @param mutex Pointer to hold the returned mutex instance. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, - pj_mutex_t **mutex ); - -/** - * Create recursive mutex. - * This function is a simple wrapper for #pj_mutex_create to create - * recursive mutex. - * - * @param pool The pool. - * @param name Mutex name. - * @param mutex Pointer to hold the returned mutex instance. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, - const char *name, - pj_mutex_t **mutex ); - -/** - * Acquire mutex lock. - * - * @param mutex The mutex. - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex); - -/** - * Release mutex lock. - * - * @param mutex The mutex. - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex); - -/** - * Try to acquire mutex lock. - * - * @param mutex The mutex. - * @return PJ_SUCCESS on success, or the error code if the - * lock couldn't be acquired. - */ -PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex); - -/** - * Destroy mutex. - * - * @param mutex Te mutex. - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex); - -/** - * Determine whether calling thread is owning the mutex (only available when - * PJ_DEBUG is set). - * @param mutex The mutex. - * @return Non-zero if yes. - */ -#if defined(PJ_DEBUG) && PJ_DEBUG != 0 - PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex); -#else -# define pj_mutex_is_locked(mutex) 1 -#endif - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJ_CRIT_SEC Critical sections. - * @ingroup PJ_OS - * @{ - * Critical section protection can be used to protect regions where: - * - mutual exclusion protection is needed. - * - it's rather too expensive to create a mutex. - * - the time spent in the region is very very brief. - * - * Critical section is a global object, and it prevents any threads from - * entering any regions that are protected by critical section once a thread - * is already in the section. - * - * Critial section is \a not recursive! - * - * Application MUST NOT call any functions that may cause current - * thread to block (such as allocating memory, performing I/O, locking mutex, - * etc.) while holding the critical section. - */ -/** - * Enter critical section. - */ -PJ_DECL(void) pj_enter_critical_section(void); - -/** - * Leave critical section. - */ -PJ_DECL(void) pj_leave_critical_section(void); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 -/** - * @defgroup PJ_SEM Semaphores. - * @ingroup PJ_OS - * @{ - * - * This module provides abstraction for semaphores, where available. - */ - -/** - * Create semaphore. - * - * @param pool The pool. - * @param name Name to be assigned to the semaphore (for logging purpose) - * @param initial The initial count of the semaphore. - * @param max The maximum count of the semaphore. - * @param sem Pointer to hold the semaphore created. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_sem_create( pj_pool_t *pool, - const char *name, - unsigned initial, - unsigned max, - pj_sem_t **sem); - -/** - * Wait for semaphore. - * - * @param sem The semaphore. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem); - -/** - * Try wait for semaphore. - * - * @param sem The semaphore. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem); - -/** - * Release semaphore. - * - * @param sem The semaphore. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem); - -/** - * Destroy semaphore. - * - * @param sem The semaphore. - * - * @return PJ_SUCCESS on success, or the error code. - */ -PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem); - -/** - * @} - */ -#endif /* PJ_HAS_SEMAPHORE */ - - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 -/** - * @defgroup PJ_EVENT Event Object. - * @ingroup PJ_OS - * @{ - * - * This module provides abstraction to event object (e.g. Win32 Event) where - * available. Event objects can be used for synchronization among threads. - */ - -/** - * Create event object. - * - * @param pool The pool. - * @param name The name of the event object (for logging purpose). - * @param manual_reset Specify whether the event is manual-reset - * @param initial Specify the initial state of the event object. - * @param event Pointer to hold the returned event object. - * - * @return event handle, or NULL if failed. - */ -PJ_DECL(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, - pj_bool_t manual_reset, pj_bool_t initial, - pj_event_t **event); - -/** - * Wait for event to be signaled. - * - * @param event The event object. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event); - -/** - * Try wait for event object to be signalled. - * - * @param event The event object. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event); - -/** - * Set the event object state to signaled. For auto-reset event, this - * will only release the first thread that are waiting on the event. For - * manual reset event, the state remains signaled until the event is reset. - * If there is no thread waiting on the event, the event object state - * remains signaled. - * - * @param event The event object. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event); - -/** - * Set the event object to signaled state to release appropriate number of - * waiting threads and then reset the event object to non-signaled. For - * manual-reset event, this function will release all waiting threads. For - * auto-reset event, this function will only release one waiting thread. - * - * @param event The event object. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event); - -/** - * Set the event object state to non-signaled. - * - * @param event The event object. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event); - -/** - * Destroy the event object. - * - * @param event The event object. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event); - -/** - * @} - */ -#endif /* PJ_HAS_EVENT_OBJ */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @addtogroup PJ_TIME Time Data Type and Manipulation. - * @ingroup PJ_OS - * @{ - * This module provides API for manipulating time. - * - * \section pj_time_examples_sec Examples - * - * For examples, please see: - * - \ref page_pjlib_sleep_test - */ - -/** - * Get current time of day in local representation. - * - * @param tv Variable to store the result. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv); - - -/** - * Parse time value into date/time representation. - * - * @param tv The time. - * @param pt Variable to store the date time result. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt); - -/** - * Encode date/time to time value. - * - * @param pt The date/time. - * @param tv Variable to store time value result. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); - -/** - * Convert local time to GMT. - * - * @param tv Time to convert. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); - -/** - * Convert GMT to local time. - * - * @param tv Time to convert. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 - -/** - * @defgroup PJ_TERM Terminal - * @ingroup PJ_OS - * @{ - */ - -/** - * Set current terminal color. - * - * @param color The RGB color. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color); - -/** - * Get current terminal foreground color. - * - * @return RGB color. - */ -PJ_DECL(pj_color_t) pj_term_get_color(void); - -/** - * @} - */ - -#endif /* PJ_TERM_HAS_COLOR */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJ_TIMESTAMP High Resolution Timestamp - * @ingroup PJ_OS - * @{ - * - * PJLIB provides High Resolution Timestamp API to access highest - * resolution timestamp value provided by the platform. The API is usefull - * to measure precise elapsed time, and can be used in applications such - * as profiling. - * - * The timestamp value is represented in cycles, and can be related to - * normal time (in seconds or sub-seconds) using various functions provided. - * - * \section pj_timestamp_examples_sec Examples - * - * For examples, please see: - * - \ref page_pjlib_sleep_test - * - \ref page_pjlib_timestamp_test - */ - -/* - * High resolution timer. - */ -#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 - -/** - * This structure represents high resolution (64bit) time value. The time - * values represent time in cycles, which is retrieved by calling - * #pj_get_timestamp(). - */ -typedef union pj_timestamp -{ - struct - { - pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */ - pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */ - } u32; /**< The 64-bit value as two 32-bit values. */ - -#if PJ_HAS_INT64 - pj_uint64_t u64; /**< The whole 64-bit value, where available. */ -#endif -} pj_timestamp; - - -/** - * Acquire high resolution timer value. The time value are stored - * in cycles. - * - * @param ts High resolution timer value. - * @return PJ_SUCCESS or the appropriate error code. - * - * @see pj_get_timestamp_freq(). - */ -PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts); - -/** - * Get high resolution timer frequency, in cycles per second. - * - * @param freq Timer frequency, in cycles per second. - * @return PJ_SUCCESS or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq); - -/** - * Calculate the elapsed time, and store it in pj_time_val. - * This function calculates the elapsed time using highest precision - * calculation that is available for current platform, considering - * whether floating point or 64-bit precision arithmetic is available. - * For maximum portability, application should prefer to use this function - * rather than calculating the elapsed time by itself. - * - * @param start The starting timestamp. - * @param stop The end timestamp. - * - * @return Elapsed time as #pj_time_val. - * - * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec() - */ -PJ_DECL(pj_time_val) pj_elapsed_time( const pj_timestamp *start, - const pj_timestamp *stop ); - -/** - * Calculate the elapsed time in 32-bit microseconds. - * This function calculates the elapsed time using highest precision - * calculation that is available for current platform, considering - * whether floating point or 64-bit precision arithmetic is available. - * For maximum portability, application should prefer to use this function - * rather than calculating the elapsed time by itself. - * - * @param start The starting timestamp. - * @param stop The end timestamp. - * - * @return Elapsed time in microsecond. - * - * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec() - */ -PJ_DECL(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, - const pj_timestamp *stop ); - -/** - * Calculate the elapsed time in 32-bit nanoseconds. - * This function calculates the elapsed time using highest precision - * calculation that is available for current platform, considering - * whether floating point or 64-bit precision arithmetic is available. - * For maximum portability, application should prefer to use this function - * rather than calculating the elapsed time by itself. - * - * @param start The starting timestamp. - * @param stop The end timestamp. - * - * @return Elapsed time in nanoseconds. - * - * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec() - */ -PJ_DECL(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, - const pj_timestamp *stop ); - -/** - * Calculate the elapsed time in 32-bit cycles. - * This function calculates the elapsed time using highest precision - * calculation that is available for current platform, considering - * whether floating point or 64-bit precision arithmetic is available. - * For maximum portability, application should prefer to use this function - * rather than calculating the elapsed time by itself. - * - * @param start The starting timestamp. - * @param stop The end timestamp. - * - * @return Elapsed time in cycles. - * - * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec() - */ -PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, - const pj_timestamp *stop ); - - -#endif /* PJ_HAS_HIGH_RES_TIMER */ - -/** @} */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * Internal PJLIB function to initialize the threading subsystem. - * @return PJ_SUCCESS or the appropriate error code. - */ -pj_status_t pj_thread_init(void); - - -PJ_END_DECL - -#endif /* __PJ_OS_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/os.h 12 10/29/05 11:30a Bennylp $ */ + +#ifndef __PJ_OS_H__ +#define __PJ_OS_H__ + +/** + * @file os.h + * @brief OS dependent functions + */ +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_OS Operating System Dependent Functionality. + * @ingroup PJ + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_THREAD Threads + * @ingroup PJ_OS + * @{ + * This module provides multithreading API. + * + * \section pj_thread_examples_sec Examples + * + * For examples, please see: + * - \ref page_pjlib_thread_test + * - \ref page_pjlib_sleep_test + * + */ + +/** + * Thread creation flags: + * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended. + */ +typedef enum pj_thread_create_flags +{ + PJ_THREAD_SUSPENDED = 1 +} pj_thread_create_flags; + + +/** + * Specify this as \a stack_size argument in #pj_thread_create() to specify + * that thread should use default stack size for the current platform. + */ +#define PJ_THREAD_DEFAULT_STACK_SIZE 0 + +/** + * Type of thread entry function. + */ +typedef int (PJ_THREAD_FUNC pj_thread_proc)(void*); + +/** + * Size of thread struct. + */ +#if !defined(PJ_THREAD_DESC_SIZE) +# define PJ_THREAD_DESC_SIZE (16) +#endif + +/** + * Thread structure, to thread's state when the thread is created by external + * or native API. + */ +typedef long pj_thread_desc[PJ_THREAD_DESC_SIZE]; + +/** + * Get process ID. + * @return process ID. + */ +PJ_DECL(pj_uint32_t) pj_getpid(void); + +/** + * Create a new thread. + * + * @param pool The memory pool from which the thread record + * will be allocated from. + * @param thread_name The optional name to be assigned to the thread. + * @param proc Thread entry function. + * @param arg Argument to be passed to the thread entry function. + * @param stack_size The size of the stack for the new thread, or ZERO or + * PJ_THREAD_DEFAULT_STACK_SIZE to let the + * library choose the reasonable size for the stack. + * For some systems, the stack will be allocated from + * the pool, so the pool must have suitable capacity. + * @param flags Flags for thread creation, which is bitmask combination + * from enum pj_thread_create_flags. + * @param thread Pointer to hold the newly created thread. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **thread ); + +/** + * Register a thread that was created by external or native API to PJLIB. + * This function must be called in the context of the thread being registered. + * When the thread is created by external function or API call, + * it must be 'registered' to PJLIB using pj_thread_register(), so that it can + * cooperate with PJLIB's framework. During registration, some data needs to + * be maintained, and this data must remain available during the thread's + * lifetime. + * + * @param thread_name The optional name to be assigned to the thread. + * @param desc Thread descriptor, which must be available throughout + * the lifetime of the thread. + * @param thread Pointer to hold the created thread handle. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_thread_register ( const char *thread_name, + pj_thread_desc desc, + pj_thread_t **thread); + +/** + * Get thread name. + * + * @param thread The thread handle. + * + * @return Thread name as null terminated string. + */ +PJ_DECL(const char*) pj_thread_get_name(pj_thread_t *thread); + +/** + * Resume a suspended thread. + * + * @param thread The thread handle. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread); + +/** + * Get the current thread. + * + * @return Thread handle of current thread. + */ +PJ_DECL(pj_thread_t*) pj_thread_this(void); + +/** + * Join thread. + * This function will block the caller thread until the specified thread exits. + * + * @param thread The thread handle. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread); + + +/** + * Destroy thread and release resources allocated for the thread. + * However, the memory allocated for the pj_thread_t itself will only be released + * when the pool used to create the thread is destroyed. + * + * @param thread The thread handle. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread); + + +/** + * Put the current thread to sleep for the specified miliseconds. + * + * @param msec Miliseconds delay. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec); + +/** + * @def PJ_CHECK_STACK() + * PJ_CHECK_STACK() macro is used to check the sanity of the stack. + * The OS implementation may check that no stack overflow occurs, and + * it also may collect statistic about stack usage. + */ +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + +# define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__) + +/** @internal + * The implementation of stack checking. + */ +PJ_DECL(void) pj_thread_check_stack(const char *file, int line); + +/** @internal + * Get maximum stack usage statistic. + */ +PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread); + +/** @internal + * Dump thread stack status. + */ +PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, + const char **file, + int *line); +#else + +# define PJ_CHECK_STACK() +/** pj_thread_get_stack_max_usage() for the thread */ +# define pj_thread_get_stack_max_usage(thread) 0 +/** pj_thread_get_stack_info() for the thread */ +# define pj_thread_get_stack_info(thread,f,l) (*(f)="",*(l)=0) +#endif /* PJ_OS_HAS_CHECK_STACK */ + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_TLS Thread Local Storage. + * @ingroup PJ_OS + * @{ + */ + +/** + * Allocate thread local storage index. The initial value of the variable at + * the index is zero. + * + * @param index Pointer to hold the return value. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index); + +/** + * Deallocate thread local variable. + * + * @param index The variable index. + */ +PJ_DECL(void) pj_thread_local_free(long index); + +/** + * Set the value of thread local variable. + * + * @param index The index of the variable. + * @param value The value. + */ +PJ_DECL(pj_status_t) pj_thread_local_set(long index, void *value); + +/** + * Get the value of thread local variable. + * + * @param index The index of the variable. + * @return The value. + */ +PJ_DECL(void*) pj_thread_local_get(long index); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_ATOMIC Atomic Variables + * @ingroup PJ_OS + * @{ + * + * This module provides API to manipulate atomic variables. + * + * \section pj_atomic_examples_sec Examples + * + * For some example codes, please see: + * - @ref page_pjlib_atomic_test + */ + + +/** + * Create atomic variable. + * + * @param pool The pool. + * @param initial The initial value of the atomic variable. + * @param atomic Pointer to hold the atomic variable upon return. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **atomic ); + +/** + * Destroy atomic variable. + * + * @param atomic_var the atomic variable. + * + * @return PJ_SUCCESS if success. + */ +PJ_DECL(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ); + +/** + * Set the value of an atomic type, and return the previous value. + * + * @param atomic_var the atomic variable. + * @param value value to be set to the variable. + * + * @return the previous value of the variable. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *atomic_var, + pj_atomic_value_t value); + +/** + * Get the value of an atomic type. + * + * @param atomic_var the atomic variable. + * + * @return the value of the atomic variable. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var); + +/** + * Increment the value of an atomic type. + * + * @param atomic_var the atomic variable. + * + * @return the result. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *atomic_var); + +/** + * Decrement the value of an atomic type. + * + * @param atomic_var the atomic variable. + * + * @return the result. + */ +PJ_DECL(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *atomic_var); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_MUTEX Mutexes. + * @ingroup PJ_OS + * @{ + * + * Mutex manipulation. Alternatively, application can use higher abstraction + * for lock objects, which provides uniform API for all kinds of lock + * mechanisms, including mutex. See @ref PJ_LOCK for more information. + */ + +/** + * Mutex types: + * - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent. + * - PJ_MUTEX_SIMPLE: non-recursive mutex. + * - PJ_MUTEX_RECURSIVE: recursive mutex. + */ +typedef enum pj_mutex_type_e +{ + PJ_MUTEX_DEFAULT, + PJ_MUTEX_SIMPLE, + PJ_MUTEX_RECURSE, +} pj_mutex_type_e; + + +/** + * Create mutex of the specified type. + * + * @param pool The pool. + * @param name Name to be associated with the mutex (for debugging). + * @param type The type of the mutex, of type #pj_mutex_type_e. + * @param mutex Pointer to hold the returned mutex instance. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **mutex); + +/** + * Create simple, non-recursive mutex. + * This function is a simple wrapper for #pj_mutex_create to create + * non-recursive mutex. + * + * @param pool The pool. + * @param name Mutex name. + * @param mutex Pointer to hold the returned mutex instance. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, + pj_mutex_t **mutex ); + +/** + * Create recursive mutex. + * This function is a simple wrapper for #pj_mutex_create to create + * recursive mutex. + * + * @param pool The pool. + * @param name Mutex name. + * @param mutex Pointer to hold the returned mutex instance. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ); + +/** + * Acquire mutex lock. + * + * @param mutex The mutex. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex); + +/** + * Release mutex lock. + * + * @param mutex The mutex. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex); + +/** + * Try to acquire mutex lock. + * + * @param mutex The mutex. + * @return PJ_SUCCESS on success, or the error code if the + * lock couldn't be acquired. + */ +PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex); + +/** + * Destroy mutex. + * + * @param mutex Te mutex. + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex); + +/** + * Determine whether calling thread is owning the mutex (only available when + * PJ_DEBUG is set). + * @param mutex The mutex. + * @return Non-zero if yes. + */ +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 + PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex); +#else +# define pj_mutex_is_locked(mutex) 1 +#endif + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_CRIT_SEC Critical sections. + * @ingroup PJ_OS + * @{ + * Critical section protection can be used to protect regions where: + * - mutual exclusion protection is needed. + * - it's rather too expensive to create a mutex. + * - the time spent in the region is very very brief. + * + * Critical section is a global object, and it prevents any threads from + * entering any regions that are protected by critical section once a thread + * is already in the section. + * + * Critial section is \a not recursive! + * + * Application MUST NOT call any functions that may cause current + * thread to block (such as allocating memory, performing I/O, locking mutex, + * etc.) while holding the critical section. + */ +/** + * Enter critical section. + */ +PJ_DECL(void) pj_enter_critical_section(void); + +/** + * Leave critical section. + */ +PJ_DECL(void) pj_leave_critical_section(void); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +/** + * @defgroup PJ_SEM Semaphores. + * @ingroup PJ_OS + * @{ + * + * This module provides abstraction for semaphores, where available. + */ + +/** + * Create semaphore. + * + * @param pool The pool. + * @param name Name to be assigned to the semaphore (for logging purpose) + * @param initial The initial count of the semaphore. + * @param max The maximum count of the semaphore. + * @param sem Pointer to hold the semaphore created. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem); + +/** + * Wait for semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem); + +/** + * Try wait for semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem); + +/** + * Release semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem); + +/** + * Destroy semaphore. + * + * @param sem The semaphore. + * + * @return PJ_SUCCESS on success, or the error code. + */ +PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem); + +/** + * @} + */ +#endif /* PJ_HAS_SEMAPHORE */ + + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 +/** + * @defgroup PJ_EVENT Event Object. + * @ingroup PJ_OS + * @{ + * + * This module provides abstraction to event object (e.g. Win32 Event) where + * available. Event objects can be used for synchronization among threads. + */ + +/** + * Create event object. + * + * @param pool The pool. + * @param name The name of the event object (for logging purpose). + * @param manual_reset Specify whether the event is manual-reset + * @param initial Specify the initial state of the event object. + * @param event Pointer to hold the returned event object. + * + * @return event handle, or NULL if failed. + */ +PJ_DECL(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, + pj_bool_t manual_reset, pj_bool_t initial, + pj_event_t **event); + +/** + * Wait for event to be signaled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event); + +/** + * Try wait for event object to be signalled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event); + +/** + * Set the event object state to signaled. For auto-reset event, this + * will only release the first thread that are waiting on the event. For + * manual reset event, the state remains signaled until the event is reset. + * If there is no thread waiting on the event, the event object state + * remains signaled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event); + +/** + * Set the event object to signaled state to release appropriate number of + * waiting threads and then reset the event object to non-signaled. For + * manual-reset event, this function will release all waiting threads. For + * auto-reset event, this function will only release one waiting thread. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event); + +/** + * Set the event object state to non-signaled. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event); + +/** + * Destroy the event object. + * + * @param event The event object. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event); + +/** + * @} + */ +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJ_TIME Time Data Type and Manipulation. + * @ingroup PJ_OS + * @{ + * This module provides API for manipulating time. + * + * \section pj_time_examples_sec Examples + * + * For examples, please see: + * - \ref page_pjlib_sleep_test + */ + +/** + * Get current time of day in local representation. + * + * @param tv Variable to store the result. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv); + + +/** + * Parse time value into date/time representation. + * + * @param tv The time. + * @param pt Variable to store the date time result. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt); + +/** + * Encode date/time to time value. + * + * @param pt The date/time. + * @param tv Variable to store time value result. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); + +/** + * Convert local time to GMT. + * + * @param tv Time to convert. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + * + * @param tv Time to convert. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + +/** + * @defgroup PJ_TERM Terminal + * @ingroup PJ_OS + * @{ + */ + +/** + * Set current terminal color. + * + * @param color The RGB color. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color); + +/** + * Get current terminal foreground color. + * + * @return RGB color. + */ +PJ_DECL(pj_color_t) pj_term_get_color(void); + +/** + * @} + */ + +#endif /* PJ_TERM_HAS_COLOR */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_TIMESTAMP High Resolution Timestamp + * @ingroup PJ_OS + * @{ + * + * PJLIB provides High Resolution Timestamp API to access highest + * resolution timestamp value provided by the platform. The API is usefull + * to measure precise elapsed time, and can be used in applications such + * as profiling. + * + * The timestamp value is represented in cycles, and can be related to + * normal time (in seconds or sub-seconds) using various functions provided. + * + * \section pj_timestamp_examples_sec Examples + * + * For examples, please see: + * - \ref page_pjlib_sleep_test + * - \ref page_pjlib_timestamp_test + */ + +/* + * High resolution timer. + */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + +/** + * This structure represents high resolution (64bit) time value. The time + * values represent time in cycles, which is retrieved by calling + * #pj_get_timestamp(). + */ +typedef union pj_timestamp +{ + struct + { +#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN!=0 + pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */ + pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */ +#else + pj_uint32_t hi; /**< high 32-bit value of the 64-bit value. */ + pj_uint32_t lo; /**< Low 32-bit value of the 64-bit value. */ +#endif + } u32; /**< The 64-bit value as two 32-bit values. */ + +#if PJ_HAS_INT64 + pj_uint64_t u64; /**< The whole 64-bit value, where available. */ +#endif +} pj_timestamp; + + +/** + * Acquire high resolution timer value. The time value are stored + * in cycles. + * + * @param ts High resolution timer value. + * @return PJ_SUCCESS or the appropriate error code. + * + * @see pj_get_timestamp_freq(). + */ +PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts); + +/** + * Get high resolution timer frequency, in cycles per second. + * + * @param freq Timer frequency, in cycles per second. + * @return PJ_SUCCESS or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq); + +/** + * Calculate the elapsed time, and store it in pj_time_val. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time as #pj_time_val. + * + * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec() + */ +PJ_DECL(pj_time_val) pj_elapsed_time( const pj_timestamp *start, + const pj_timestamp *stop ); + +/** + * Calculate the elapsed time in 32-bit microseconds. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time in microsecond. + * + * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec() + */ +PJ_DECL(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ); + +/** + * Calculate the elapsed time in 32-bit nanoseconds. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time in nanoseconds. + * + * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec() + */ +PJ_DECL(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, + const pj_timestamp *stop ); + +/** + * Calculate the elapsed time in 32-bit cycles. + * This function calculates the elapsed time using highest precision + * calculation that is available for current platform, considering + * whether floating point or 64-bit precision arithmetic is available. + * For maximum portability, application should prefer to use this function + * rather than calculating the elapsed time by itself. + * + * @param start The starting timestamp. + * @param stop The end timestamp. + * + * @return Elapsed time in cycles. + * + * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec() + */ +PJ_DECL(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, + const pj_timestamp *stop ); + + +#endif /* PJ_HAS_HIGH_RES_TIMER */ + +/** @} */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * Internal PJLIB function to initialize the threading subsystem. + * @return PJ_SUCCESS or the appropriate error code. + */ +pj_status_t pj_thread_init(void); + + +PJ_END_DECL + +#endif /* __PJ_OS_H__ */ + diff --git a/pjlib/include/pj/pool.h b/pjlib/include/pj/pool.h index 78ed45b47..270ae71ee 100644 --- a/pjlib/include/pj/pool.h +++ b/pjlib/include/pj/pool.h @@ -1,570 +1,570 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/pool.h 10 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_POOL_H__ -#define __PJ_POOL_H__ - -/** - * @file pool.h - * @brief Memory Pool. - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_POOL_GROUP Memory Pool Management - * @ingroup PJ - * @brief - * Memory pool management provides API to allocate and deallocate memory from - * memory pool and to manage and establish policy for pool creation and - * destruction in pool factory. - * - * \section PJ_POOL_FACTORY_SEC Pool Factory - * See: \ref PJ_POOL_FACTORY "Pool Factory" - * - * A memory pool must be created through a factory. A factory not only provides - * generic interface functions to create and release pool, but also provides - * strategy to manage the life time of pools. One sample implementation, - * \a pj_caching_pool, can be set to keep the pools released by application for - * future use as long as the total memory is below the limit. - * - * The pool factory interface declared in PJLIB is designed to be extensible. - * Application can define its own strategy by creating it's own pool factory - * implementation, and this strategy can be used even by existing library - * without recompilation. - * - * - * \section PJ_POOL_POLICY_SEC Pool Factory Policy - * See: \ref PJ_POOL_FACTORY "Pool Factory Policy" - * - * A pool factory only defines functions to create and release pool and how - * to manage pools, but the rest of the functionalities are controlled by - * policy. A pool policy defines: - * - how memory block is allocated and deallocated (the default implementation - * allocates and deallocate memory by calling malloc() and free()). - * - callback to be called when memory allocation inside a pool fails (the - * default implementation will throw PJ_NO_MEMORY_EXCEPTION exception). - * - concurrency when creating and releasing pool from/to the factory. - * - * A pool factory can be given different policy during creation to make - * it behave differently. For example, caching pool factory can be configured - * to allocate and deallocate from a static/contiguous/preallocated memory - * instead of using malloc()/free(). - * - * What strategy/factory and what policy to use is not defined by PJLIB, but - * instead is left to application to make use whichever is most efficient for - * itself. - * - * - * \section PJ_POOL_POOL_SEC The Pool - * See: \ref PJ_POOL "Pool" - * - * The memory pool is an opaque object created by pool factory. - * Application uses this object to request a memory chunk, by calling - * #pj_pool_alloc or #pj_pool_calloc. When the application has finished using - * the pool, it must call #pj_pool_release to free all the chunks previously - * allocated and release the pool back to the factory. - * - * \section PJ_POOL_THREADING_SEC More on Threading Policies: - * - By design, memory allocation from a pool is not thread safe. We assumed - * that a pool will be owned by an object, and thread safety should be - * handled by that object. Thus these functions are not thread safe: - * - #pj_pool_alloc, - * - #pj_pool_calloc, - * - and other pool statistic functions. - * - Threading in the pool factory is decided by the policy set for the - * factory when it was created. - * - * \section PJ_POOL_EXAMPLES_SEC Examples - * - * For some sample codes on how to use the pool, please see: - * - @ref page_pjlib_pool_test - */ - -/** - * @defgroup PJ_POOL Memory Pool. - * @ingroup PJ_POOL_GROUP - * @brief - * A memory pool is initialized with an initial amount of memory, which is - * called a block. Pool can be configured to dynamically allocate more memory - * blocks when it runs out of memory. Subsequent memory allocations by user - * will use up portions of these block. - * The pool doesn't keep track of individual memory allocations - * by user, and the user doesn't have to free these indidual allocations. This - * makes memory allocation simple and very fast. All the memory allocated from - * the pool will be destroyed when the pool itself is destroyed. - * @{ - */ - -/** - * The type for function to receive callback from the pool when it is unable - * to allocate memory. The elegant way to handle this condition is to throw - * exception, and this is what is expected by most of this library - * components. - */ -typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size); - -/** - * This class, which is used internally by the pool, describes a single - * block of memory from which user memory allocations will be allocated from. - */ -typedef struct pj_pool_block -{ - PJ_DECL_LIST_MEMBER(struct pj_pool_block) /**< List's prev and next. */ - unsigned char *buf; /**< Start of buffer. */ - unsigned char *cur; /**< Current alloc ptr. */ - unsigned char *end; /**< End of buffer. */ -} pj_pool_block; - - -/** - * This structure describes the memory pool. Only implementors of pool factory - * need to care about the contents of this structure. - */ -struct pj_pool_t -{ - PJ_DECL_LIST_MEMBER(struct pj_pool_t) - - /** Pool name */ - char obj_name[PJ_MAX_OBJ_NAME]; - - /** Pool factory. */ - pj_pool_factory *factory; - - /** Current capacity allocated by the pool. */ - pj_size_t capacity; - - /** Number of memory used/allocated. */ - pj_size_t used_size; - - /** Size of memory block to be allocated when the pool runs out of memory */ - pj_size_t increment_size; - - /** List of memory blocks allcoated by the pool. */ - pj_pool_block block_list; - - /** The callback to be called when the pool is unable to allocate memory. */ - pj_pool_callback *callback; - -}; - - -/** - * Guidance on how much memory required for initial pool administrative data. - */ -#define PJ_POOL_SIZE (sizeof(struct pj_pool_t)) - -/** - * Pool memory alignment (must be power of 2). - */ -#ifndef PJ_POOL_ALIGNMENT -# define PJ_POOL_ALIGNMENT 4 -#endif - -/** - * Create a new pool from the pool factory. This wrapper will call create_pool - * member of the pool factory. - * - * @param factory The pool factory. - * @param name The name to be assigned to the pool. The name should - * not be longer than PJ_MAX_OBJ_NAME (32 chars), or - * otherwise it will be truncated. - * @param initial_size The size of initial memory blocks taken by the pool. - * Note that the pool will take 68+20 bytes for - * administrative area from this block. - * @param increment_size the size of each additional blocks to be allocated - * when the pool is running out of memory. If user - * requests memory which is larger than this size, then - * an error occurs. - * Note that each time a pool allocates additional block, - * it needs PJ_POOL_SIZE more to store some - * administrative info. - * @param callback Callback to be called when error occurs in the pool. - * If this value is NULL, then the callback from pool - * factory policy will be used. - * Note that when an error occurs during pool creation, - * the callback itself is not called. Instead, NULL - * will be returned. - * - * @return The memory pool, or NULL. - */ -PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory, - const char *name, - pj_size_t initial_size, - pj_size_t increment_size, - pj_pool_callback *callback); - -/** - * Release the pool back to pool factory. - * - * @param pool Memory pool. - */ -PJ_IDECL(void) pj_pool_release( pj_pool_t *pool ); - -/** - * Get pool object name. - * - * @param pool the pool. - * - * @return pool name as NULL terminated string. - */ -PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool ); - -/** - * Reset the pool to its state when it was initialized. - * This means that if additional blocks have been allocated during runtime, - * then they will be freed. Only the original block allocated during - * initialization is retained. This function will also reset the internal - * counters, such as pool capacity and used size. - * - * @param pool the pool. - */ -PJ_DECL(void) pj_pool_reset( pj_pool_t *pool ); - - -/** - * Get the pool capacity, that is, the system storage that have been allocated - * by the pool, and have been used/will be used to allocate user requests. - * There's no guarantee that the returned value represent a single - * contiguous block, because the capacity may be spread in several blocks. - * - * @param pool the pool. - * - * @return the capacity. - */ -PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ); - -/** - * Get the total size of user allocation request. - * - * @param pool the pool. - * - * @return the total size. - */ -PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ); - -/** - * Allocate storage with the specified size from the pool. - * If there's no storage available in the pool, then the pool can allocate more - * blocks if the increment size is larger than the requested size. - * - * @param pool the pool. - * @param size the requested size. - * - * @return pointer to the allocated memory. - */ -PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size); - -/** - * Allocate storage from the pool, and initialize it to zero. - * This function behaves like pj_pool_alloc(), except that the storage will - * be initialized to zero. - * - * @param pool the pool. - * @param count the number of elements in the array. - * @param elem the size of individual element. - * - * @return pointer to the allocated memory. - */ -PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, - pj_size_t elem); - - -/** - * @def pj_pool_zalloc(pj_pool_t *pool, pj_size_t size) - * Allocate storage from the pool and initialize it to zero. - * - * @param pool The pool. - * @param size The size to be allocated. - * - * @return Pointer to the allocated memory. - */ -#define pj_pool_zalloc(pool, size) pj_pool_calloc(pool, 1, size) - - -/** - * @} // PJ_POOL - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJ_POOL_FACTORY Pool Factory and Policy. - * @ingroup PJ_POOL_GROUP - * @brief - * Pool factory declares an interface to create and destroy pool. There may - * be several strategies for pool creation, and these strategies should - * implement the interface defined by pool factory. - * - * \section PJ_POOL_FACTORY_ITF Pool Factory Interface - * The pool factory defines the following interface: - * - \a policy: the memory pool factory policy. - * - \a create_pool(): create a new memory pool. - * - \a release_pool(): release memory pool back to factory. - * - * \section PJ_POOL_FACTORY_POL Pool Factory Policy. - * The pool factory policy controls the behaviour of memory factories, and - * defines the following interface: - * - \a block_alloc(): allocate memory block from backend memory mgmt/system. - * - \a block_free(): free memory block back to backend memory mgmt/system. - * @{ - */ - -/* We unfortunately don't have support for factory policy options as now, - so we keep this commented at the moment. -enum PJ_POOL_FACTORY_OPTION -{ - PJ_POOL_FACTORY_SERIALIZE = 1 -}; -*/ - -/** - * This structure declares pool factory interface. - */ -typedef struct pj_pool_factory_policy -{ - /** - * Allocate memory block (for use by pool). This function is called - * by memory pool to allocate memory block. - * - * @param factory Pool factory. - * @param size The size of memory block to allocate. - * - * @return Memory block. - */ - void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size); - - /** - * Free memory block. - * - * @param factory Pool factory. - * @param mem Memory block previously allocated by block_alloc(). - * @param size The size of memory block. - */ - void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size); - - /** - * Default callback to be called when memory allocation fails. - */ - pj_pool_callback *callback; - - /** - * Option flags. - */ - unsigned flags; - -} pj_pool_factory_policy; - -/** - * This constant denotes the exception number that will be thrown by default - * memory factory policy when memory allocation fails. - */ -extern int PJ_NO_MEMORY_EXCEPTION; - -/** - * This global variable points to default memory pool factory policy. - * The behaviour of the default policy is: - * - block allocation and deallocation use malloc() and free(). - * - callback will raise PJ_NO_MEMORY_EXCEPTION exception. - * - access to pool factory is not serialized (i.e. not thread safe). - */ -extern pj_pool_factory_policy pj_pool_factory_default_policy; - -/** - * This structure contains the declaration for pool factory interface. - */ -struct pj_pool_factory -{ - /** - * Memory pool policy. - */ - pj_pool_factory_policy policy; - - /** - * Create a new pool from the pool factory. - * - * @param factory The pool factory. - * @param name the name to be assigned to the pool. The name should - * not be longer than PJ_MAX_OBJ_NAME (32 chars), or - * otherwise it will be truncated. - * @param initial_size the size of initial memory blocks taken by the pool. - * Note that the pool will take 68+20 bytes for - * administrative area from this block. - * @param increment_size the size of each additional blocks to be allocated - * when the pool is running out of memory. If user - * requests memory which is larger than this size, then - * an error occurs. - * Note that each time a pool allocates additional block, - * it needs 20 bytes (equal to sizeof(pj_pool_block)) to - * store some administrative info. - * @param callback Cllback to be called when error occurs in the pool. - * Note that when an error occurs during pool creation, - * the callback itself is not called. Instead, NULL - * will be returned. - * - * @return the memory pool, or NULL. - */ - pj_pool_t* (*create_pool)( pj_pool_factory *factory, - const char *name, - pj_size_t initial_size, - pj_size_t increment_size, - pj_pool_callback *callback); - - /** - * Release the pool to the pool factory. - * - * @param factory The pool factory. - * @param pool The pool to be released. - */ - void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool ); - - /** - * Dump pool status to log. - * - * @param factory The pool factory. - */ - void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail ); -}; - -/** - * This function is intended to be used by pool factory implementors. - * @param factory Pool factory. - * @param name Pool name. - * @param initial_size Initial size. - * @param increment_size Increment size. - * @param callback Callback. - * @return The pool object, or NULL. - */ -PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory, - const char *name, - pj_size_t initial_size, - pj_size_t increment_size, - pj_pool_callback *callback); - -/** - * This function is intended to be used by pool factory implementors. - * @param pool The pool. - * @param name Pool name. - * @param increment_size Increment size. - * @param callback Callback function. - */ -PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool, - const char *name, - pj_size_t increment_size, - pj_pool_callback *callback); - -/** - * This function is intended to be used by pool factory implementors. - * @param pool The memory pool. - */ -PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool ); - - -/** - * @} // PJ_POOL_FACTORY - */ - -/////////////////////////////////////////////////////////////////////////////// - -/** - * @defgroup PJ_CACHING_POOL Caching Pool Factory. - * @ingroup PJ_POOL_GROUP - * @brief - * Caching pool is one sample implementation of pool factory where the - * factory can reuse memory to create a pool. Application defines what the - * maximum memory the factory can hold, and when a pool is released the - * factory decides whether to destroy the pool or to keep it for future use. - * If the total amount of memory in the internal cache is still within the - * limit, the factory will keep the pool in the internal cache, otherwise the - * pool will be destroyed, thus releasing the memory back to the system. - * - * @{ - */ - -/** - * Number of unique sizes, to be used as index to the free list. - * Each pool in the free list is organized by it's size. - */ -#define PJ_CACHING_POOL_ARRAY_SIZE 16 - -/** - * Declaration for caching pool. Application doesn't normally need to - * care about the contents of this struct, it is only provided here because - * application need to define an instance of this struct (we can not allocate - * the struct from a pool since there is no pool factory yet!). - */ -struct pj_caching_pool -{ - /** Pool factory interface, must be declared first. */ - pj_pool_factory factory; - - /** Current factory's capacity, i.e. number of bytes that are allocated - * and available for application in this factory. The factory's - * capacity represents the size of all pools kept by this factory - * in it's free list, which will be returned to application when it - * requests to create a new pool. - */ - pj_size_t capacity; - - /** Maximum size that can be held by this factory. Once the capacity - * has exceeded @a max_capacity, further #pj_pool_release() will - * flush the pool. If the capacity is still below the @a max_capacity, - * #pj_pool_release() will save the pool to the factory's free list. - */ - pj_size_t max_capacity; - - /** - * Number of pools currently held by applications. This number gets - * incremented everytime #pj_pool_create() is called, and gets - * decremented when #pj_pool_release() is called. - */ - pj_size_t used_count; - - /** - * Lists of pools in the cache, indexed by pool size. - */ - pj_list free_list[PJ_CACHING_POOL_ARRAY_SIZE]; - - /** - * List of pools currently allocated by applications. - */ - pj_list used_list; -}; - - - -/** - * Initialize caching pool. - * - * @param ch_pool The caching pool factory to be initialized. - * @param policy Pool factory policy. - * @param max_capacity The total capacity to be retained in the cache. When - * the pool is returned to the cache, it will be kept in - * recycling list if the total capacity of pools in this - * list plus the capacity of the pool is still below this - * value. - */ -PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool, - const pj_pool_factory_policy *policy, - pj_size_t max_capacity); - - -/** - * Destroy caching pool, and release all the pools in the recycling list. - * - * @param ch_pool The caching pool. - */ -PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool ); - -/** - * @} // PJ_CACHING_POOL - */ - -# if PJ_FUNCTIONS_ARE_INLINED -# include "pool_i.h" -# endif - -PJ_END_DECL - -#endif /* __PJ_POOL_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/pool.h 10 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_POOL_H__ +#define __PJ_POOL_H__ + +/** + * @file pool.h + * @brief Memory Pool. + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_POOL_GROUP Memory Pool Management + * @ingroup PJ + * @brief + * Memory pool management provides API to allocate and deallocate memory from + * memory pool and to manage and establish policy for pool creation and + * destruction in pool factory. + * + * \section PJ_POOL_FACTORY_SEC Pool Factory + * See: \ref PJ_POOL_FACTORY "Pool Factory" + * + * A memory pool must be created through a factory. A factory not only provides + * generic interface functions to create and release pool, but also provides + * strategy to manage the life time of pools. One sample implementation, + * \a pj_caching_pool, can be set to keep the pools released by application for + * future use as long as the total memory is below the limit. + * + * The pool factory interface declared in PJLIB is designed to be extensible. + * Application can define its own strategy by creating it's own pool factory + * implementation, and this strategy can be used even by existing library + * without recompilation. + * + * + * \section PJ_POOL_POLICY_SEC Pool Factory Policy + * See: \ref PJ_POOL_FACTORY "Pool Factory Policy" + * + * A pool factory only defines functions to create and release pool and how + * to manage pools, but the rest of the functionalities are controlled by + * policy. A pool policy defines: + * - how memory block is allocated and deallocated (the default implementation + * allocates and deallocate memory by calling malloc() and free()). + * - callback to be called when memory allocation inside a pool fails (the + * default implementation will throw PJ_NO_MEMORY_EXCEPTION exception). + * - concurrency when creating and releasing pool from/to the factory. + * + * A pool factory can be given different policy during creation to make + * it behave differently. For example, caching pool factory can be configured + * to allocate and deallocate from a static/contiguous/preallocated memory + * instead of using malloc()/free(). + * + * What strategy/factory and what policy to use is not defined by PJLIB, but + * instead is left to application to make use whichever is most efficient for + * itself. + * + * + * \section PJ_POOL_POOL_SEC The Pool + * See: \ref PJ_POOL "Pool" + * + * The memory pool is an opaque object created by pool factory. + * Application uses this object to request a memory chunk, by calling + * #pj_pool_alloc or #pj_pool_calloc. When the application has finished using + * the pool, it must call #pj_pool_release to free all the chunks previously + * allocated and release the pool back to the factory. + * + * \section PJ_POOL_THREADING_SEC More on Threading Policies: + * - By design, memory allocation from a pool is not thread safe. We assumed + * that a pool will be owned by an object, and thread safety should be + * handled by that object. Thus these functions are not thread safe: + * - #pj_pool_alloc, + * - #pj_pool_calloc, + * - and other pool statistic functions. + * - Threading in the pool factory is decided by the policy set for the + * factory when it was created. + * + * \section PJ_POOL_EXAMPLES_SEC Examples + * + * For some sample codes on how to use the pool, please see: + * - @ref page_pjlib_pool_test + */ + +/** + * @defgroup PJ_POOL Memory Pool. + * @ingroup PJ_POOL_GROUP + * @brief + * A memory pool is initialized with an initial amount of memory, which is + * called a block. Pool can be configured to dynamically allocate more memory + * blocks when it runs out of memory. Subsequent memory allocations by user + * will use up portions of these block. + * The pool doesn't keep track of individual memory allocations + * by user, and the user doesn't have to free these indidual allocations. This + * makes memory allocation simple and very fast. All the memory allocated from + * the pool will be destroyed when the pool itself is destroyed. + * @{ + */ + +/** + * The type for function to receive callback from the pool when it is unable + * to allocate memory. The elegant way to handle this condition is to throw + * exception, and this is what is expected by most of this library + * components. + */ +typedef void pj_pool_callback(pj_pool_t *pool, pj_size_t size); + +/** + * This class, which is used internally by the pool, describes a single + * block of memory from which user memory allocations will be allocated from. + */ +typedef struct pj_pool_block +{ + PJ_DECL_LIST_MEMBER(struct pj_pool_block) /**< List's prev and next. */ + unsigned char *buf; /**< Start of buffer. */ + unsigned char *cur; /**< Current alloc ptr. */ + unsigned char *end; /**< End of buffer. */ +} pj_pool_block; + + +/** + * This structure describes the memory pool. Only implementors of pool factory + * need to care about the contents of this structure. + */ +struct pj_pool_t +{ + PJ_DECL_LIST_MEMBER(struct pj_pool_t) + + /** Pool name */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** Pool factory. */ + pj_pool_factory *factory; + + /** Current capacity allocated by the pool. */ + pj_size_t capacity; + + /** Number of memory used/allocated. */ + pj_size_t used_size; + + /** Size of memory block to be allocated when the pool runs out of memory */ + pj_size_t increment_size; + + /** List of memory blocks allcoated by the pool. */ + pj_pool_block block_list; + + /** The callback to be called when the pool is unable to allocate memory. */ + pj_pool_callback *callback; + +}; + + +/** + * Guidance on how much memory required for initial pool administrative data. + */ +#define PJ_POOL_SIZE (sizeof(struct pj_pool_t)) + +/** + * Pool memory alignment (must be power of 2). + */ +#ifndef PJ_POOL_ALIGNMENT +# define PJ_POOL_ALIGNMENT 4 +#endif + +/** + * Create a new pool from the pool factory. This wrapper will call create_pool + * member of the pool factory. + * + * @param factory The pool factory. + * @param name The name to be assigned to the pool. The name should + * not be longer than PJ_MAX_OBJ_NAME (32 chars), or + * otherwise it will be truncated. + * @param initial_size The size of initial memory blocks taken by the pool. + * Note that the pool will take 68+20 bytes for + * administrative area from this block. + * @param increment_size the size of each additional blocks to be allocated + * when the pool is running out of memory. If user + * requests memory which is larger than this size, then + * an error occurs. + * Note that each time a pool allocates additional block, + * it needs PJ_POOL_SIZE more to store some + * administrative info. + * @param callback Callback to be called when error occurs in the pool. + * If this value is NULL, then the callback from pool + * factory policy will be used. + * Note that when an error occurs during pool creation, + * the callback itself is not called. Instead, NULL + * will be returned. + * + * @return The memory pool, or NULL. + */ +PJ_IDECL(pj_pool_t*) pj_pool_create(pj_pool_factory *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback); + +/** + * Release the pool back to pool factory. + * + * @param pool Memory pool. + */ +PJ_IDECL(void) pj_pool_release( pj_pool_t *pool ); + +/** + * Get pool object name. + * + * @param pool the pool. + * + * @return pool name as NULL terminated string. + */ +PJ_IDECL(const char *) pj_pool_getobjname( const pj_pool_t *pool ); + +/** + * Reset the pool to its state when it was initialized. + * This means that if additional blocks have been allocated during runtime, + * then they will be freed. Only the original block allocated during + * initialization is retained. This function will also reset the internal + * counters, such as pool capacity and used size. + * + * @param pool the pool. + */ +PJ_DECL(void) pj_pool_reset( pj_pool_t *pool ); + + +/** + * Get the pool capacity, that is, the system storage that have been allocated + * by the pool, and have been used/will be used to allocate user requests. + * There's no guarantee that the returned value represent a single + * contiguous block, because the capacity may be spread in several blocks. + * + * @param pool the pool. + * + * @return the capacity. + */ +PJ_IDECL(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ); + +/** + * Get the total size of user allocation request. + * + * @param pool the pool. + * + * @return the total size. + */ +PJ_IDECL(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ); + +/** + * Allocate storage with the specified size from the pool. + * If there's no storage available in the pool, then the pool can allocate more + * blocks if the increment size is larger than the requested size. + * + * @param pool the pool. + * @param size the requested size. + * + * @return pointer to the allocated memory. + */ +PJ_IDECL(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size); + +/** + * Allocate storage from the pool, and initialize it to zero. + * This function behaves like pj_pool_alloc(), except that the storage will + * be initialized to zero. + * + * @param pool the pool. + * @param count the number of elements in the array. + * @param elem the size of individual element. + * + * @return pointer to the allocated memory. + */ +PJ_IDECL(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, + pj_size_t elem); + + +/** + * @def pj_pool_zalloc(pj_pool_t *pool, pj_size_t size) + * Allocate storage from the pool and initialize it to zero. + * + * @param pool The pool. + * @param size The size to be allocated. + * + * @return Pointer to the allocated memory. + */ +#define pj_pool_zalloc(pool, size) pj_pool_calloc(pool, 1, size) + + +/** + * @} // PJ_POOL + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJ_POOL_FACTORY Pool Factory and Policy. + * @ingroup PJ_POOL_GROUP + * @brief + * Pool factory declares an interface to create and destroy pool. There may + * be several strategies for pool creation, and these strategies should + * implement the interface defined by pool factory. + * + * \section PJ_POOL_FACTORY_ITF Pool Factory Interface + * The pool factory defines the following interface: + * - \a policy: the memory pool factory policy. + * - \a create_pool(): create a new memory pool. + * - \a release_pool(): release memory pool back to factory. + * + * \section PJ_POOL_FACTORY_POL Pool Factory Policy. + * The pool factory policy controls the behaviour of memory factories, and + * defines the following interface: + * - \a block_alloc(): allocate memory block from backend memory mgmt/system. + * - \a block_free(): free memory block back to backend memory mgmt/system. + * @{ + */ + +/* We unfortunately don't have support for factory policy options as now, + so we keep this commented at the moment. +enum PJ_POOL_FACTORY_OPTION +{ + PJ_POOL_FACTORY_SERIALIZE = 1 +}; +*/ + +/** + * This structure declares pool factory interface. + */ +typedef struct pj_pool_factory_policy +{ + /** + * Allocate memory block (for use by pool). This function is called + * by memory pool to allocate memory block. + * + * @param factory Pool factory. + * @param size The size of memory block to allocate. + * + * @return Memory block. + */ + void* (*block_alloc)(pj_pool_factory *factory, pj_size_t size); + + /** + * Free memory block. + * + * @param factory Pool factory. + * @param mem Memory block previously allocated by block_alloc(). + * @param size The size of memory block. + */ + void (*block_free)(pj_pool_factory *factory, void *mem, pj_size_t size); + + /** + * Default callback to be called when memory allocation fails. + */ + pj_pool_callback *callback; + + /** + * Option flags. + */ + unsigned flags; + +} pj_pool_factory_policy; + +/** + * This constant denotes the exception number that will be thrown by default + * memory factory policy when memory allocation fails. + */ +extern int PJ_NO_MEMORY_EXCEPTION; + +/** + * This global variable points to default memory pool factory policy. + * The behaviour of the default policy is: + * - block allocation and deallocation use malloc() and free(). + * - callback will raise PJ_NO_MEMORY_EXCEPTION exception. + * - access to pool factory is not serialized (i.e. not thread safe). + */ +extern pj_pool_factory_policy pj_pool_factory_default_policy; + +/** + * This structure contains the declaration for pool factory interface. + */ +struct pj_pool_factory +{ + /** + * Memory pool policy. + */ + pj_pool_factory_policy policy; + + /** + * Create a new pool from the pool factory. + * + * @param factory The pool factory. + * @param name the name to be assigned to the pool. The name should + * not be longer than PJ_MAX_OBJ_NAME (32 chars), or + * otherwise it will be truncated. + * @param initial_size the size of initial memory blocks taken by the pool. + * Note that the pool will take 68+20 bytes for + * administrative area from this block. + * @param increment_size the size of each additional blocks to be allocated + * when the pool is running out of memory. If user + * requests memory which is larger than this size, then + * an error occurs. + * Note that each time a pool allocates additional block, + * it needs 20 bytes (equal to sizeof(pj_pool_block)) to + * store some administrative info. + * @param callback Cllback to be called when error occurs in the pool. + * Note that when an error occurs during pool creation, + * the callback itself is not called. Instead, NULL + * will be returned. + * + * @return the memory pool, or NULL. + */ + pj_pool_t* (*create_pool)( pj_pool_factory *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback); + + /** + * Release the pool to the pool factory. + * + * @param factory The pool factory. + * @param pool The pool to be released. + */ + void (*release_pool)( pj_pool_factory *factory, pj_pool_t *pool ); + + /** + * Dump pool status to log. + * + * @param factory The pool factory. + */ + void (*dump_status)( pj_pool_factory *factory, pj_bool_t detail ); +}; + +/** + * This function is intended to be used by pool factory implementors. + * @param factory Pool factory. + * @param name Pool name. + * @param initial_size Initial size. + * @param increment_size Increment size. + * @param callback Callback. + * @return The pool object, or NULL. + */ +PJ_DECL(pj_pool_t*) pj_pool_create_int( pj_pool_factory *factory, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback); + +/** + * This function is intended to be used by pool factory implementors. + * @param pool The pool. + * @param name Pool name. + * @param increment_size Increment size. + * @param callback Callback function. + */ +PJ_DECL(void) pj_pool_init_int( pj_pool_t *pool, + const char *name, + pj_size_t increment_size, + pj_pool_callback *callback); + +/** + * This function is intended to be used by pool factory implementors. + * @param pool The memory pool. + */ +PJ_DECL(void) pj_pool_destroy_int( pj_pool_t *pool ); + + +/** + * @} // PJ_POOL_FACTORY + */ + +/////////////////////////////////////////////////////////////////////////////// + +/** + * @defgroup PJ_CACHING_POOL Caching Pool Factory. + * @ingroup PJ_POOL_GROUP + * @brief + * Caching pool is one sample implementation of pool factory where the + * factory can reuse memory to create a pool. Application defines what the + * maximum memory the factory can hold, and when a pool is released the + * factory decides whether to destroy the pool or to keep it for future use. + * If the total amount of memory in the internal cache is still within the + * limit, the factory will keep the pool in the internal cache, otherwise the + * pool will be destroyed, thus releasing the memory back to the system. + * + * @{ + */ + +/** + * Number of unique sizes, to be used as index to the free list. + * Each pool in the free list is organized by it's size. + */ +#define PJ_CACHING_POOL_ARRAY_SIZE 16 + +/** + * Declaration for caching pool. Application doesn't normally need to + * care about the contents of this struct, it is only provided here because + * application need to define an instance of this struct (we can not allocate + * the struct from a pool since there is no pool factory yet!). + */ +struct pj_caching_pool +{ + /** Pool factory interface, must be declared first. */ + pj_pool_factory factory; + + /** Current factory's capacity, i.e. number of bytes that are allocated + * and available for application in this factory. The factory's + * capacity represents the size of all pools kept by this factory + * in it's free list, which will be returned to application when it + * requests to create a new pool. + */ + pj_size_t capacity; + + /** Maximum size that can be held by this factory. Once the capacity + * has exceeded @a max_capacity, further #pj_pool_release() will + * flush the pool. If the capacity is still below the @a max_capacity, + * #pj_pool_release() will save the pool to the factory's free list. + */ + pj_size_t max_capacity; + + /** + * Number of pools currently held by applications. This number gets + * incremented everytime #pj_pool_create() is called, and gets + * decremented when #pj_pool_release() is called. + */ + pj_size_t used_count; + + /** + * Lists of pools in the cache, indexed by pool size. + */ + pj_list free_list[PJ_CACHING_POOL_ARRAY_SIZE]; + + /** + * List of pools currently allocated by applications. + */ + pj_list used_list; +}; + + + +/** + * Initialize caching pool. + * + * @param ch_pool The caching pool factory to be initialized. + * @param policy Pool factory policy. + * @param max_capacity The total capacity to be retained in the cache. When + * the pool is returned to the cache, it will be kept in + * recycling list if the total capacity of pools in this + * list plus the capacity of the pool is still below this + * value. + */ +PJ_DECL(void) pj_caching_pool_init( pj_caching_pool *ch_pool, + const pj_pool_factory_policy *policy, + pj_size_t max_capacity); + + +/** + * Destroy caching pool, and release all the pools in the recycling list. + * + * @param ch_pool The caching pool. + */ +PJ_DECL(void) pj_caching_pool_destroy( pj_caching_pool *ch_pool ); + +/** + * @} // PJ_CACHING_POOL + */ + +# if PJ_FUNCTIONS_ARE_INLINED +# include "pool_i.h" +# endif + +PJ_END_DECL + +#endif /* __PJ_POOL_H__ */ + diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h index 08dd7ca97..a99825156 100644 --- a/pjlib/include/pj/pool_i.h +++ b/pjlib/include/pj/pool_i.h @@ -1,74 +1,74 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/pool_i.h 6 10/14/05 12:26a Bennylp $ */ - - -#include - -PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size); - -PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) -{ - return pool->capacity; -} - -PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) -{ - return pool->used_size; -} - -PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_t *pool, - pj_pool_block *block, pj_size_t size ) -{ - /* The operation below is valid for size==0. - * When size==0, the function will return the pointer to the pool - * memory address, but no memory will be allocated. - */ - if (size & (PJ_POOL_ALIGNMENT-1)) { - size &= ~(PJ_POOL_ALIGNMENT-1); - size += PJ_POOL_ALIGNMENT; - } - if ((unsigned)(block->end - block->cur) >= size) { - void *ptr = block->cur; - block->cur += size; - pool->used_size += size; - return ptr; - } - return NULL; -} - -PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size) -{ - pj_pool_block *block = pool->block_list.next; - void *ptr = pj_pool_alloc_from_block(pool, block, size); - if (!ptr) - ptr = pj_pool_allocate_find(pool, size); - return ptr; -} - - -PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size) -{ - void *buf = pj_pool_alloc( pool, size*count); - if (buf) - pj_memset(buf, 0, size * count); - return buf; -} - -PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool ) -{ - return pool->obj_name; -} - -PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f, - const char *name, - pj_size_t initial_size, - pj_size_t increment_size, - pj_pool_callback *callback) -{ - return (*f->create_pool)(f, name, initial_size, increment_size, callback); -} - -PJ_IDEF(void) pj_pool_release( pj_pool_t *pool ) -{ - (*pool->factory->release_pool)(pool->factory, pool); -} - +/* $Header: /pjproject-0.3/pjlib/include/pj/pool_i.h 6 10/14/05 12:26a Bennylp $ */ + + +#include + +PJ_DECL(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size); + +PJ_IDEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) +{ + return pool->capacity; +} + +PJ_IDEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) +{ + return pool->used_size; +} + +PJ_IDEF(void*) pj_pool_alloc_from_block( pj_pool_t *pool, + pj_pool_block *block, pj_size_t size ) +{ + /* The operation below is valid for size==0. + * When size==0, the function will return the pointer to the pool + * memory address, but no memory will be allocated. + */ + if (size & (PJ_POOL_ALIGNMENT-1)) { + size &= ~(PJ_POOL_ALIGNMENT-1); + size += PJ_POOL_ALIGNMENT; + } + if ((unsigned)(block->end - block->cur) >= size) { + void *ptr = block->cur; + block->cur += size; + pool->used_size += size; + return ptr; + } + return NULL; +} + +PJ_IDEF(void*) pj_pool_alloc( pj_pool_t *pool, pj_size_t size) +{ + pj_pool_block *block = pool->block_list.next; + void *ptr = pj_pool_alloc_from_block(pool, block, size); + if (!ptr) + ptr = pj_pool_allocate_find(pool, size); + return ptr; +} + + +PJ_IDEF(void*) pj_pool_calloc( pj_pool_t *pool, pj_size_t count, pj_size_t size) +{ + void *buf = pj_pool_alloc( pool, size*count); + if (buf) + pj_memset(buf, 0, size * count); + return buf; +} + +PJ_IDEF(const char *) pj_pool_getobjname( const pj_pool_t *pool ) +{ + return pool->obj_name; +} + +PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + return (*f->create_pool)(f, name, initial_size, increment_size, callback); +} + +PJ_IDEF(void) pj_pool_release( pj_pool_t *pool ) +{ + (*pool->factory->release_pool)(pool->factory, pool); +} + diff --git a/pjlib/include/pj/rand.h b/pjlib/include/pj/rand.h index 2f612319c..4a0bb8019 100644 --- a/pjlib/include/pj/rand.h +++ b/pjlib/include/pj/rand.h @@ -1,60 +1,60 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/rand.h 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/rand.h $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - * 1 9/15/05 8:40p Bennylp - * Created. - */ -#ifndef __PJ_RAND_H__ -#define __PJ_RAND_H__ - -/** - * @file rand.h - * @brief Random Number Generator. - */ - -#include - -PJ_BEGIN_DECL - - -/** - * @defgroup PJ_RAND Random Number Generator - * @ingroup PJ_MISC - * @{ - * This module contains functions for generating random numbers. - * This abstraction is needed not only because not all platforms have - * \a rand() and \a srand(), but also on some platforms \a rand() - * only has 16-bit randomness, which is not good enough. - */ - -/** - * Put in seed to random number generator. - * - * @param seed Seed value. - */ -PJ_DECL(void) pj_srand(unsigned int seed); - - -/** - * Generate random integer with 32bit randomness. - * - * @return a random integer. - */ -PJ_DECL(int) pj_rand(void); - - -/** @} */ - - -PJ_END_DECL - - -#endif /* __PJ_RAND_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/rand.h 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/rand.h $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + * 1 9/15/05 8:40p Bennylp + * Created. + */ +#ifndef __PJ_RAND_H__ +#define __PJ_RAND_H__ + +/** + * @file rand.h + * @brief Random Number Generator. + */ + +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_RAND Random Number Generator + * @ingroup PJ_MISC + * @{ + * This module contains functions for generating random numbers. + * This abstraction is needed not only because not all platforms have + * \a rand() and \a srand(), but also on some platforms \a rand() + * only has 16-bit randomness, which is not good enough. + */ + +/** + * Put in seed to random number generator. + * + * @param seed Seed value. + */ +PJ_DECL(void) pj_srand(unsigned int seed); + + +/** + * Generate random integer with 32bit randomness. + * + * @return a random integer. + */ +PJ_DECL(int) pj_rand(void); + + +/** @} */ + + +PJ_END_DECL + + +#endif /* __PJ_RAND_H__ */ + diff --git a/pjlib/include/pj/rbtree.h b/pjlib/include/pj/rbtree.h index f94e3658e..b09976807 100644 --- a/pjlib/include/pj/rbtree.h +++ b/pjlib/include/pj/rbtree.h @@ -1,193 +1,193 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/rbtree.h 5 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_RBTREE_H__ -#define __PJ_RBTREE_H__ - -/** - * @file rbtree.h - * @brief Red/Black Tree - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_RBTREE Red/Black Balanced Tree - * @ingroup PJ_DS - * @brief - * Red/Black tree is the variant of balanced tree, where the search, insert, - * and delete operation is \b guaranteed to take at most \a O( lg(n) ). - * @{ - */ -/** - * Color type for Red-Black tree. - */ -typedef enum pj_rbcolor_t -{ - PJ_RBCOLOR_BLACK, - PJ_RBCOLOR_RED -} pj_rbcolor_t; - -/** - * The type of the node of the R/B Tree. - */ -typedef struct pj_rbtree_node -{ - /** Pointers to the node's parent, and left and right siblings. */ - struct pj_rbtree_node *parent, *left, *right; - - /** Key associated with the node. */ - const void *key; - - /** User data associated with the node. */ - void *user_data; - - /** The R/B Tree node color. */ - pj_rbcolor_t color; - -} pj_rbtree_node; - - -/** - * The type of function use to compare key value of tree node. - * @return - * 0 if the keys are equal - * <0 if key1 is lower than key2 - * >0 if key1 is greater than key2. - */ -typedef int pj_rbtree_comp(const void *key1, const void *key2); - - -/** - * Declaration of a red-black tree. All elements in the tree must have UNIQUE - * key. - * A red black tree always maintains the balance of the tree, so that the - * tree height will not be greater than lg(N). Insert, search, and delete - * operation will take lg(N) on the worst case. But for insert and delete, - * there is additional time needed to maintain the balance of the tree. - */ -typedef struct pj_rbtree -{ - pj_rbtree_node null_node; /**< Constant to indicate NULL node. */ - pj_rbtree_node *null; /**< Constant to indicate NULL node. */ - pj_rbtree_node *root; /**< Root tree node. */ - unsigned size; /**< Number of elements in the tree. */ - pj_rbtree_comp *comp; /**< Key comparison function. */ -} pj_rbtree; - - -/** - * Guidance on how much memory required for each of the node. - */ -#define PJ_RBTREE_NODE_SIZE (sizeof(pj_rbtree_node)) - - -/** - * Guidance on memory required for the tree. - */ -#define PJ_RBTREE_SIZE (sizeof(pj_rbtree)) - - -/** - * Initialize the tree. - * @param tree the tree to be initialized. - * @param comp key comparison function to be used for this tree. - */ -PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp); - -/** - * Get the first element in the tree. - * The first element always has the least value for the key, according to - * the comparison function. - * @param tree the tree. - * @return the tree node, or NULL if the tree has no element. - */ -PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ); - -/** - * Get the last element in the tree. - * The last element always has the greatest key value, according to the - * comparison function defined for the tree. - * @param tree the tree. - * @return the tree node, or NULL if the tree has no element. - */ -PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ); - -/** - * Get the successive element for the specified node. - * The successive element is an element with greater key value. - * @param tree the tree. - * @param node the node. - * @return the successive node, or NULL if the node has no successor. - */ -PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, - pj_rbtree_node *node ); - -/** - * The the previous node for the specified node. - * The previous node is an element with less key value. - * @param tree the tree. - * @param node the node. - * @return the previous node, or NULL if the node has no previous node. - */ -PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, - pj_rbtree_node *node ); - -/** - * Insert a new node. - * The node will be inserted at sorted location. The key of the node must - * be UNIQUE, i.e. it hasn't existed in the tree. - * @param tree the tree. - * @param node the node to be inserted. - * @return zero on success, or -1 if the key already exist. - */ -PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree, - pj_rbtree_node *node ); - -/** - * Find a node which has the specified key. - * @param tree the tree. - * @param key the key to search. - * @return the tree node with the specified key, or NULL if the key can not - * be found. - */ -PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, - const void *key ); - -/** - * Erase a node from the tree. - * @param tree the tree. - * @param node the node to be erased. - * @return the tree node itself. - */ -PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, - pj_rbtree_node *node ); - -/** - * Get the maximum tree height from the specified node. - * @param tree the tree. - * @param node the node, or NULL to get the root of the tree. - * @return the maximum height, which should be at most lg(N) - */ -PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree, - pj_rbtree_node *node ); - -/** - * Get the minumum tree height from the specified node. - * @param tree the tree. - * @param node the node, or NULL to get the root of the tree. - * @return the height - */ -PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree, - pj_rbtree_node *node ); - - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJ_RBTREE_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/rbtree.h 5 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_RBTREE_H__ +#define __PJ_RBTREE_H__ + +/** + * @file rbtree.h + * @brief Red/Black Tree + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_RBTREE Red/Black Balanced Tree + * @ingroup PJ_DS + * @brief + * Red/Black tree is the variant of balanced tree, where the search, insert, + * and delete operation is \b guaranteed to take at most \a O( lg(n) ). + * @{ + */ +/** + * Color type for Red-Black tree. + */ +typedef enum pj_rbcolor_t +{ + PJ_RBCOLOR_BLACK, + PJ_RBCOLOR_RED +} pj_rbcolor_t; + +/** + * The type of the node of the R/B Tree. + */ +typedef struct pj_rbtree_node +{ + /** Pointers to the node's parent, and left and right siblings. */ + struct pj_rbtree_node *parent, *left, *right; + + /** Key associated with the node. */ + const void *key; + + /** User data associated with the node. */ + void *user_data; + + /** The R/B Tree node color. */ + pj_rbcolor_t color; + +} pj_rbtree_node; + + +/** + * The type of function use to compare key value of tree node. + * @return + * 0 if the keys are equal + * <0 if key1 is lower than key2 + * >0 if key1 is greater than key2. + */ +typedef int pj_rbtree_comp(const void *key1, const void *key2); + + +/** + * Declaration of a red-black tree. All elements in the tree must have UNIQUE + * key. + * A red black tree always maintains the balance of the tree, so that the + * tree height will not be greater than lg(N). Insert, search, and delete + * operation will take lg(N) on the worst case. But for insert and delete, + * there is additional time needed to maintain the balance of the tree. + */ +typedef struct pj_rbtree +{ + pj_rbtree_node null_node; /**< Constant to indicate NULL node. */ + pj_rbtree_node *null; /**< Constant to indicate NULL node. */ + pj_rbtree_node *root; /**< Root tree node. */ + unsigned size; /**< Number of elements in the tree. */ + pj_rbtree_comp *comp; /**< Key comparison function. */ +} pj_rbtree; + + +/** + * Guidance on how much memory required for each of the node. + */ +#define PJ_RBTREE_NODE_SIZE (sizeof(pj_rbtree_node)) + + +/** + * Guidance on memory required for the tree. + */ +#define PJ_RBTREE_SIZE (sizeof(pj_rbtree)) + + +/** + * Initialize the tree. + * @param tree the tree to be initialized. + * @param comp key comparison function to be used for this tree. + */ +PJ_DECL(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp); + +/** + * Get the first element in the tree. + * The first element always has the least value for the key, according to + * the comparison function. + * @param tree the tree. + * @return the tree node, or NULL if the tree has no element. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ); + +/** + * Get the last element in the tree. + * The last element always has the greatest key value, according to the + * comparison function defined for the tree. + * @param tree the tree. + * @return the tree node, or NULL if the tree has no element. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ); + +/** + * Get the successive element for the specified node. + * The successive element is an element with greater key value. + * @param tree the tree. + * @param node the node. + * @return the successive node, or NULL if the node has no successor. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * The the previous node for the specified node. + * The previous node is an element with less key value. + * @param tree the tree. + * @param node the node. + * @return the previous node, or NULL if the node has no previous node. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Insert a new node. + * The node will be inserted at sorted location. The key of the node must + * be UNIQUE, i.e. it hasn't existed in the tree. + * @param tree the tree. + * @param node the node to be inserted. + * @return zero on success, or -1 if the key already exist. + */ +PJ_DECL(int) pj_rbtree_insert( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Find a node which has the specified key. + * @param tree the tree. + * @param key the key to search. + * @return the tree node with the specified key, or NULL if the key can not + * be found. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, + const void *key ); + +/** + * Erase a node from the tree. + * @param tree the tree. + * @param node the node to be erased. + * @return the tree node itself. + */ +PJ_DECL(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Get the maximum tree height from the specified node. + * @param tree the tree. + * @param node the node, or NULL to get the root of the tree. + * @return the maximum height, which should be at most lg(N) + */ +PJ_DECL(unsigned) pj_rbtree_max_height( pj_rbtree *tree, + pj_rbtree_node *node ); + +/** + * Get the minumum tree height from the specified node. + * @param tree the tree. + * @param node the node, or NULL to get the root of the tree. + * @return the height + */ +PJ_DECL(unsigned) pj_rbtree_min_height( pj_rbtree *tree, + pj_rbtree_node *node ); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJ_RBTREE_H__ */ + diff --git a/pjlib/include/pj/scanner.h b/pjlib/include/pj/scanner.h index 5b04c687f..5a82ff919 100644 --- a/pjlib/include/pj/scanner.h +++ b/pjlib/include/pj/scanner.h @@ -1,454 +1,454 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/scanner.h 10 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_PARSER_H__ -#define __PJ_PARSER_H__ - -/** - * @file scanner.h - * @brief Text Scanning. - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_SCAN Text Scanning - * @ingroup PJ_MISC - * @brief - * Text scanning utility. - */ - -/** - * @defgroup PJ_CHARSPEC Character Filter Specification - * @ingroup PJ_SCAN - * @brief - * The type pj_char_spec is a specification of character set used in - * scanner. Application can define multiple character specs, such as to - * scan alpha numerics, numbers, tokens, etc. - * @{ - */ - -/** - * This describes the type of individual character specification in - * #pj_char_spec. - */ -typedef pj_uint8_t pj_char_spec_element_t; - -/** - * The character specification is implemented as array of boolean flags. Each - * flag indicates the membership of the character in the spec. If the flag - * at one position is non-zero, then the character at that position belongs - * to the specification, and vice versa. - */ -typedef pj_char_spec_element_t pj_char_spec[256]; -// Note: it's got to be 256 (not 128) to cater for extended character in input. - -/** - * Initialize character spec. - * @param cs the scanner character specification. - */ -PJ_DECL(void) pj_cs_init( pj_char_spec cs); - -/** - * Set the membership of the specified character to TRUE. - * @param cs the scanner character specification. - * @param c the character. - */ -PJ_DECL(void) pj_cs_set( pj_char_spec cs, int c); - -/** - * Add the characters in the specified range '[cstart, cend)' to the - * specification (the last character itself ('cend') is not added). - * @param cs the scanner character specification. - * @param cstart the first character in the range. - * @param cend the next character after the last character in the range. - */ -PJ_DECL(void) pj_cs_add_range( pj_char_spec cs, int cstart, int cend); - -/** - * Add alphabetic characters to the specification. - * @param cs the scanner character specification. - */ -PJ_DECL(void) pj_cs_add_alpha( pj_char_spec cs); - -/** - * Add numeric characters to the specification. - * @param cs the scanner character specification. - */ -PJ_DECL(void) pj_cs_add_num( pj_char_spec cs); - -/** - * Add the characters in the string to the specification. - * @param cs the scanner character specification. - * @param str the string. - */ -PJ_DECL(void) pj_cs_add_str( pj_char_spec cs, const char *str); - -/** - * Delete characters in the specified range from the specification. - * @param cs the scanner character specification. - * @param cstart the first character in the range. - * @param cend the next character after the last character in the range. - */ -PJ_DECL(void) pj_cs_del_range( pj_char_spec cs, int cstart, int cend); - -/** - * Delete characters in the specified string from the specification. - * @param cs the scanner character specification. - * @param str the string. - */ -PJ_DECL(void) pj_cs_del_str( pj_char_spec cs, const char *str); - -/** - * Invert specification. - * @param cs the scanner character specification. - */ -PJ_DECL(void) pj_cs_invert( pj_char_spec cs ); - -/** - * Check whether the specified character belongs to the specification. - * @param cs the scanner character specification. - * @param c the character to check for matching. - */ -PJ_INLINE(int) pj_cs_match( const pj_char_spec cs, int c ) -{ - return cs[c]; -} - -/** - * @} - */ - -/** - * @defgroup PJ_SCANNER Text Scanner - * @ingroup PJ_SCAN - * @{ - */ - -/** - * Flags for scanner. - */ -enum -{ - /** This flags specifies that the scanner should automatically skip - whitespaces - */ - PJ_SCAN_AUTOSKIP_WS = 1, - - /** This flags specifies that the scanner should automatically skip - SIP header continuation. This flag implies PJ_SCAN_AUTOSKIP_WS. - */ - PJ_SCAN_AUTOSKIP_WS_HEADER = 3, - - /** Auto-skip new lines. - */ - PJ_SCAN_AUTOSKIP_NEWLINE = 4, -}; - - -/* Forward decl. */ -struct pj_scanner; - - -/** - * The callback function type to be called by the scanner when it encounters - * syntax error. - * @param scanner The scanner instance that calls the callback . - */ -typedef void (*pj_syn_err_func_ptr)(struct pj_scanner *scanner); - - -/** - * The text scanner structure. - */ -typedef struct pj_scanner -{ - char *begin; /**< Start of input buffer. */ - char *end; /**< End of input buffer. */ - char *curptr; /**< Current pointer. */ - int line; /**< Current line. */ - int col; /**< Current column. */ - int skip_ws; /**< Skip whitespace flag. */ - pj_syn_err_func_ptr callback; /**< Syntax error callback. */ -} pj_scanner; - - -/** - * This structure can be used by application to store the state of the parser, - * so that the scanner state can be rollback to this state when necessary. - */ -typedef struct pj_scan_state -{ - char *curptr; /**< Current scanner's pointer. */ - int line; /**< Current line. */ - int col; /**< Current column. */ -} pj_scan_state; - - -/** - * Initialize the scanner. Note that the input string buffer must have - * length at least buflen+1 because the scanner will NULL terminate the - * string during initialization. - * - * @param scanner The scanner to be initialized. - * @param bufstart The input buffer to scan. Note that buffer[buflen] will be - * filled with NULL char until scanner is destroyed, so - * the actual buffer length must be at least buflen+1. - * @param buflen The length of the input buffer, which normally is - * strlen(bufstart). - * @param options Zero, or combination of PJ_SCAN_AUTOSKIP_WS or - * PJ_SCAN_AUTOSKIP_WS_HEADER - * @param callback Callback to be called when the scanner encounters syntax - * error condition. - */ -PJ_DECL(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen, - unsigned options, - pj_syn_err_func_ptr callback ); - - -/** - * Call this function when application has finished using the scanner. - * - * @param scanner The scanner. - */ -PJ_DECL(void) pj_scan_fini( pj_scanner *scanner ); - - -/** - * Determine whether the EOF condition for the scanner has been met. - * - * @param scanner The scanner. - * - * @return Non-zero if scanner is EOF. - */ -PJ_INLINE(int) pj_scan_is_eof( const pj_scanner *scanner) -{ - return scanner->curptr >= scanner->end; -} - - -/** - * Peek strings in current position according to parameter spec, and return - * the strings in parameter out. The current scanner position will not be - * moved. If the scanner is already in EOF state, syntax error callback will - * be called thrown. - * - * @param scanner The scanner. - * @param spec The spec to match input string. - * @param out String to store the result. - * - * @return the character right after the peek-ed position or zero if there's - * no more characters. - */ -PJ_DECL(int) pj_scan_peek( pj_scanner *scanner, - const pj_char_spec spec, pj_str_t *out); - - -/** - * Peek len characters in current position, and return them in out parameter. - * Note that whitespaces or newlines will be returned as it is, regardless - * of PJ_SCAN_AUTOSKIP_WS settings. If the character left is less than len, - * syntax error callback will be called. - * - * @param scanner The scanner. - * @param len Length to peek. - * @param out String to store the result. - * - * @return the character right after the peek-ed position or zero if there's - * no more characters. - */ -PJ_DECL(int) pj_scan_peek_n( pj_scanner *scanner, - pj_size_t len, pj_str_t *out); - - -/** - * Peek strings in current position until spec is matched, and return - * the strings in parameter out. The current scanner position will not be - * moved. If the scanner is already in EOF state, syntax error callback will - * be called. - * - * @param scanner The scanner. - * @param spec The peeking will stop when the input match this spec. - * @param out String to store the result. - * - * @return the character right after the peek-ed position. - */ -PJ_DECL(int) pj_scan_peek_until( pj_scanner *scanner, - const pj_char_spec spec, - pj_str_t *out); - - -/** - * Get characters from the buffer according to the spec, and return them - * in out parameter. The scanner will attempt to get as many characters as - * possible as long as the spec matches. If the first character doesn't - * match the spec, or scanner is already in EOF when this function is called, - * an exception will be thrown. - * - * @param scanner The scanner. - * @param spec The spec to match input string. - * @param out String to store the result. - */ -PJ_DECL(void) pj_scan_get( pj_scanner *scanner, - const pj_char_spec spec, pj_str_t *out); - - -/** - * Get characters between quotes. If current input doesn't match begin_quote, - * syntax error will be thrown. - * - * @param scanner The scanner. - * @param begin_quote The character to begin the quote. - * @param end_quote The character to end the quote. - * @param out String to store the result. - */ -PJ_DECL(void) pj_scan_get_quote( pj_scanner *scanner, - int begin_quote, int end_quote, - pj_str_t *out); - -/** - * Get N characters from the scanner. - * - * @param scanner The scanner. - * @param N Number of characters to get. - * @param out String to store the result. - */ -PJ_DECL(void) pj_scan_get_n( pj_scanner *scanner, - unsigned N, pj_str_t *out); - - -/** - * Get one character from the scanner. - * - * @param scanner The scanner. - * - * @return (unknown) - */ -PJ_DECL(int) pj_scan_get_char( pj_scanner *scanner ); - - -/** - * Get a newline from the scanner. A newline is defined as '\\n', or '\\r', or - * "\\r\\n". If current input is not newline, syntax error will be thrown. - * - * @param scanner The scanner. - */ -PJ_DECL(void) pj_scan_get_newline( pj_scanner *scanner ); - - -/** - * Get characters from the scanner and move the scanner position until the - * current character matches the spec. - * - * @param scanner The scanner. - * @param spec Get until the input match this spec. - * @param out String to store the result. - */ -PJ_DECL(void) pj_scan_get_until( pj_scanner *scanner, - const pj_char_spec spec, pj_str_t *out); - - -/** - * Get characters from the scanner and move the scanner position until the - * current character matches until_char. - * - * @param scanner The scanner. - * @param until_char Get until the input match this character. - * @param out String to store the result. - */ -PJ_DECL(void) pj_scan_get_until_ch( pj_scanner *scanner, - int until_char, pj_str_t *out); - - -/** - * Get characters from the scanner and move the scanner position until the - * current character matches until_char. - * - * @param scanner The scanner. - * @param until_spec Get until the input match any of these characters. - * @param out String to store the result. - */ -PJ_DECL(void) pj_scan_get_until_chr( pj_scanner *scanner, - const char *until_spec, pj_str_t *out); - -/** - * Advance the scanner N characters, and skip whitespace - * if necessary. - * - * @param scanner The scanner. - * @param N Number of characters to skip. - * @param skip Flag to specify whether whitespace should be skipped - * after skipping the characters. - */ -PJ_DECL(void) pj_scan_advance_n( pj_scanner *scanner, - unsigned N, pj_bool_t skip); - - -/** - * Compare string in current position with the specified string. - * - * @param scanner The scanner. - * @param s The string to compare with. - * @param len Length of the string to compare. - * - * @return zero, <0, or >0 (just like strcmp()). - */ -PJ_DECL(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len); - - -/** - * Case-less string comparison of current position with the specified - * string. - * - * @param scanner The scanner. - * @param s The string to compare with. - * @param len Length of the string to compare with. - * - * @return zero, <0, or >0 (just like strcmp()). - */ -PJ_DECL(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len); - - -/** - * Manually skip whitespaces according to flag that was specified when - * the scanner was initialized. - * - * @param scanner The scanner. - */ -PJ_DECL(void) pj_scan_skip_whitespace( pj_scanner *scanner ); - - -/** - * Save the full scanner state. - * - * @param scanner The scanner. - * @param state Variable to store scanner's state. - */ -PJ_DECL(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state); - - -/** - * Restore the full scanner state. - * Note that this would not restore the string if application has modified - * it. This will only restore the scanner scanning position. - * - * @param scanner The scanner. - * @param state State of the scanner. - */ -PJ_DECL(void) pj_scan_restore_state( pj_scanner *scanner, - pj_scan_state *state); - -/** - * @} - */ - -#if PJ_FUNCTIONS_ARE_INLINED -# include "scanner_i.h" -#endif - - -PJ_END_DECL - -#endif - +/* $Header: /pjproject-0.3/pjlib/include/pj/scanner.h 10 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_PARSER_H__ +#define __PJ_PARSER_H__ + +/** + * @file scanner.h + * @brief Text Scanning. + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_SCAN Text Scanning + * @ingroup PJ_MISC + * @brief + * Text scanning utility. + */ + +/** + * @defgroup PJ_CHARSPEC Character Filter Specification + * @ingroup PJ_SCAN + * @brief + * The type pj_char_spec is a specification of character set used in + * scanner. Application can define multiple character specs, such as to + * scan alpha numerics, numbers, tokens, etc. + * @{ + */ + +/** + * This describes the type of individual character specification in + * #pj_char_spec. + */ +typedef pj_uint8_t pj_char_spec_element_t; + +/** + * The character specification is implemented as array of boolean flags. Each + * flag indicates the membership of the character in the spec. If the flag + * at one position is non-zero, then the character at that position belongs + * to the specification, and vice versa. + */ +typedef pj_char_spec_element_t pj_char_spec[256]; +// Note: it's got to be 256 (not 128) to cater for extended character in input. + +/** + * Initialize character spec. + * @param cs the scanner character specification. + */ +PJ_DECL(void) pj_cs_init( pj_char_spec cs); + +/** + * Set the membership of the specified character to TRUE. + * @param cs the scanner character specification. + * @param c the character. + */ +PJ_DECL(void) pj_cs_set( pj_char_spec cs, int c); + +/** + * Add the characters in the specified range '[cstart, cend)' to the + * specification (the last character itself ('cend') is not added). + * @param cs the scanner character specification. + * @param cstart the first character in the range. + * @param cend the next character after the last character in the range. + */ +PJ_DECL(void) pj_cs_add_range( pj_char_spec cs, int cstart, int cend); + +/** + * Add alphabetic characters to the specification. + * @param cs the scanner character specification. + */ +PJ_DECL(void) pj_cs_add_alpha( pj_char_spec cs); + +/** + * Add numeric characters to the specification. + * @param cs the scanner character specification. + */ +PJ_DECL(void) pj_cs_add_num( pj_char_spec cs); + +/** + * Add the characters in the string to the specification. + * @param cs the scanner character specification. + * @param str the string. + */ +PJ_DECL(void) pj_cs_add_str( pj_char_spec cs, const char *str); + +/** + * Delete characters in the specified range from the specification. + * @param cs the scanner character specification. + * @param cstart the first character in the range. + * @param cend the next character after the last character in the range. + */ +PJ_DECL(void) pj_cs_del_range( pj_char_spec cs, int cstart, int cend); + +/** + * Delete characters in the specified string from the specification. + * @param cs the scanner character specification. + * @param str the string. + */ +PJ_DECL(void) pj_cs_del_str( pj_char_spec cs, const char *str); + +/** + * Invert specification. + * @param cs the scanner character specification. + */ +PJ_DECL(void) pj_cs_invert( pj_char_spec cs ); + +/** + * Check whether the specified character belongs to the specification. + * @param cs the scanner character specification. + * @param c the character to check for matching. + */ +PJ_INLINE(int) pj_cs_match( const pj_char_spec cs, int c ) +{ + return cs[c]; +} + +/** + * @} + */ + +/** + * @defgroup PJ_SCANNER Text Scanner + * @ingroup PJ_SCAN + * @{ + */ + +/** + * Flags for scanner. + */ +enum +{ + /** This flags specifies that the scanner should automatically skip + whitespaces + */ + PJ_SCAN_AUTOSKIP_WS = 1, + + /** This flags specifies that the scanner should automatically skip + SIP header continuation. This flag implies PJ_SCAN_AUTOSKIP_WS. + */ + PJ_SCAN_AUTOSKIP_WS_HEADER = 3, + + /** Auto-skip new lines. + */ + PJ_SCAN_AUTOSKIP_NEWLINE = 4, +}; + + +/* Forward decl. */ +struct pj_scanner; + + +/** + * The callback function type to be called by the scanner when it encounters + * syntax error. + * @param scanner The scanner instance that calls the callback . + */ +typedef void (*pj_syn_err_func_ptr)(struct pj_scanner *scanner); + + +/** + * The text scanner structure. + */ +typedef struct pj_scanner +{ + char *begin; /**< Start of input buffer. */ + char *end; /**< End of input buffer. */ + char *curptr; /**< Current pointer. */ + int line; /**< Current line. */ + int col; /**< Current column. */ + int skip_ws; /**< Skip whitespace flag. */ + pj_syn_err_func_ptr callback; /**< Syntax error callback. */ +} pj_scanner; + + +/** + * This structure can be used by application to store the state of the parser, + * so that the scanner state can be rollback to this state when necessary. + */ +typedef struct pj_scan_state +{ + char *curptr; /**< Current scanner's pointer. */ + int line; /**< Current line. */ + int col; /**< Current column. */ +} pj_scan_state; + + +/** + * Initialize the scanner. Note that the input string buffer must have + * length at least buflen+1 because the scanner will NULL terminate the + * string during initialization. + * + * @param scanner The scanner to be initialized. + * @param bufstart The input buffer to scan. Note that buffer[buflen] will be + * filled with NULL char until scanner is destroyed, so + * the actual buffer length must be at least buflen+1. + * @param buflen The length of the input buffer, which normally is + * strlen(bufstart). + * @param options Zero, or combination of PJ_SCAN_AUTOSKIP_WS or + * PJ_SCAN_AUTOSKIP_WS_HEADER + * @param callback Callback to be called when the scanner encounters syntax + * error condition. + */ +PJ_DECL(void) pj_scan_init( pj_scanner *scanner, char *bufstart, int buflen, + unsigned options, + pj_syn_err_func_ptr callback ); + + +/** + * Call this function when application has finished using the scanner. + * + * @param scanner The scanner. + */ +PJ_DECL(void) pj_scan_fini( pj_scanner *scanner ); + + +/** + * Determine whether the EOF condition for the scanner has been met. + * + * @param scanner The scanner. + * + * @return Non-zero if scanner is EOF. + */ +PJ_INLINE(int) pj_scan_is_eof( const pj_scanner *scanner) +{ + return scanner->curptr >= scanner->end; +} + + +/** + * Peek strings in current position according to parameter spec, and return + * the strings in parameter out. The current scanner position will not be + * moved. If the scanner is already in EOF state, syntax error callback will + * be called thrown. + * + * @param scanner The scanner. + * @param spec The spec to match input string. + * @param out String to store the result. + * + * @return the character right after the peek-ed position or zero if there's + * no more characters. + */ +PJ_DECL(int) pj_scan_peek( pj_scanner *scanner, + const pj_char_spec spec, pj_str_t *out); + + +/** + * Peek len characters in current position, and return them in out parameter. + * Note that whitespaces or newlines will be returned as it is, regardless + * of PJ_SCAN_AUTOSKIP_WS settings. If the character left is less than len, + * syntax error callback will be called. + * + * @param scanner The scanner. + * @param len Length to peek. + * @param out String to store the result. + * + * @return the character right after the peek-ed position or zero if there's + * no more characters. + */ +PJ_DECL(int) pj_scan_peek_n( pj_scanner *scanner, + pj_size_t len, pj_str_t *out); + + +/** + * Peek strings in current position until spec is matched, and return + * the strings in parameter out. The current scanner position will not be + * moved. If the scanner is already in EOF state, syntax error callback will + * be called. + * + * @param scanner The scanner. + * @param spec The peeking will stop when the input match this spec. + * @param out String to store the result. + * + * @return the character right after the peek-ed position. + */ +PJ_DECL(int) pj_scan_peek_until( pj_scanner *scanner, + const pj_char_spec spec, + pj_str_t *out); + + +/** + * Get characters from the buffer according to the spec, and return them + * in out parameter. The scanner will attempt to get as many characters as + * possible as long as the spec matches. If the first character doesn't + * match the spec, or scanner is already in EOF when this function is called, + * an exception will be thrown. + * + * @param scanner The scanner. + * @param spec The spec to match input string. + * @param out String to store the result. + */ +PJ_DECL(void) pj_scan_get( pj_scanner *scanner, + const pj_char_spec spec, pj_str_t *out); + + +/** + * Get characters between quotes. If current input doesn't match begin_quote, + * syntax error will be thrown. + * + * @param scanner The scanner. + * @param begin_quote The character to begin the quote. + * @param end_quote The character to end the quote. + * @param out String to store the result. + */ +PJ_DECL(void) pj_scan_get_quote( pj_scanner *scanner, + int begin_quote, int end_quote, + pj_str_t *out); + +/** + * Get N characters from the scanner. + * + * @param scanner The scanner. + * @param N Number of characters to get. + * @param out String to store the result. + */ +PJ_DECL(void) pj_scan_get_n( pj_scanner *scanner, + unsigned N, pj_str_t *out); + + +/** + * Get one character from the scanner. + * + * @param scanner The scanner. + * + * @return (unknown) + */ +PJ_DECL(int) pj_scan_get_char( pj_scanner *scanner ); + + +/** + * Get a newline from the scanner. A newline is defined as '\\n', or '\\r', or + * "\\r\\n". If current input is not newline, syntax error will be thrown. + * + * @param scanner The scanner. + */ +PJ_DECL(void) pj_scan_get_newline( pj_scanner *scanner ); + + +/** + * Get characters from the scanner and move the scanner position until the + * current character matches the spec. + * + * @param scanner The scanner. + * @param spec Get until the input match this spec. + * @param out String to store the result. + */ +PJ_DECL(void) pj_scan_get_until( pj_scanner *scanner, + const pj_char_spec spec, pj_str_t *out); + + +/** + * Get characters from the scanner and move the scanner position until the + * current character matches until_char. + * + * @param scanner The scanner. + * @param until_char Get until the input match this character. + * @param out String to store the result. + */ +PJ_DECL(void) pj_scan_get_until_ch( pj_scanner *scanner, + int until_char, pj_str_t *out); + + +/** + * Get characters from the scanner and move the scanner position until the + * current character matches until_char. + * + * @param scanner The scanner. + * @param until_spec Get until the input match any of these characters. + * @param out String to store the result. + */ +PJ_DECL(void) pj_scan_get_until_chr( pj_scanner *scanner, + const char *until_spec, pj_str_t *out); + +/** + * Advance the scanner N characters, and skip whitespace + * if necessary. + * + * @param scanner The scanner. + * @param N Number of characters to skip. + * @param skip Flag to specify whether whitespace should be skipped + * after skipping the characters. + */ +PJ_DECL(void) pj_scan_advance_n( pj_scanner *scanner, + unsigned N, pj_bool_t skip); + + +/** + * Compare string in current position with the specified string. + * + * @param scanner The scanner. + * @param s The string to compare with. + * @param len Length of the string to compare. + * + * @return zero, <0, or >0 (just like strcmp()). + */ +PJ_DECL(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len); + + +/** + * Case-less string comparison of current position with the specified + * string. + * + * @param scanner The scanner. + * @param s The string to compare with. + * @param len Length of the string to compare with. + * + * @return zero, <0, or >0 (just like strcmp()). + */ +PJ_DECL(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len); + + +/** + * Manually skip whitespaces according to flag that was specified when + * the scanner was initialized. + * + * @param scanner The scanner. + */ +PJ_DECL(void) pj_scan_skip_whitespace( pj_scanner *scanner ); + + +/** + * Save the full scanner state. + * + * @param scanner The scanner. + * @param state Variable to store scanner's state. + */ +PJ_DECL(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state); + + +/** + * Restore the full scanner state. + * Note that this would not restore the string if application has modified + * it. This will only restore the scanner scanning position. + * + * @param scanner The scanner. + * @param state State of the scanner. + */ +PJ_DECL(void) pj_scan_restore_state( pj_scanner *scanner, + pj_scan_state *state); + +/** + * @} + */ + +#if PJ_FUNCTIONS_ARE_INLINED +# include "scanner_i.h" +#endif + + +PJ_END_DECL + +#endif + diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h index c0aa56c1f..fee0f6a9f 100644 --- a/pjlib/include/pj/sock.h +++ b/pjlib/include/pj/sock.h @@ -1,677 +1,677 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/sock.h 10 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_SOCK_H__ -#define __PJ_SOCK_H__ - -/** - * @file sock.h - * @brief Socket Abstraction. - */ - -#include - -PJ_BEGIN_DECL - - -/** - * @defgroup PJ_SOCK Socket Abstraction - * @ingroup PJ_IO - * @{ - * - * The PJLIB socket abstraction layer is a thin and very portable abstraction - * for socket API. It provides API similar to BSD socket API. The abstraction - * is needed because BSD socket API is not always available on all platforms, - * therefore it wouldn't be possible to create a trully portable network - * programs unless we provide such abstraction. - * - * Applications can use this API directly in their application, just - * as they would when using traditional BSD socket API, provided they - * call #pj_init() first. - * - * \section pj_sock_examples_sec Examples - * - * For some examples on how to use the socket API, please see: - * - * - \ref page_pjlib_sock_test - * - \ref page_pjlib_select_test - * - \ref page_pjlib_sock_perf_test - */ - - -/** - * Supported address families. - * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL AF_*, BECAUSE - * THE LIBRARY WILL DO TRANSLATION TO THE NATIVE VALUE. - */ -extern const pj_uint16_t PJ_AF_UNIX; /**< Unix domain socket. */ -#define PJ_AF_LOCAL PJ_AF_UNIX; /**< POSIX name for AF_UNIX */ -extern const pj_uint16_t PJ_AF_INET; /**< Internet IP protocol. */ -extern const pj_uint16_t PJ_AF_INET6; /**< IP version 6. */ -extern const pj_uint16_t PJ_AF_PACKET; /**< Packet family. */ -extern const pj_uint16_t PJ_AF_IRDA; /**< IRDA sockets. */ - - -/** - * Supported types of sockets. - * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOCK_*, BECAUSE - * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. - */ - -extern const pj_uint16_t PJ_SOCK_STREAM; /**< Sequenced, reliable, connection- - based byte streams. */ -extern const pj_uint16_t PJ_SOCK_DGRAM; /**< Connectionless, unreliable - datagrams of fixed maximum - lengths. */ -extern const pj_uint16_t PJ_SOCK_RAW; /**< Raw protocol interface. */ -extern const pj_uint16_t PJ_SOCK_RDM; /**< Reliably-delivered messages. */ - - -/** - * Socket level specified in #pj_sock_setsockopt(). - * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOL_*, BECAUSE - * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. - */ -extern const pj_uint16_t PJ_SOL_SOCKET; /**< Socket level. */ -extern const pj_uint16_t PJ_SOL_IP; /**< IP level. */ -extern const pj_uint16_t PJ_SOL_TCP; /**< TCP level. */ -extern const pj_uint16_t PJ_SOL_UDP; /**< UDP level. */ -extern const pj_uint16_t PJ_SOL_IPV6; /**< IP version 6 */ - -/** - * Flags to be specified in #pj_sock_recv, #pj_sock_send, etc. - */ -typedef enum pj_sock_msg_flag -{ - PJ_MSG_OOB = 0x01, /**< Out-of-band messages. */ - PJ_MSG_PEEK = 0x02, /**< Peek, don't remove from buffer. */ - PJ_MSG_DONTROUTE = 0x04, /**< Don't route. */ -} pj_sock_msg_flag; - - -/** - * Flag to be specified in #pj_sock_shutdown. - */ -typedef enum pj_socket_sd_type -{ - PJ_SD_RECEIVE = 0, /**< No more receive. */ - PJ_SHUT_RD = 0, /**< Alias for SD_RECEIVE. */ - PJ_SD_SEND = 1, /**< No more sending. */ - PJ_SHUT_WR = 1, /**< Alias for SD_SEND. */ - PJ_SD_BOTH = 2, /**< No more send and receive. */ - PJ_SHUT_RDWR = 2, /**< Alias for SD_BOTH. */ -} pj_socket_sd_type; - - - -/** Address to accept any incoming messages. */ -#define PJ_INADDR_ANY ((pj_uint32_t)0) - -/** Address indicating an error return */ -#define PJ_INADDR_NONE ((pj_uint32_t)0xffffffff) - -/** Address to send to all hosts. */ -#define PJ_INADDR_BROADCAST ((pj_uint32_t)0xffffffff) - - -/** - * Maximum length specifiable by #pj_sock_listen(). - * If the build system doesn't override this value, then the lowest - * denominator (five, in Win32 systems) will be used. - */ -#if !defined(PJ_SOMAXCONN) -# define PJ_SOMAXCONN 5 -#endif - - -/** - * Constant for invalid socket returned by #pj_sock_socket() and - * #pj_sock_accept(). - */ -#define PJ_INVALID_SOCKET (-1) - -/** - * Structure describing a generic socket address. - */ -typedef struct pj_sockaddr -{ - pj_uint16_t sa_family; /**< Common data: address family. */ - char sa_data[14]; /**< Address data. */ -} pj_sockaddr; - - -/** - * This structure describes Internet address. - */ -typedef struct pj_in_addr -{ - pj_uint32_t s_addr; /**< The 32bit IP address. */ -} pj_in_addr; - - -/** - * This structure describes Internet socket address. - */ -typedef struct pj_sockaddr_in -{ - pj_uint16_t sin_family; /**< Address family. */ - pj_uint16_t sin_port; /**< Transport layer port number. */ - pj_in_addr sin_addr; /**< IP address. */ - char sin_zero[8]; /**< Padding. */ -} pj_sockaddr_in; - - -/** - * This structure describes IPv6 address. - */ -typedef struct pj_in6_addr -{ - /** Union of address formats. */ - union { - pj_uint8_t u6_addr8[16]; /**< u6_addr8 */ - pj_uint16_t u6_addr16[8]; /**< u6_addr16 */ - pj_uint32_t u6_addr32[4]; /**< u6_addr32 */ - } in6_u; -/** Shortcut to access in6_u.u6_addr8. */ -#define s6_addr in6_u.u6_addr8 -/** Shortcut to access in6_u.u6_addr16. */ -#define s6_addr16 in6_u.u6_addr16 -/** Shortcut to access in6_u.u6_addr32. */ -#define s6_addr32 in6_u.u6_addr32 -} pj_in6_addr; - -/** Initializer value for pj_in6_addr. */ -#define PJ_IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } - -/** Initializer value for pj_in6_addr. */ -#define PJ_IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } - -/** - * This structure describes IPv6 socket address. - */ -typedef struct pj_sockaddr_in6 -{ - pj_uint16_t sin6_family; /**< Address family */ - pj_uint16_t sin6_port; /**< Transport layer port number. */ - pj_uint32_t sin6_flowinfo; /**< IPv6 flow information */ - pj_in6_addr sin6_addr; /**< IPv6 address. */ - pj_uint32_t sin6_scope_id; /**< IPv6 scope-id */ -} pj_sockaddr_in6; - - -/***************************************************************************** - * - * SOCKET ADDRESS MANIPULATION. - * - ***************************************************************************** - */ - -/** - * Convert 16-bit value from network byte order to host byte order. - * - * @param netshort 16-bit network value. - * @return 16-bit host value. - */ -PJ_DECL(pj_uint16_t) pj_ntohs(pj_uint16_t netshort); - -/** - * Convert 16-bit value from host byte order to network byte order. - * - * @param hostshort 16-bit host value. - * @return 16-bit network value. - */ -PJ_DECL(pj_uint16_t) pj_htons(pj_uint16_t hostshort); - -/** - * Convert 32-bit value from network byte order to host byte order. - * - * @param netlong 32-bit network value. - * @return 32-bit host value. - */ -PJ_DECL(pj_uint32_t) pj_ntohl(pj_uint32_t netlong); - -/** - * Convert 32-bit value from host byte order to network byte order. - * - * @param hostlong 32-bit host value. - * @return 32-bit network value. - */ -PJ_DECL(pj_uint32_t) pj_htonl(pj_uint32_t hostlong); - -/** - * Convert an Internet host address given in network byte order - * to string in standard numbers and dots notation. - * - * @param inaddr The host address. - * @return The string address. - */ -PJ_DECL(char*) pj_inet_ntoa(pj_in_addr inaddr); - -/** - * This function converts the Internet host address cp from the standard - * numbers-and-dots notation into binary data and stores it in the structure - * that inp points to. - * - * @param cp IP address in standard numbers-and-dots notation. - * @param inp Structure that holds the output of the conversion. - * - * @return nonzero if the address is valid, zero if not. - */ -PJ_DECL(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp); - -/** - * Convert address string with numbers and dots to binary IP address. - * - * @param cp The IP address in numbers and dots notation. - * @return If success, the IP address is returned in network - * byte order. If failed, PJ_INADDR_NONE will be - * returned. - * @remark - * This is an obsolete interface to #pj_inet_aton(); it is obsolete - * because -1 is a valid address (255.255.255.255), and #pj_inet_aton() - * provides a cleaner way to indicate error return. - */ -PJ_DECL(pj_in_addr) pj_inet_addr(const pj_str_t *cp); - - -/** - * Get the transport layer port number of an Internet socket address. - * The port is returned in host byte order. - * - * @param addr The IP socket address. - * @return Port number, in host byte order. - */ -PJ_INLINE(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr) -{ - return pj_ntohs(addr->sin_port); -} - -/** - * Set the port number of an Internet socket address. - * - * @param addr The IP socket address. - * @param hostport The port number, in host byte order. - */ -PJ_INLINE(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, - pj_uint16_t hostport) -{ - addr->sin_port = pj_htons(hostport); -} - -/** - * Get the IP address of an Internet socket address. - * The address is returned as 32bit value in host byte order. - * - * @param addr The IP socket address. - * @return 32bit address, in host byte order. - */ -PJ_INLINE(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr) -{ - pj_in_addr in_addr; - in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr); - return in_addr; -}; - -/** - * Set the IP address of an Internet socket address. - * - * @param addr The IP socket address. - * @param hostaddr The host address, in host byte order. - */ -PJ_INLINE(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, - pj_uint32_t hostaddr) -{ - addr->sin_addr.s_addr = pj_htonl(hostaddr); -} - -/** - * Set the IP address of an IP socket address from string address, - * with resolving the host if necessary. The string address may be in a - * standard numbers and dots notation or may be a hostname. If hostname - * is specified, then the function will resolve the host into the IP - * address. - * - * @param addr The IP socket address to be set. - * @param cp The address string, which can be in a standard - * dotted numbers or a hostname to be resolved. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, - const pj_str_t *cp); - -/** - * Set the IP address and port of an IP socket address. - * The string address may be in a standard numbers and dots notation or - * may be a hostname. If hostname is specified, then the function will - * resolve the host into the IP address. - * - * @param addr The IP socket address to be set. - * @param cp The address string, which can be in a standard - * dotted numbers or a hostname to be resolved. - * @param port The port number, in host byte order. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, - const pj_str_t *cp, - pj_uint16_t port); - - -/***************************************************************************** - * - * HOST NAME AND ADDRESS. - * - ***************************************************************************** - */ - -/** - * Get system's host name. - * - * @return The hostname, or empty string if the hostname can not - * be identified. - */ -PJ_DECL(const pj_str_t*) pj_gethostname(void); - -/** - * Get host's IP address, which the the first IP address that is resolved - * from the hostname. - * - * @return The host's IP address, PJ_INADDR_NONE if the host - * IP address can not be identified. - */ -PJ_DECL(pj_in_addr) pj_gethostaddr(void); - - -/***************************************************************************** - * - * SOCKET API. - * - ***************************************************************************** - */ - -/** - * Create new socket/endpoint for communication. - * - * @param family Specifies a communication domain; this selects the - * protocol family which will be used for communication. - * @param type The socket has the indicated type, which specifies the - * communication semantics. - * @param protocol Specifies a particular protocol to be used with the - * socket. Normally only a single protocol exists to support - * a particular socket type within a given protocol family, - * in which a case protocol can be specified as 0. - * @param sock New socket descriptor, or PJ_INVALID_SOCKET on error. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_socket(int family, - int type, - int protocol, - pj_sock_t *sock); - -/** - * Close the socket descriptor. - * - * @param sockfd The socket descriptor. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_close(pj_sock_t sockfd); - - -/** - * This function gives the socket sockfd the local address my_addr. my_addr is - * addrlen bytes long. Traditionally, this is called assigning a name to - * a socket. When a socket is created with #pj_sock_socket(), it exists in a - * name space (address family) but has no name assigned. - * - * @param sockfd The socket desriptor. - * @param my_addr The local address to bind the socket to. - * @param addrlen The length of the address. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_bind( pj_sock_t sockfd, - const pj_sockaddr_t *my_addr, - int addrlen); - -/** - * Bind the IP socket sockfd to the given address and port. - * - * @param sockfd The socket descriptor. - * @param addr Local address to bind the socket to, in host byte order. - * @param port The local port to bind the socket to, in host byte order. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, - pj_uint32_t addr, - pj_uint16_t port); - -#if PJ_HAS_TCP -/** - * Listen for incoming connection. This function only applies to connection - * oriented sockets (such as PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET), and it - * indicates the willingness to accept incoming connections. - * - * @param sockfd The socket descriptor. - * @param backlog Defines the maximum length the queue of pending - * connections may grow to. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_listen( pj_sock_t sockfd, - int backlog ); - -/** - * Accept new connection on the specified connection oriented server socket. - * - * @param serverfd The server socket. - * @param newsock New socket on success, of PJ_INVALID_SOCKET if failed. - * @param addr A pointer to sockaddr type. If the argument is not NULL, - * it will be filled by the address of connecting entity. - * @param addrlen Initially specifies the length of the address, and upon - * return will be filled with the exact address length. - * - * @return Zero on success, or the error number. - */ -PJ_DECL(pj_status_t) pj_sock_accept( pj_sock_t serverfd, - pj_sock_t *newsock, - pj_sockaddr_t *addr, - int *addrlen); -#endif - -/** - * The file descriptor sockfd must refer to a socket. If the socket is of - * type PJ_SOCK_DGRAM then the serv_addr address is the address to which - * datagrams are sent by default, and the only address from which datagrams - * are received. If the socket is of type PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET, - * this call attempts to make a connection to another socket. The - * other socket is specified by serv_addr, which is an address (of length - * addrlen) in the communications space of the socket. Each communications - * space interprets the serv_addr parameter in its own way. - * - * @param sockfd The socket descriptor. - * @param serv_addr Server address to connect to. - * @param addrlen The length of server address. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_connect( pj_sock_t sockfd, - const pj_sockaddr_t *serv_addr, - int addrlen); - -/** - * Return the address of peer which is connected to socket sockfd. - * - * @param sockfd The socket descriptor. - * @param addr Pointer to sockaddr structure to which the address - * will be returned. - * @param namelen Initially the length of the addr. Upon return the value - * will be set to the actual length of the address. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_getpeername(pj_sock_t sockfd, - pj_sockaddr_t *addr, - int *namelen); - -/** - * Return the current name of the specified socket. - * - * @param sockfd The socket descriptor. - * @param addr Pointer to sockaddr structure to which the address - * will be returned. - * @param namelen Initially the length of the addr. Upon return the value - * will be set to the actual length of the address. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, - pj_sockaddr_t *addr, - int *namelen); - -/** - * Get socket option associated with a socket. Options may exist at multiple - * protocol levels; they are always present at the uppermost socket level. - * - * @param sockfd The socket descriptor. - * @param level The level which to get the option from. - * @param optname The option name, which will be passed uninterpreted - * by the library. - * @param optval Identifies the buffer which the value will be - * returned. - * @param optlen Initially contains the length of the buffer, upon - * return will be set to the actual size of the value. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, - int level, - int optname, - void *optval, - int *optlen); -/** - * Manipulate the options associated with a socket. Options may exist at - * multiple protocol levels; they are always present at the uppermost socket - * level. - * - * @param sockfd The socket descriptor. - * @param level The level which to get the option from. - * @param optname The option name, which will be passed uninterpreted - * by the library. - * @param optval Identifies the buffer which contain the value. - * @param optlen The length of the value. - * - * @return PJ_SUCCESS or the status code. - */ -PJ_DECL(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, - int level, - int optname, - const void *optval, - int optlen); - - -/** - * Receives data stream or message coming to the specified socket. - * - * @param sockfd The socket descriptor. - * @param buf The buffer to receive the data or message. - * @param len On input, the length of the buffer. On return, - * contains the length of data received. - * @param flags Combination of #pj_sock_msg_flag. - * - * @return PJ_SUCCESS or the error code. - */ -PJ_DECL(pj_status_t) pj_sock_recv(pj_sock_t sockfd, - void *buf, - pj_ssize_t *len, - unsigned flags); - -/** - * Receives data stream or message coming to the specified socket. - * - * @param sockfd The socket descriptor. - * @param buf The buffer to receive the data or message. - * @param len On input, the length of the buffer. On return, - * contains the length of data received. - * @param flags Bitmask combination of #pj_sock_msg_flag. - * @param from If not NULL, it will be filled with the source - * address of the connection. - * @param fromlen Initially contains the length of from address, - * and upon return will be filled with the actual - * length of the address. - * - * @return PJ_SUCCESS or the error code. - */ -PJ_DECL(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, - void *buf, - pj_ssize_t *len, - unsigned flags, - pj_sockaddr_t *from, - int *fromlen); - -/** - * Transmit data to the socket. - * - * @param sockfd Socket descriptor. - * @param buf Buffer containing data to be sent. - * @param len On input, the length of the data in the buffer. - * Upon return, it will be filled with the length - * of data sent. - * @param flags Bitmask combination of #pj_sock_msg_flag. - * - * @return PJ_SUCCESS or the status code. - */ -PJ_DECL(pj_status_t) pj_sock_send(pj_sock_t sockfd, - const void *buf, - pj_ssize_t *len, - unsigned flags); - -/** - * Transmit data to the socket to the specified address. - * - * @param sockfd Socket descriptor. - * @param buf Buffer containing data to be sent. - * @param len On input, the length of the data in the buffer. - * Upon return, it will be filled with the length - * of data sent. - * @param flags Bitmask combination of #pj_sock_msg_flag. - * @param to The address to send. - * @param tolen The length of the address in bytes. - * - * @return The length of data successfully sent. - */ -PJ_DECL(pj_status_t) pj_sock_sendto(pj_sock_t sockfd, - const void *buf, - pj_ssize_t *len, - unsigned flags, - const pj_sockaddr_t *to, - int tolen); - -#if PJ_HAS_TCP -/** - * The shutdown call causes all or part of a full-duplex connection on the - * socket associated with sockfd to be shut down. - * - * @param sockfd The socket descriptor. - * @param how If how is PJ_SHUT_RD, further receptions will be - * disallowed. If how is PJ_SHUT_WR, further transmissions - * will be disallowed. If how is PJ_SHUT_RDWR, further - * receptions andtransmissions will be disallowed. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, - int how); -#endif - -/** - * @} - */ - - -PJ_END_DECL - -#endif /* __PJ_SOCK_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/sock.h 10 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_SOCK_H__ +#define __PJ_SOCK_H__ + +/** + * @file sock.h + * @brief Socket Abstraction. + */ + +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJ_SOCK Socket Abstraction + * @ingroup PJ_IO + * @{ + * + * The PJLIB socket abstraction layer is a thin and very portable abstraction + * for socket API. It provides API similar to BSD socket API. The abstraction + * is needed because BSD socket API is not always available on all platforms, + * therefore it wouldn't be possible to create a trully portable network + * programs unless we provide such abstraction. + * + * Applications can use this API directly in their application, just + * as they would when using traditional BSD socket API, provided they + * call #pj_init() first. + * + * \section pj_sock_examples_sec Examples + * + * For some examples on how to use the socket API, please see: + * + * - \ref page_pjlib_sock_test + * - \ref page_pjlib_select_test + * - \ref page_pjlib_sock_perf_test + */ + + +/** + * Supported address families. + * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL AF_*, BECAUSE + * THE LIBRARY WILL DO TRANSLATION TO THE NATIVE VALUE. + */ +extern const pj_uint16_t PJ_AF_UNIX; /**< Unix domain socket. */ +#define PJ_AF_LOCAL PJ_AF_UNIX; /**< POSIX name for AF_UNIX */ +extern const pj_uint16_t PJ_AF_INET; /**< Internet IP protocol. */ +extern const pj_uint16_t PJ_AF_INET6; /**< IP version 6. */ +extern const pj_uint16_t PJ_AF_PACKET; /**< Packet family. */ +extern const pj_uint16_t PJ_AF_IRDA; /**< IRDA sockets. */ + + +/** + * Supported types of sockets. + * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOCK_*, BECAUSE + * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. + */ + +extern const pj_uint16_t PJ_SOCK_STREAM; /**< Sequenced, reliable, connection- + based byte streams. */ +extern const pj_uint16_t PJ_SOCK_DGRAM; /**< Connectionless, unreliable + datagrams of fixed maximum + lengths. */ +extern const pj_uint16_t PJ_SOCK_RAW; /**< Raw protocol interface. */ +extern const pj_uint16_t PJ_SOCK_RDM; /**< Reliably-delivered messages. */ + + +/** + * Socket level specified in #pj_sock_setsockopt(). + * APPLICATION MUST USE THESE VALUES INSTEAD OF NORMAL SOL_*, BECAUSE + * THE LIBRARY WILL TRANSLATE THE VALUE TO THE NATIVE VALUE. + */ +extern const pj_uint16_t PJ_SOL_SOCKET; /**< Socket level. */ +extern const pj_uint16_t PJ_SOL_IP; /**< IP level. */ +extern const pj_uint16_t PJ_SOL_TCP; /**< TCP level. */ +extern const pj_uint16_t PJ_SOL_UDP; /**< UDP level. */ +extern const pj_uint16_t PJ_SOL_IPV6; /**< IP version 6 */ + +/** + * Flags to be specified in #pj_sock_recv, #pj_sock_send, etc. + */ +typedef enum pj_sock_msg_flag +{ + PJ_MSG_OOB = 0x01, /**< Out-of-band messages. */ + PJ_MSG_PEEK = 0x02, /**< Peek, don't remove from buffer. */ + PJ_MSG_DONTROUTE = 0x04, /**< Don't route. */ +} pj_sock_msg_flag; + + +/** + * Flag to be specified in #pj_sock_shutdown. + */ +typedef enum pj_socket_sd_type +{ + PJ_SD_RECEIVE = 0, /**< No more receive. */ + PJ_SHUT_RD = 0, /**< Alias for SD_RECEIVE. */ + PJ_SD_SEND = 1, /**< No more sending. */ + PJ_SHUT_WR = 1, /**< Alias for SD_SEND. */ + PJ_SD_BOTH = 2, /**< No more send and receive. */ + PJ_SHUT_RDWR = 2, /**< Alias for SD_BOTH. */ +} pj_socket_sd_type; + + + +/** Address to accept any incoming messages. */ +#define PJ_INADDR_ANY ((pj_uint32_t)0) + +/** Address indicating an error return */ +#define PJ_INADDR_NONE ((pj_uint32_t)0xffffffff) + +/** Address to send to all hosts. */ +#define PJ_INADDR_BROADCAST ((pj_uint32_t)0xffffffff) + + +/** + * Maximum length specifiable by #pj_sock_listen(). + * If the build system doesn't override this value, then the lowest + * denominator (five, in Win32 systems) will be used. + */ +#if !defined(PJ_SOMAXCONN) +# define PJ_SOMAXCONN 5 +#endif + + +/** + * Constant for invalid socket returned by #pj_sock_socket() and + * #pj_sock_accept(). + */ +#define PJ_INVALID_SOCKET (-1) + +/** + * Structure describing a generic socket address. + */ +typedef struct pj_sockaddr +{ + pj_uint16_t sa_family; /**< Common data: address family. */ + char sa_data[14]; /**< Address data. */ +} pj_sockaddr; + + +/** + * This structure describes Internet address. + */ +typedef struct pj_in_addr +{ + pj_uint32_t s_addr; /**< The 32bit IP address. */ +} pj_in_addr; + + +/** + * This structure describes Internet socket address. + */ +typedef struct pj_sockaddr_in +{ + pj_uint16_t sin_family; /**< Address family. */ + pj_uint16_t sin_port; /**< Transport layer port number. */ + pj_in_addr sin_addr; /**< IP address. */ + char sin_zero[8]; /**< Padding. */ +} pj_sockaddr_in; + + +/** + * This structure describes IPv6 address. + */ +typedef struct pj_in6_addr +{ + /** Union of address formats. */ + union { + pj_uint8_t u6_addr8[16]; /**< u6_addr8 */ + pj_uint16_t u6_addr16[8]; /**< u6_addr16 */ + pj_uint32_t u6_addr32[4]; /**< u6_addr32 */ + } in6_u; +/** Shortcut to access in6_u.u6_addr8. */ +#define s6_addr in6_u.u6_addr8 +/** Shortcut to access in6_u.u6_addr16. */ +#define s6_addr16 in6_u.u6_addr16 +/** Shortcut to access in6_u.u6_addr32. */ +#define s6_addr32 in6_u.u6_addr32 +} pj_in6_addr; + +/** Initializer value for pj_in6_addr. */ +#define PJ_IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } + +/** Initializer value for pj_in6_addr. */ +#define PJ_IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } + +/** + * This structure describes IPv6 socket address. + */ +typedef struct pj_sockaddr_in6 +{ + pj_uint16_t sin6_family; /**< Address family */ + pj_uint16_t sin6_port; /**< Transport layer port number. */ + pj_uint32_t sin6_flowinfo; /**< IPv6 flow information */ + pj_in6_addr sin6_addr; /**< IPv6 address. */ + pj_uint32_t sin6_scope_id; /**< IPv6 scope-id */ +} pj_sockaddr_in6; + + +/***************************************************************************** + * + * SOCKET ADDRESS MANIPULATION. + * + ***************************************************************************** + */ + +/** + * Convert 16-bit value from network byte order to host byte order. + * + * @param netshort 16-bit network value. + * @return 16-bit host value. + */ +PJ_DECL(pj_uint16_t) pj_ntohs(pj_uint16_t netshort); + +/** + * Convert 16-bit value from host byte order to network byte order. + * + * @param hostshort 16-bit host value. + * @return 16-bit network value. + */ +PJ_DECL(pj_uint16_t) pj_htons(pj_uint16_t hostshort); + +/** + * Convert 32-bit value from network byte order to host byte order. + * + * @param netlong 32-bit network value. + * @return 32-bit host value. + */ +PJ_DECL(pj_uint32_t) pj_ntohl(pj_uint32_t netlong); + +/** + * Convert 32-bit value from host byte order to network byte order. + * + * @param hostlong 32-bit host value. + * @return 32-bit network value. + */ +PJ_DECL(pj_uint32_t) pj_htonl(pj_uint32_t hostlong); + +/** + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + * + * @param inaddr The host address. + * @return The string address. + */ +PJ_DECL(char*) pj_inet_ntoa(pj_in_addr inaddr); + +/** + * This function converts the Internet host address cp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + * + * @param cp IP address in standard numbers-and-dots notation. + * @param inp Structure that holds the output of the conversion. + * + * @return nonzero if the address is valid, zero if not. + */ +PJ_DECL(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp); + +/** + * Convert address string with numbers and dots to binary IP address. + * + * @param cp The IP address in numbers and dots notation. + * @return If success, the IP address is returned in network + * byte order. If failed, PJ_INADDR_NONE will be + * returned. + * @remark + * This is an obsolete interface to #pj_inet_aton(); it is obsolete + * because -1 is a valid address (255.255.255.255), and #pj_inet_aton() + * provides a cleaner way to indicate error return. + */ +PJ_DECL(pj_in_addr) pj_inet_addr(const pj_str_t *cp); + + +/** + * Get the transport layer port number of an Internet socket address. + * The port is returned in host byte order. + * + * @param addr The IP socket address. + * @return Port number, in host byte order. + */ +PJ_INLINE(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr) +{ + return pj_ntohs(addr->sin_port); +} + +/** + * Set the port number of an Internet socket address. + * + * @param addr The IP socket address. + * @param hostport The port number, in host byte order. + */ +PJ_INLINE(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, + pj_uint16_t hostport) +{ + addr->sin_port = pj_htons(hostport); +} + +/** + * Get the IP address of an Internet socket address. + * The address is returned as 32bit value in host byte order. + * + * @param addr The IP socket address. + * @return 32bit address, in host byte order. + */ +PJ_INLINE(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr) +{ + pj_in_addr in_addr; + in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr); + return in_addr; +}; + +/** + * Set the IP address of an Internet socket address. + * + * @param addr The IP socket address. + * @param hostaddr The host address, in host byte order. + */ +PJ_INLINE(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr, + pj_uint32_t hostaddr) +{ + addr->sin_addr.s_addr = pj_htonl(hostaddr); +} + +/** + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + * + * @param addr The IP socket address to be set. + * @param cp The address string, which can be in a standard + * dotted numbers or a hostname to be resolved. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *cp); + +/** + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + * + * @param addr The IP socket address to be set. + * @param cp The address string, which can be in a standard + * dotted numbers or a hostname to be resolved. + * @param port The port number, in host byte order. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *cp, + pj_uint16_t port); + + +/***************************************************************************** + * + * HOST NAME AND ADDRESS. + * + ***************************************************************************** + */ + +/** + * Get system's host name. + * + * @return The hostname, or empty string if the hostname can not + * be identified. + */ +PJ_DECL(const pj_str_t*) pj_gethostname(void); + +/** + * Get host's IP address, which the the first IP address that is resolved + * from the hostname. + * + * @return The host's IP address, PJ_INADDR_NONE if the host + * IP address can not be identified. + */ +PJ_DECL(pj_in_addr) pj_gethostaddr(void); + + +/***************************************************************************** + * + * SOCKET API. + * + ***************************************************************************** + */ + +/** + * Create new socket/endpoint for communication. + * + * @param family Specifies a communication domain; this selects the + * protocol family which will be used for communication. + * @param type The socket has the indicated type, which specifies the + * communication semantics. + * @param protocol Specifies a particular protocol to be used with the + * socket. Normally only a single protocol exists to support + * a particular socket type within a given protocol family, + * in which a case protocol can be specified as 0. + * @param sock New socket descriptor, or PJ_INVALID_SOCKET on error. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_socket(int family, + int type, + int protocol, + pj_sock_t *sock); + +/** + * Close the socket descriptor. + * + * @param sockfd The socket descriptor. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_close(pj_sock_t sockfd); + + +/** + * This function gives the socket sockfd the local address my_addr. my_addr is + * addrlen bytes long. Traditionally, this is called assigning a name to + * a socket. When a socket is created with #pj_sock_socket(), it exists in a + * name space (address family) but has no name assigned. + * + * @param sockfd The socket desriptor. + * @param my_addr The local address to bind the socket to. + * @param addrlen The length of the address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_bind( pj_sock_t sockfd, + const pj_sockaddr_t *my_addr, + int addrlen); + +/** + * Bind the IP socket sockfd to the given address and port. + * + * @param sockfd The socket descriptor. + * @param addr Local address to bind the socket to, in host byte order. + * @param port The local port to bind the socket to, in host byte order. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, + pj_uint32_t addr, + pj_uint16_t port); + +#if PJ_HAS_TCP +/** + * Listen for incoming connection. This function only applies to connection + * oriented sockets (such as PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET), and it + * indicates the willingness to accept incoming connections. + * + * @param sockfd The socket descriptor. + * @param backlog Defines the maximum length the queue of pending + * connections may grow to. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_listen( pj_sock_t sockfd, + int backlog ); + +/** + * Accept new connection on the specified connection oriented server socket. + * + * @param serverfd The server socket. + * @param newsock New socket on success, of PJ_INVALID_SOCKET if failed. + * @param addr A pointer to sockaddr type. If the argument is not NULL, + * it will be filled by the address of connecting entity. + * @param addrlen Initially specifies the length of the address, and upon + * return will be filled with the exact address length. + * + * @return Zero on success, or the error number. + */ +PJ_DECL(pj_status_t) pj_sock_accept( pj_sock_t serverfd, + pj_sock_t *newsock, + pj_sockaddr_t *addr, + int *addrlen); +#endif + +/** + * The file descriptor sockfd must refer to a socket. If the socket is of + * type PJ_SOCK_DGRAM then the serv_addr address is the address to which + * datagrams are sent by default, and the only address from which datagrams + * are received. If the socket is of type PJ_SOCK_STREAM or PJ_SOCK_SEQPACKET, + * this call attempts to make a connection to another socket. The + * other socket is specified by serv_addr, which is an address (of length + * addrlen) in the communications space of the socket. Each communications + * space interprets the serv_addr parameter in its own way. + * + * @param sockfd The socket descriptor. + * @param serv_addr Server address to connect to. + * @param addrlen The length of server address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_connect( pj_sock_t sockfd, + const pj_sockaddr_t *serv_addr, + int addrlen); + +/** + * Return the address of peer which is connected to socket sockfd. + * + * @param sockfd The socket descriptor. + * @param addr Pointer to sockaddr structure to which the address + * will be returned. + * @param namelen Initially the length of the addr. Upon return the value + * will be set to the actual length of the address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_getpeername(pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen); + +/** + * Return the current name of the specified socket. + * + * @param sockfd The socket descriptor. + * @param addr Pointer to sockaddr structure to which the address + * will be returned. + * @param namelen Initially the length of the addr. Upon return the value + * will be set to the actual length of the address. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen); + +/** + * Get socket option associated with a socket. Options may exist at multiple + * protocol levels; they are always present at the uppermost socket level. + * + * @param sockfd The socket descriptor. + * @param level The level which to get the option from. + * @param optname The option name, which will be passed uninterpreted + * by the library. + * @param optval Identifies the buffer which the value will be + * returned. + * @param optlen Initially contains the length of the buffer, upon + * return will be set to the actual size of the value. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, + int level, + int optname, + void *optval, + int *optlen); +/** + * Manipulate the options associated with a socket. Options may exist at + * multiple protocol levels; they are always present at the uppermost socket + * level. + * + * @param sockfd The socket descriptor. + * @param level The level which to get the option from. + * @param optname The option name, which will be passed uninterpreted + * by the library. + * @param optval Identifies the buffer which contain the value. + * @param optlen The length of the value. + * + * @return PJ_SUCCESS or the status code. + */ +PJ_DECL(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, + int level, + int optname, + const void *optval, + int optlen); + + +/** + * Receives data stream or message coming to the specified socket. + * + * @param sockfd The socket descriptor. + * @param buf The buffer to receive the data or message. + * @param len On input, the length of the buffer. On return, + * contains the length of data received. + * @param flags Combination of #pj_sock_msg_flag. + * + * @return PJ_SUCCESS or the error code. + */ +PJ_DECL(pj_status_t) pj_sock_recv(pj_sock_t sockfd, + void *buf, + pj_ssize_t *len, + unsigned flags); + +/** + * Receives data stream or message coming to the specified socket. + * + * @param sockfd The socket descriptor. + * @param buf The buffer to receive the data or message. + * @param len On input, the length of the buffer. On return, + * contains the length of data received. + * @param flags Bitmask combination of #pj_sock_msg_flag. + * @param from If not NULL, it will be filled with the source + * address of the connection. + * @param fromlen Initially contains the length of from address, + * and upon return will be filled with the actual + * length of the address. + * + * @return PJ_SUCCESS or the error code. + */ +PJ_DECL(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, + void *buf, + pj_ssize_t *len, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen); + +/** + * Transmit data to the socket. + * + * @param sockfd Socket descriptor. + * @param buf Buffer containing data to be sent. + * @param len On input, the length of the data in the buffer. + * Upon return, it will be filled with the length + * of data sent. + * @param flags Bitmask combination of #pj_sock_msg_flag. + * + * @return PJ_SUCCESS or the status code. + */ +PJ_DECL(pj_status_t) pj_sock_send(pj_sock_t sockfd, + const void *buf, + pj_ssize_t *len, + unsigned flags); + +/** + * Transmit data to the socket to the specified address. + * + * @param sockfd Socket descriptor. + * @param buf Buffer containing data to be sent. + * @param len On input, the length of the data in the buffer. + * Upon return, it will be filled with the length + * of data sent. + * @param flags Bitmask combination of #pj_sock_msg_flag. + * @param to The address to send. + * @param tolen The length of the address in bytes. + * + * @return The length of data successfully sent. + */ +PJ_DECL(pj_status_t) pj_sock_sendto(pj_sock_t sockfd, + const void *buf, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *to, + int tolen); + +#if PJ_HAS_TCP +/** + * The shutdown call causes all or part of a full-duplex connection on the + * socket associated with sockfd to be shut down. + * + * @param sockfd The socket descriptor. + * @param how If how is PJ_SHUT_RD, further receptions will be + * disallowed. If how is PJ_SHUT_WR, further transmissions + * will be disallowed. If how is PJ_SHUT_RDWR, further + * receptions andtransmissions will be disallowed. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, + int how); +#endif + +/** + * @} + */ + + +PJ_END_DECL + +#endif /* __PJ_SOCK_H__ */ + diff --git a/pjlib/include/pj/sock_select.h b/pjlib/include/pj/sock_select.h index c54b31ce8..47f84082d 100644 --- a/pjlib/include/pj/sock_select.h +++ b/pjlib/include/pj/sock_select.h @@ -1,141 +1,141 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/sock_select.h 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/include/pj/sock_select.h $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/15/05 8:40p Bennylp - * Created. - */ -#ifndef __PJ_SELECT_H__ -#define __PJ_SELECT_H__ - -/** - * @file sock_select.h - * @brief Socket select(). - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_SOCK_SELECT Socket select() API. - * @ingroup PJ_IO - * @{ - * This module provides portable abstraction for \a select() like API. - * The abstraction is needed so that it can utilize various event - * dispatching mechanisms that are available across platforms. - * - * The API is very similar to normal \a select() usage. - * - * \section pj_sock_select_examples_sec Examples - * - * For some examples on how to use the select API, please see: - * - * - \ref page_pjlib_select_test - */ - -/** - * Portable structure declarations for pj_fd_set. - * The implementation of pj_sock_select() does not use this structure - * per-se, but instead it will use the native fd_set structure. However, - * we must make sure that the size of pj_fd_set_t can accomodate the - * native fd_set structure. - */ -typedef struct pj_fd_set_t -{ - pj_sock_t data[FD_SETSIZE + 4]; /**< Opaque buffer for fd_set */ -} pj_fd_set_t; - - -/** - * Initialize the descriptor set pointed to by fdsetp to the null set. - * - * @param fdsetp The descriptor set. - */ -PJ_DECL(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp); - - -/** - * Add the file descriptor fd to the set pointed to by fdsetp. - * If the file descriptor fd is already in this set, there shall be no effect - * on the set, nor will an error be returned. - * - * @param fd The socket descriptor. - * @param fdsetp The descriptor set. - */ -PJ_DECL(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp); - -/** - * Remove the file descriptor fd from the set pointed to by fdsetp. - * If fd is not a member of this set, there shall be no effect on the set, - * nor will an error be returned. - * - * @param fd The socket descriptor. - * @param fdsetp The descriptor set. - */ -PJ_DECL(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp); - - -/** - * Evaluate to non-zero if the file descriptor fd is a member of the set - * pointed to by fdsetp, and shall evaluate to zero otherwise. - * - * @param fd The socket descriptor. - * @param fdsetp The descriptor set. - * - * @return Nonzero if fd is member of the descriptor set. - */ -PJ_DECL(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp); - - -/** - * Get the number of descriptors in the set. - * - * @param fdsetp The descriptor set. - * - * @return Number of descriptors in the set. - */ -PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp); - - -/** - * This function wait for a number of file descriptors to change status. - * The behaviour is the same as select() function call which appear in - * standard BSD socket libraries. - * - * @param n On Unices, this specifies the highest-numbered - * descriptor in any of the three set, plus 1. On Windows, - * the value is ignored. - * @param readfds Optional pointer to a set of sockets to be checked for - * readability. - * @param writefds Optional pointer to a set of sockets to be checked for - * writability. - * @param exceptfds Optional pointer to a set of sockets to be checked for - * errors. - * @param timeout Maximum time for select to wait, or null for blocking - * operations. - * - * @return The total number of socket handles that are ready, or - * zero if the time limit expired, or -1 if an error occurred. - */ -PJ_DECL(int) pj_sock_select( int n, - pj_fd_set_t *readfds, - pj_fd_set_t *writefds, - pj_fd_set_t *exceptfds, - const pj_time_val *timeout); - - -/** - * @} - */ - - -PJ_END_DECL - -#endif /* __PJ_SELECT_H__ */ +/* $Header: /pjproject-0.3/pjlib/include/pj/sock_select.h 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/include/pj/sock_select.h $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/15/05 8:40p Bennylp + * Created. + */ +#ifndef __PJ_SELECT_H__ +#define __PJ_SELECT_H__ + +/** + * @file sock_select.h + * @brief Socket select(). + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_SOCK_SELECT Socket select() API. + * @ingroup PJ_IO + * @{ + * This module provides portable abstraction for \a select() like API. + * The abstraction is needed so that it can utilize various event + * dispatching mechanisms that are available across platforms. + * + * The API is very similar to normal \a select() usage. + * + * \section pj_sock_select_examples_sec Examples + * + * For some examples on how to use the select API, please see: + * + * - \ref page_pjlib_select_test + */ + +/** + * Portable structure declarations for pj_fd_set. + * The implementation of pj_sock_select() does not use this structure + * per-se, but instead it will use the native fd_set structure. However, + * we must make sure that the size of pj_fd_set_t can accomodate the + * native fd_set structure. + */ +typedef struct pj_fd_set_t +{ + pj_sock_t data[FD_SETSIZE + 4]; /**< Opaque buffer for fd_set */ +} pj_fd_set_t; + + +/** + * Initialize the descriptor set pointed to by fdsetp to the null set. + * + * @param fdsetp The descriptor set. + */ +PJ_DECL(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp); + + +/** + * Add the file descriptor fd to the set pointed to by fdsetp. + * If the file descriptor fd is already in this set, there shall be no effect + * on the set, nor will an error be returned. + * + * @param fd The socket descriptor. + * @param fdsetp The descriptor set. + */ +PJ_DECL(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp); + +/** + * Remove the file descriptor fd from the set pointed to by fdsetp. + * If fd is not a member of this set, there shall be no effect on the set, + * nor will an error be returned. + * + * @param fd The socket descriptor. + * @param fdsetp The descriptor set. + */ +PJ_DECL(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp); + + +/** + * Evaluate to non-zero if the file descriptor fd is a member of the set + * pointed to by fdsetp, and shall evaluate to zero otherwise. + * + * @param fd The socket descriptor. + * @param fdsetp The descriptor set. + * + * @return Nonzero if fd is member of the descriptor set. + */ +PJ_DECL(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp); + + +/** + * Get the number of descriptors in the set. + * + * @param fdsetp The descriptor set. + * + * @return Number of descriptors in the set. + */ +PJ_DECL(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp); + + +/** + * This function wait for a number of file descriptors to change status. + * The behaviour is the same as select() function call which appear in + * standard BSD socket libraries. + * + * @param n On Unices, this specifies the highest-numbered + * descriptor in any of the three set, plus 1. On Windows, + * the value is ignored. + * @param readfds Optional pointer to a set of sockets to be checked for + * readability. + * @param writefds Optional pointer to a set of sockets to be checked for + * writability. + * @param exceptfds Optional pointer to a set of sockets to be checked for + * errors. + * @param timeout Maximum time for select to wait, or null for blocking + * operations. + * + * @return The total number of socket handles that are ready, or + * zero if the time limit expired, or -1 if an error occurred. + */ +PJ_DECL(int) pj_sock_select( int n, + pj_fd_set_t *readfds, + pj_fd_set_t *writefds, + pj_fd_set_t *exceptfds, + const pj_time_val *timeout); + + +/** + * @} + */ + + +PJ_END_DECL + +#endif /* __PJ_SELECT_H__ */ diff --git a/pjlib/include/pj/string.h b/pjlib/include/pj/string.h index 4562addd1..c75bf2d2b 100644 --- a/pjlib/include/pj/string.h +++ b/pjlib/include/pj/string.h @@ -1,515 +1,515 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/string.h 8 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_STRING_H__ -#define __PJ_STRING_H__ - -/** - * @file string.h - * @brief PJLIB String Operations. - */ - -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_PSTR String Operations - * @ingroup PJ_DS - * @{ - * This module provides string manipulation API. - * - * \section pj_pstr_not_null_sec PJLIB String is NOT Null Terminated! - * - * That is the first information that developers need to know. Instead - * of using normal C string, strings in PJLIB are represented as - * pj_str_t structure below: - * - *
- *   typedef struct pj_str_t
- *   {
- *       char      *ptr;
- *       pj_size_t  slen;
- *   } pj_str_t;
- * 
- * - * There are some advantages of using this approach: - * - the string can point to arbitrary location in memory even - * if the string in that location is not null terminated. This is - * most usefull for text parsing, where the parsed text can just - * point to the original text in the input. If we use C string, - * then we will have to copy the text portion from the input - * to a string variable. - * - because the length of the string is known, string copy operation - * can be made more efficient. - * - * Most of APIs in PJLIB that expect or return string will represent - * the string as pj_str_t instead of normal C string. - * - * \section pj_pstr_examples_sec Examples - * - * For some examples, please see: - * - @ref page_pjlib_string_test - */ - -/** - * Create string initializer from a normal C string. - * - * @param str Null terminated string to be stored. - * - * @return pj_str_t. - */ -PJ_IDECL(pj_str_t) pj_str(char *str); - -/** - * Create constant string from normal C string. - * - * @param str The string to be initialized. - * @param s Null terminated string. - * - * @return pj_str_t. - */ -PJ_INLINE(const pj_str_t*) pj_cstr(pj_str_t *str, const char *s) -{ - str->ptr = (char*)s; - str->slen = s ? strlen(s) : 0; - return str; -} - -/** - * Set the pointer and length to the specified value. - * - * @param str the string. - * @param ptr pointer to set. - * @param length length to set. - * - * @return the string. - */ -PJ_INLINE(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length) -{ - str->ptr = ptr; - str->slen = length; - return str; -} - -/** - * Set the pointer and length of the string to the source string, which - * must be NULL terminated. - * - * @param str the string. - * @param src pointer to set. - * - * @return the string. - */ -PJ_INLINE(pj_str_t*) pj_strset2( pj_str_t *str, char *src) -{ - str->ptr = src; - str->slen = src ? strlen(src) : 0; - return str; -} - -/** - * Set the pointer and the length of the string. - * - * @param str The target string. - * @param begin The start of the string. - * @param end The end of the string. - * - * @return the target string. - */ -PJ_INLINE(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end ) -{ - str->ptr = begin; - str->slen = end-begin; - return str; -} - -/** - * Assign string. - * - * @param dst The target string. - * @param src The source string. - * - * @return the target string. - */ -PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ); - -/** - * Copy string contents. - * - * @param dst The target string. - * @param src The source string. - * - * @return the target string. - */ -PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src); - -/** - * Copy string contents. - * - * @param dst The target string. - * @param src The source string. - * - * @return the target string. - */ -PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src); - -/** - * Duplicate string. - * - * @param pool The pool. - * @param dst The string result. - * @param src The string to duplicate. - * - * @return the string result. - */ -PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool, - pj_str_t *dst, - const pj_str_t *src); - -/** - * Duplicate string and NULL terminate the destination string. - * - * @param pool - * @param dst - * @param src - */ -PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool, - pj_str_t *dst, - const pj_str_t *src); - -/** - * Duplicate string. - * - * @param pool The pool. - * @param dst The string result. - * @param src The string to duplicate. - * - * @return the string result. - */ -PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool, - pj_str_t *dst, - const char *src); - -/** - * Duplicate string. - * - * @param pool The pool. - * @param src The string to duplicate. - * - * @return the string result. - */ -PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src); - -/** - * Return the length of the string. - * - * @param str The string. - * - * @return the length of the string. - */ -PJ_INLINE(pj_size_t) pj_strlen( const pj_str_t *str ) -{ - return str->slen; -} - -/** - * Return the pointer to the string data. - * - * @param str The string. - * - * @return the pointer to the string buffer. - */ -PJ_INLINE(const char*) pj_strbuf( const pj_str_t *str ) -{ - return str->ptr; -} - -/** - * Compare strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2); - -/** - * Compare strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ); - -/** - * Compare strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * @param len The maximum number of characters to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, - pj_size_t len); - -/** - * Compare strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * @param len The maximum number of characters to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2, - pj_size_t len); - -/** - * Perform lowercase comparison to the strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2); - -/** - * Perform lowercase comparison to the strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2); - -/** - * Perform lowercase comparison to the strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * @param len The maximum number of characters to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, - pj_size_t len); - -/** - * Perform lowercase comparison to the strings. - * - * @param str1 The string to compare. - * @param str2 The string to compare. - * @param len The maximum number of characters to compare. - * - * @return - * - < 0 if str1 is less than str2 - * - 0 if str1 is identical to str2 - * - > 0 if str1 is greater than str2 - */ -PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, - pj_size_t len); - -/** - * Concatenate strings. - * - * @param dst The destination string. - * @param src The source string. - */ -PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src); - -/** - * Finds a character in a string. - * - * @param str The string. - * @param chr The character to find. - * - * @return the pointer to first character found, or NULL. - */ -PJ_INLINE(char*) pj_strchr( pj_str_t *str, int chr) -{ - return (char*) memchr(str->ptr, chr, str->slen); -} - -/** - * Remove (trim) leading whitespaces from the string. - * - * @param str The string. - * - * @return the string. - */ -PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str ); - -/** - * Remove (trim) the trailing whitespaces from the string. - * - * @param str The string. - * - * @return the string. - */ -PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str ); - -/** - * Remove (trim) leading and trailing whitespaces from the string. - * - * @param str The string. - * - * @return the string. - */ -PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str ); - -/** - * Initialize the buffer with some random string. - * - * @param str the string to store the result. - * @param length the length of the random string to generate. - * - * @return the string. - */ -PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length); - -/** - * Convert string to unsigned integer. - * - * @param str the string. - * - * @return the unsigned integer. - */ -PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str); - -/** - * Utility to convert unsigned integer to string. Note that the - * string will be NULL terminated. - * - * @param val the unsigned integer value. - * @param buf the buffer - * - * @return the number of characters written - */ -PJ_DECL(int) pj_utoa(unsigned long val, char *buf); - -/** - * Convert unsigned integer to string with minimum digits. Note that the - * string will be NULL terminated. - * - * @param val The unsigned integer value. - * @param buf The buffer. - * @param min_dig Minimum digits to be printed, or zero to specify no - * minimum digit. - * @param pad The padding character to be put in front of the string - * when the digits is less than minimum. - * - * @return the number of characters written. - */ -PJ_DECL(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad); - -/** - * Fill the memory location with value. - * - * @param dst The destination buffer. - * @param c Character to set. - * @param size The number of characters. - * - * @return the value of dst. - */ -PJ_INLINE(void*) pj_memset(void *dst, int c, pj_size_t size) -{ - return memset(dst, c, size); -} - -/** - * Copy buffer. - * - * @param dst The destination buffer. - * @param src The source buffer. - * @param size The size to copy. - * - * @return the destination buffer. - */ -PJ_INLINE(void*) pj_memcpy(void *dst, const void *src, pj_size_t size) -{ - return memcpy(dst, src, size); -} - -/** - * Move memory. - * - * @param dst The destination buffer. - * @param src The source buffer. - * @param size The size to copy. - * - * @return the destination buffer. - */ -PJ_INLINE(void*) pj_memmove(void *dst, const void *src, pj_size_t size) -{ - return memmove(dst, src, size); -} - -/** - * Compare buffers. - * - * @param buf1 The first buffer. - * @param buf2 The second buffer. - * @param size The size to compare. - * - * @return negative, zero, or positive value. - */ -PJ_INLINE(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size) -{ - return memcmp(buf1, buf2, size); -} - -/** - * Find character in the buffer. - * - * @param buf The buffer. - * @param c The character to find. - * @param size The size to check. - * - * @return the pointer to location where the character is found, or NULL if - * not found. - */ -PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size) -{ - return memchr(buf, c, size); -} - -/** - * @} - */ - -#if PJ_FUNCTIONS_ARE_INLINED -# include -#endif - -PJ_END_DECL - -#endif /* __PJ_STRING_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/string.h 8 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_STRING_H__ +#define __PJ_STRING_H__ + +/** + * @file string.h + * @brief PJLIB String Operations. + */ + +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_PSTR String Operations + * @ingroup PJ_DS + * @{ + * This module provides string manipulation API. + * + * \section pj_pstr_not_null_sec PJLIB String is NOT Null Terminated! + * + * That is the first information that developers need to know. Instead + * of using normal C string, strings in PJLIB are represented as + * pj_str_t structure below: + * + *
+ *   typedef struct pj_str_t
+ *   {
+ *       char      *ptr;
+ *       pj_size_t  slen;
+ *   } pj_str_t;
+ * 
+ * + * There are some advantages of using this approach: + * - the string can point to arbitrary location in memory even + * if the string in that location is not null terminated. This is + * most usefull for text parsing, where the parsed text can just + * point to the original text in the input. If we use C string, + * then we will have to copy the text portion from the input + * to a string variable. + * - because the length of the string is known, string copy operation + * can be made more efficient. + * + * Most of APIs in PJLIB that expect or return string will represent + * the string as pj_str_t instead of normal C string. + * + * \section pj_pstr_examples_sec Examples + * + * For some examples, please see: + * - @ref page_pjlib_string_test + */ + +/** + * Create string initializer from a normal C string. + * + * @param str Null terminated string to be stored. + * + * @return pj_str_t. + */ +PJ_IDECL(pj_str_t) pj_str(char *str); + +/** + * Create constant string from normal C string. + * + * @param str The string to be initialized. + * @param s Null terminated string. + * + * @return pj_str_t. + */ +PJ_INLINE(const pj_str_t*) pj_cstr(pj_str_t *str, const char *s) +{ + str->ptr = (char*)s; + str->slen = s ? strlen(s) : 0; + return str; +} + +/** + * Set the pointer and length to the specified value. + * + * @param str the string. + * @param ptr pointer to set. + * @param length length to set. + * + * @return the string. + */ +PJ_INLINE(pj_str_t*) pj_strset( pj_str_t *str, char *ptr, pj_size_t length) +{ + str->ptr = ptr; + str->slen = length; + return str; +} + +/** + * Set the pointer and length of the string to the source string, which + * must be NULL terminated. + * + * @param str the string. + * @param src pointer to set. + * + * @return the string. + */ +PJ_INLINE(pj_str_t*) pj_strset2( pj_str_t *str, char *src) +{ + str->ptr = src; + str->slen = src ? strlen(src) : 0; + return str; +} + +/** + * Set the pointer and the length of the string. + * + * @param str The target string. + * @param begin The start of the string. + * @param end The end of the string. + * + * @return the target string. + */ +PJ_INLINE(pj_str_t*) pj_strset3( pj_str_t *str, char *begin, char *end ) +{ + str->ptr = begin; + str->slen = end-begin; + return str; +} + +/** + * Assign string. + * + * @param dst The target string. + * @param src The source string. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ); + +/** + * Copy string contents. + * + * @param dst The target string. + * @param src The source string. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src); + +/** + * Copy string contents. + * + * @param dst The target string. + * @param src The source string. + * + * @return the target string. + */ +PJ_IDECL(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src); + +/** + * Duplicate string. + * + * @param pool The pool. + * @param dst The string result. + * @param src The string to duplicate. + * + * @return the string result. + */ +PJ_IDECL(pj_str_t*) pj_strdup(pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src); + +/** + * Duplicate string and NULL terminate the destination string. + * + * @param pool + * @param dst + * @param src + */ +PJ_IDECL(pj_str_t*) pj_strdup_with_null(pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src); + +/** + * Duplicate string. + * + * @param pool The pool. + * @param dst The string result. + * @param src The string to duplicate. + * + * @return the string result. + */ +PJ_IDECL(pj_str_t*) pj_strdup2(pj_pool_t *pool, + pj_str_t *dst, + const char *src); + +/** + * Duplicate string. + * + * @param pool The pool. + * @param src The string to duplicate. + * + * @return the string result. + */ +PJ_IDECL(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src); + +/** + * Return the length of the string. + * + * @param str The string. + * + * @return the length of the string. + */ +PJ_INLINE(pj_size_t) pj_strlen( const pj_str_t *str ) +{ + return str->slen; +} + +/** + * Return the pointer to the string data. + * + * @param str The string. + * + * @return the pointer to the string buffer. + */ +PJ_INLINE(const char*) pj_strbuf( const pj_str_t *str ) +{ + return str->ptr; +} + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2); + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ); + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len); + +/** + * Compare strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strncmp2( const pj_str_t *str1, const char *str2, + pj_size_t len); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_stricmp2( const pj_str_t *str1, const char *str2); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len); + +/** + * Perform lowercase comparison to the strings. + * + * @param str1 The string to compare. + * @param str2 The string to compare. + * @param len The maximum number of characters to compare. + * + * @return + * - < 0 if str1 is less than str2 + * - 0 if str1 is identical to str2 + * - > 0 if str1 is greater than str2 + */ +PJ_IDECL(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, + pj_size_t len); + +/** + * Concatenate strings. + * + * @param dst The destination string. + * @param src The source string. + */ +PJ_IDECL(void) pj_strcat(pj_str_t *dst, const pj_str_t *src); + +/** + * Finds a character in a string. + * + * @param str The string. + * @param chr The character to find. + * + * @return the pointer to first character found, or NULL. + */ +PJ_INLINE(char*) pj_strchr( pj_str_t *str, int chr) +{ + return (char*) memchr(str->ptr, chr, str->slen); +} + +/** + * Remove (trim) leading whitespaces from the string. + * + * @param str The string. + * + * @return the string. + */ +PJ_DECL(pj_str_t*) pj_strltrim( pj_str_t *str ); + +/** + * Remove (trim) the trailing whitespaces from the string. + * + * @param str The string. + * + * @return the string. + */ +PJ_DECL(pj_str_t*) pj_strrtrim( pj_str_t *str ); + +/** + * Remove (trim) leading and trailing whitespaces from the string. + * + * @param str The string. + * + * @return the string. + */ +PJ_IDECL(pj_str_t*) pj_strtrim( pj_str_t *str ); + +/** + * Initialize the buffer with some random string. + * + * @param str the string to store the result. + * @param length the length of the random string to generate. + * + * @return the string. + */ +PJ_DECL(char*) pj_create_random_string(char *str, pj_size_t length); + +/** + * Convert string to unsigned integer. + * + * @param str the string. + * + * @return the unsigned integer. + */ +PJ_DECL(unsigned long) pj_strtoul(const pj_str_t *str); + +/** + * Utility to convert unsigned integer to string. Note that the + * string will be NULL terminated. + * + * @param val the unsigned integer value. + * @param buf the buffer + * + * @return the number of characters written + */ +PJ_DECL(int) pj_utoa(unsigned long val, char *buf); + +/** + * Convert unsigned integer to string with minimum digits. Note that the + * string will be NULL terminated. + * + * @param val The unsigned integer value. + * @param buf The buffer. + * @param min_dig Minimum digits to be printed, or zero to specify no + * minimum digit. + * @param pad The padding character to be put in front of the string + * when the digits is less than minimum. + * + * @return the number of characters written. + */ +PJ_DECL(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad); + +/** + * Fill the memory location with value. + * + * @param dst The destination buffer. + * @param c Character to set. + * @param size The number of characters. + * + * @return the value of dst. + */ +PJ_INLINE(void*) pj_memset(void *dst, int c, pj_size_t size) +{ + return memset(dst, c, size); +} + +/** + * Copy buffer. + * + * @param dst The destination buffer. + * @param src The source buffer. + * @param size The size to copy. + * + * @return the destination buffer. + */ +PJ_INLINE(void*) pj_memcpy(void *dst, const void *src, pj_size_t size) +{ + return memcpy(dst, src, size); +} + +/** + * Move memory. + * + * @param dst The destination buffer. + * @param src The source buffer. + * @param size The size to copy. + * + * @return the destination buffer. + */ +PJ_INLINE(void*) pj_memmove(void *dst, const void *src, pj_size_t size) +{ + return memmove(dst, src, size); +} + +/** + * Compare buffers. + * + * @param buf1 The first buffer. + * @param buf2 The second buffer. + * @param size The size to compare. + * + * @return negative, zero, or positive value. + */ +PJ_INLINE(int) pj_memcmp(const void *buf1, const void *buf2, pj_size_t size) +{ + return memcmp(buf1, buf2, size); +} + +/** + * Find character in the buffer. + * + * @param buf The buffer. + * @param c The character to find. + * @param size The size to check. + * + * @return the pointer to location where the character is found, or NULL if + * not found. + */ +PJ_INLINE(void*) pj_memchr(const void *buf, int c, pj_size_t size) +{ + return memchr(buf, c, size); +} + +/** + * @} + */ + +#if PJ_FUNCTIONS_ARE_INLINED +# include +#endif + +PJ_END_DECL + +#endif /* __PJ_STRING_H__ */ + diff --git a/pjlib/include/pj/string_i.h b/pjlib/include/pj/string_i.h index 0c60623b2..17e6255de 100644 --- a/pjlib/include/pj/string_i.h +++ b/pjlib/include/pj/string_i.h @@ -1,162 +1,162 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/string_i.h 8 10/14/05 12:26a Bennylp $ */ - -PJ_IDEF(pj_str_t) pj_str(char *str) -{ - pj_str_t dst; - dst.ptr = str; - dst.slen = str ? strlen(str) : 0; - return dst; -} - -PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool, - pj_str_t *dst, - const pj_str_t *src) -{ - if (src->slen) { - dst->ptr = (char*)pj_pool_alloc(pool, src->slen); - pj_memcpy(dst->ptr, src->ptr, src->slen); - } - dst->slen = src->slen; - return dst; -} - -PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool, - pj_str_t *dst, - const pj_str_t *src) -{ - if (src->slen) { - dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1); - pj_memcpy(dst->ptr, src->ptr, src->slen); - } else { - dst->ptr = (char*)pj_pool_alloc(pool, 1); - } - dst->slen = src->slen; - dst->ptr[dst->slen] = '\0'; - return dst; -} - -PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool, - pj_str_t *dst, - const char *src) -{ - dst->slen = src ? strlen(src) : 0; - if (dst->slen) { - dst->ptr = (char*)pj_pool_alloc(pool, dst->slen); - pj_memcpy(dst->ptr, src, dst->slen); - } else { - dst->ptr = NULL; - } - return dst; -} - - -PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src) -{ - pj_str_t temp; - pj_strdup2(pool, &temp, src); - return temp; -} - -PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ) -{ - dst->ptr = src->ptr; - dst->slen = src->slen; - return dst; -} - -PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src) -{ - dst->slen = src->slen; - if (src->slen > 0) - pj_memcpy(dst->ptr, src->ptr, src->slen); - return dst; -} - -PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src) -{ - dst->slen = src ? strlen(src) : 0; - if (dst->slen > 0) - pj_memcpy(dst->ptr, src, dst->slen); - return dst; -} - -PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2) -{ - pj_ssize_t diff; - - diff = str1->slen - str2->slen; - if (diff) { - return (int)diff; - } else if (str1->ptr) { - return strncmp(str1->ptr, str2->ptr, str1->slen); - } else { - return 0; - } -} - -PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, - pj_size_t len) -{ - return (str1->ptr && str2->ptr) ? strncmp(str1->ptr, str2->ptr, len) : - (str1->ptr == str2->ptr ? 0 : 1); -} - -PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2, - pj_size_t len) -{ - return (str1->ptr && str2) ? strncmp(str1->ptr, str2, len) : - (str1->ptr==str2 ? 0 : 1); -} - -PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ) -{ - return pj_strncmp2( str1, str2, str1->slen); -} - -PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2) -{ - pj_ssize_t diff; - - diff = str1->slen - str2->slen; - if (diff) { - return (int)diff; - } else { - return strnicmp(str1->ptr, str2->ptr, str1->slen); - } -} - -PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2) -{ - return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, str1->slen) : - (str1->ptr==str2 ? 0 : 1); -} - -PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, - pj_size_t len) -{ - return (str1->ptr && str2->ptr) ? strnicmp(str1->ptr, str2->ptr, len) : - (str1->ptr == str2->ptr ? 0 : 1); -} - -PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, - pj_size_t len) -{ - return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, len) : - (str1->ptr == str2 ? 0 : 1); -} - -PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src) -{ - if (src->slen) { - pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen); - dst->slen += src->slen; - } -} - -PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str ) -{ - pj_strltrim(str); - pj_strrtrim(str); - return str; -} - +/* $Header: /pjproject-0.3/pjlib/include/pj/string_i.h 8 10/14/05 12:26a Bennylp $ */ + +PJ_IDEF(pj_str_t) pj_str(char *str) +{ + pj_str_t dst; + dst.ptr = str; + dst.slen = str ? strlen(str) : 0; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strdup(pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src) +{ + if (src->slen) { + dst->ptr = (char*)pj_pool_alloc(pool, src->slen); + pj_memcpy(dst->ptr, src->ptr, src->slen); + } + dst->slen = src->slen; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strdup_with_null( pj_pool_t *pool, + pj_str_t *dst, + const pj_str_t *src) +{ + if (src->slen) { + dst->ptr = (char*)pj_pool_alloc(pool, src->slen+1); + pj_memcpy(dst->ptr, src->ptr, src->slen); + } else { + dst->ptr = (char*)pj_pool_alloc(pool, 1); + } + dst->slen = src->slen; + dst->ptr[dst->slen] = '\0'; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strdup2(pj_pool_t *pool, + pj_str_t *dst, + const char *src) +{ + dst->slen = src ? strlen(src) : 0; + if (dst->slen) { + dst->ptr = (char*)pj_pool_alloc(pool, dst->slen); + pj_memcpy(dst->ptr, src, dst->slen); + } else { + dst->ptr = NULL; + } + return dst; +} + + +PJ_IDEF(pj_str_t) pj_strdup3(pj_pool_t *pool, const char *src) +{ + pj_str_t temp; + pj_strdup2(pool, &temp, src); + return temp; +} + +PJ_IDEF(pj_str_t*) pj_strassign( pj_str_t *dst, pj_str_t *src ) +{ + dst->ptr = src->ptr; + dst->slen = src->slen; + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strcpy(pj_str_t *dst, const pj_str_t *src) +{ + dst->slen = src->slen; + if (src->slen > 0) + pj_memcpy(dst->ptr, src->ptr, src->slen); + return dst; +} + +PJ_IDEF(pj_str_t*) pj_strcpy2(pj_str_t *dst, const char *src) +{ + dst->slen = src ? strlen(src) : 0; + if (dst->slen > 0) + pj_memcpy(dst->ptr, src, dst->slen); + return dst; +} + +PJ_IDEF(int) pj_strcmp( const pj_str_t *str1, const pj_str_t *str2) +{ + pj_ssize_t diff; + + diff = str1->slen - str2->slen; + if (diff) { + return (int)diff; + } else if (str1->ptr) { + return strncmp(str1->ptr, str2->ptr, str1->slen); + } else { + return 0; + } +} + +PJ_IDEF(int) pj_strncmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len) +{ + return (str1->ptr && str2->ptr) ? strncmp(str1->ptr, str2->ptr, len) : + (str1->ptr == str2->ptr ? 0 : 1); +} + +PJ_IDEF(int) pj_strncmp2( const pj_str_t *str1, const char *str2, + pj_size_t len) +{ + return (str1->ptr && str2) ? strncmp(str1->ptr, str2, len) : + (str1->ptr==str2 ? 0 : 1); +} + +PJ_IDEF(int) pj_strcmp2( const pj_str_t *str1, const char *str2 ) +{ + return pj_strncmp2( str1, str2, str1->slen); +} + +PJ_IDEF(int) pj_stricmp( const pj_str_t *str1, const pj_str_t *str2) +{ + pj_ssize_t diff; + + diff = str1->slen - str2->slen; + if (diff) { + return (int)diff; + } else { + return strnicmp(str1->ptr, str2->ptr, str1->slen); + } +} + +PJ_IDEF(int) pj_stricmp2( const pj_str_t *str1, const char *str2) +{ + return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, str1->slen) : + (str1->ptr==str2 ? 0 : 1); +} + +PJ_IDEF(int) pj_strnicmp( const pj_str_t *str1, const pj_str_t *str2, + pj_size_t len) +{ + return (str1->ptr && str2->ptr) ? strnicmp(str1->ptr, str2->ptr, len) : + (str1->ptr == str2->ptr ? 0 : 1); +} + +PJ_IDEF(int) pj_strnicmp2( const pj_str_t *str1, const char *str2, + pj_size_t len) +{ + return (str1->ptr && str2) ? strnicmp(str1->ptr, str2, len) : + (str1->ptr == str2 ? 0 : 1); +} + +PJ_IDEF(void) pj_strcat(pj_str_t *dst, const pj_str_t *src) +{ + if (src->slen) { + pj_memcpy(dst->ptr + dst->slen, src->ptr, src->slen); + dst->slen += src->slen; + } +} + +PJ_IDEF(pj_str_t*) pj_strtrim( pj_str_t *str ) +{ + pj_strltrim(str); + pj_strrtrim(str); + return str; +} + diff --git a/pjlib/include/pj/stun.h b/pjlib/include/pj/stun.h index 999dda42a..90c20ec6f 100644 --- a/pjlib/include/pj/stun.h +++ b/pjlib/include/pj/stun.h @@ -1,123 +1,123 @@ -#ifndef __PJ_STUN_H__ -#define __PJ_STUN_H__ - -#include -#include - -PJ_BEGIN_DECL - -#define PJ_STUN_MAX_ATTR 16 - -typedef enum pj_stun_msg_type -{ - PJ_STUN_BINDING_REQUEST = 0x0001, - PJ_STUN_BINDING_RESPONSE = 0x0101, - PJ_STUN_BINDING_ERROR_RESPONSE = 0x0111, - PJ_STUN_SHARED_SECRET_REQUEST = 0x0002, - PJ_STUN_SHARED_SECRET_RESPONSE = 0x0102, - PJ_STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112 -} pj_stun_msg_type; - -typedef enum pj_stun_attr_type -{ - PJ_STUN_ATTR_MAPPED_ADDR = 1, - PJ_STUN_ATTR_RESPONSE_ADDR, - PJ_STUN_ATTR_CHANGE_REQUEST, - PJ_STUN_ATTR_SOURCE_ADDR, - PJ_STUN_ATTR_CHANGED_ADDR, - PJ_STUN_ATTR_USERNAME, - PJ_STUN_ATTR_PASSWORD, - PJ_STUN_ATTR_MESSAGE_INTEGRITY, - PJ_STUN_ATTR_ERROR_CODE, - PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, - PJ_STUN_ATTR_REFLECTED_FORM -} pj_stun_attr_type; - -typedef struct pj_stun_msg_hdr -{ - pj_uint16_t type; - pj_uint16_t length; - pj_uint32_t tsx[4]; -} pj_stun_msg_hdr; - -typedef struct pj_stun_attr_hdr -{ - pj_uint16_t type; - pj_uint16_t length; -} pj_stun_attr_hdr; - -typedef struct pj_stun_mapped_addr_attr -{ - pj_stun_attr_hdr hdr; - pj_uint8_t ignored; - pj_uint8_t family; - pj_uint16_t port; - pj_uint32_t addr; -} pj_stun_mapped_addr_attr; - -typedef pj_stun_mapped_addr_attr pj_stun_response_addr_attr; -typedef pj_stun_mapped_addr_attr pj_stun_changed_addr_attr; -typedef pj_stun_mapped_addr_attr pj_stun_src_addr_attr; -typedef pj_stun_mapped_addr_attr pj_stun_reflected_form_attr; - -typedef struct pj_stun_change_request_attr -{ - pj_stun_attr_hdr hdr; - pj_uint32_t value; -} pj_stun_change_request_attr; - -typedef struct pj_stun_username_attr -{ - pj_stun_attr_hdr hdr; - pj_uint32_t value[1]; -} pj_stun_username_attr; - -typedef pj_stun_username_attr pj_stun_password_attr; - -typedef struct pj_stun_error_code_attr -{ - pj_stun_attr_hdr hdr; - pj_uint16_t ignored; - pj_uint8_t err_class; - pj_uint8_t number; - char reason[4]; -} pj_stun_error_code_attr; - -typedef struct pj_stun_msg -{ - pj_stun_msg_hdr *hdr; - int attr_count; - pj_stun_attr_hdr *attr[PJ_STUN_MAX_ATTR]; -} pj_stun_msg; - -/* STUN message API (stun.c). */ - -PJ_DECL(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, - void **msg, pj_size_t *len, - pj_uint32_t id_hi, - pj_uint32_t id_lo); -PJ_DECL(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len, - pj_stun_msg *msg); -PJ_DECL(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t); - -/* STUN simple client API (stun_client.c) */ -enum pj_stun_err_code { - PJ_STUN_ERR_MEMORY = (-2), - PJ_STUN_ERR_RESOLVE = (-3), - PJ_STUN_ERR_TRANSPORT = (-4), - PJ_STUN_ERR_INVALID_MSG = (-5), - PJ_STUN_ERR_NO_RESPONSE = (-6), - PJ_STUN_ERR_SYMETRIC = (-7), -}; - -PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf, - int sock_cnt, pj_sock_t sock[], - const pj_str_t *srv1, int port1, - const pj_str_t *srv2, int port2, - pj_sockaddr_in mapped_addr[]); -PJ_DECL(const char*) pj_stun_get_err_msg(pj_status_t status); - -PJ_END_DECL - -#endif /* __PJ_STUN_H__ */ - +#ifndef __PJ_STUN_H__ +#define __PJ_STUN_H__ + +#include +#include + +PJ_BEGIN_DECL + +#define PJ_STUN_MAX_ATTR 16 + +typedef enum pj_stun_msg_type +{ + PJ_STUN_BINDING_REQUEST = 0x0001, + PJ_STUN_BINDING_RESPONSE = 0x0101, + PJ_STUN_BINDING_ERROR_RESPONSE = 0x0111, + PJ_STUN_SHARED_SECRET_REQUEST = 0x0002, + PJ_STUN_SHARED_SECRET_RESPONSE = 0x0102, + PJ_STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112 +} pj_stun_msg_type; + +typedef enum pj_stun_attr_type +{ + PJ_STUN_ATTR_MAPPED_ADDR = 1, + PJ_STUN_ATTR_RESPONSE_ADDR, + PJ_STUN_ATTR_CHANGE_REQUEST, + PJ_STUN_ATTR_SOURCE_ADDR, + PJ_STUN_ATTR_CHANGED_ADDR, + PJ_STUN_ATTR_USERNAME, + PJ_STUN_ATTR_PASSWORD, + PJ_STUN_ATTR_MESSAGE_INTEGRITY, + PJ_STUN_ATTR_ERROR_CODE, + PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, + PJ_STUN_ATTR_REFLECTED_FORM +} pj_stun_attr_type; + +typedef struct pj_stun_msg_hdr +{ + pj_uint16_t type; + pj_uint16_t length; + pj_uint32_t tsx[4]; +} pj_stun_msg_hdr; + +typedef struct pj_stun_attr_hdr +{ + pj_uint16_t type; + pj_uint16_t length; +} pj_stun_attr_hdr; + +typedef struct pj_stun_mapped_addr_attr +{ + pj_stun_attr_hdr hdr; + pj_uint8_t ignored; + pj_uint8_t family; + pj_uint16_t port; + pj_uint32_t addr; +} pj_stun_mapped_addr_attr; + +typedef pj_stun_mapped_addr_attr pj_stun_response_addr_attr; +typedef pj_stun_mapped_addr_attr pj_stun_changed_addr_attr; +typedef pj_stun_mapped_addr_attr pj_stun_src_addr_attr; +typedef pj_stun_mapped_addr_attr pj_stun_reflected_form_attr; + +typedef struct pj_stun_change_request_attr +{ + pj_stun_attr_hdr hdr; + pj_uint32_t value; +} pj_stun_change_request_attr; + +typedef struct pj_stun_username_attr +{ + pj_stun_attr_hdr hdr; + pj_uint32_t value[1]; +} pj_stun_username_attr; + +typedef pj_stun_username_attr pj_stun_password_attr; + +typedef struct pj_stun_error_code_attr +{ + pj_stun_attr_hdr hdr; + pj_uint16_t ignored; + pj_uint8_t err_class; + pj_uint8_t number; + char reason[4]; +} pj_stun_error_code_attr; + +typedef struct pj_stun_msg +{ + pj_stun_msg_hdr *hdr; + int attr_count; + pj_stun_attr_hdr *attr[PJ_STUN_MAX_ATTR]; +} pj_stun_msg; + +/* STUN message API (stun.c). */ + +PJ_DECL(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, + void **msg, pj_size_t *len, + pj_uint32_t id_hi, + pj_uint32_t id_lo); +PJ_DECL(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len, + pj_stun_msg *msg); +PJ_DECL(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t); + +/* STUN simple client API (stun_client.c) */ +enum pj_stun_err_code { + PJ_STUN_ERR_MEMORY = (-2), + PJ_STUN_ERR_RESOLVE = (-3), + PJ_STUN_ERR_TRANSPORT = (-4), + PJ_STUN_ERR_INVALID_MSG = (-5), + PJ_STUN_ERR_NO_RESPONSE = (-6), + PJ_STUN_ERR_SYMETRIC = (-7), +}; + +PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf, + int sock_cnt, pj_sock_t sock[], + const pj_str_t *srv1, int port1, + const pj_str_t *srv2, int port2, + pj_sockaddr_in mapped_addr[]); +PJ_DECL(const char*) pj_stun_get_err_msg(pj_status_t status); + +PJ_END_DECL + +#endif /* __PJ_STUN_H__ */ + diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h index 03e4be3f1..0fb5588c8 100644 --- a/pjlib/include/pj/timer.h +++ b/pjlib/include/pj/timer.h @@ -1,237 +1,237 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/timer.h 7 10/14/05 12:26a Bennylp $ */ -/* (C)1993-2003 Douglas C. Schmidt - * - * This file is originaly from ACE library by Doug Schmidt - * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research - * group at Washington University, University of California, Irvine, and Vanderbilt - * University Copyright (c) 1993-2003, all rights reserved. - * - */ - -#ifndef __PJ_TIMER_H__ -#define __PJ_TIMER_H__ - -/** - * @file timer.h - * @brief Timer Heap - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_TIMER Timer Heap Management. - * @ingroup PJ_MISC - * @brief - * The timer scheduling implementation here is based on ACE library's - * ACE_Timer_Heap, with only little modification to suit our library's style - * (I even left most of the comments in the original source). - * - * To quote the original quote in ACE_Timer_Heap_T class: - * - * This implementation uses a heap-based callout queue of - * absolute times. Therefore, in the average and worst case, - * scheduling, canceling, and expiring timers is O(log N) (where - * N is the total number of timers). In addition, we can also - * preallocate as many \a ACE_Timer_Nodes as there are slots in - * the heap. This allows us to completely remove the need for - * dynamic memory allocation, which is important for real-time - * systems. - * @{ - * - * \section pj_timer_examples_sec Examples - * - * For some examples on how to use the timer heap, please see the link below. - * - * - \ref page_pjlib_timer_test - */ - - -/** - * The type for internal timer ID. - */ -typedef int pj_timer_id_t; - -/** - * Forward declaration for pj_timer_entry. - */ -struct pj_timer_entry; - -/** - * The type of callback function to be called by timer scheduler when a timer - * has expired. - * - * @param timer_heap The timer heap. - * @param entry Timer entry which timer's has expired. - */ -typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap, - struct pj_timer_entry *entry); - - -/** - * This structure represents an entry to the timer. - */ -struct pj_timer_entry -{ - /** - * User data to be associated with this entry. - * Applications normally will put the instance of object that - * owns the timer entry in this field. - */ - void *user_data; - - /** - * Arbitrary ID assigned by the user/owner of this entry. - * Applications can use this ID to distinguish multiple - * timer entries that share the same callback and user_data. - */ - int id; - - /** - * Callback to be called when the timer expires. - */ - pj_timer_heap_callback *cb; - - /** - * Internal unique timer ID, which is assigned by the timer heap. - * Application should not touch this ID. - */ - pj_timer_id_t _timer_id; - - /** - * The future time when the timer expires, which the value is updated - * by timer heap when the timer is scheduled. - */ - pj_time_val _timer_value; -}; - - -/** - * Default flag for timer heap, indicates that synchronization will be - * used. - */ -#define PJ_TIMER_HEAP_SYNCHRONIZE (0) - -/** - * Flag to indicate that thread synchronization is NOT needed for the - * timer heap. - */ -#define PJ_TIMER_HEAP_NO_SYNCHRONIZE (1) - -/** - * Calculate memory size required to create a timer heap. - * - * @param count Number of timer entries to be supported. - * @return Memory size requirement in bytes. - */ -PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count); - -/** - * Create a timer heap. - * - * @param pool The pool where allocations in the timer heap will be - * allocated. The timer heap will dynamicly allocate - * more storate from the pool if the number of timer - * entries registered is more than the size originally - * requested when calling this function. - * @param count The maximum number of timer entries to be supported - * initially. If the application registers more entries - * during runtime, then the timer heap will resize. - * @param flag Creation flag, currently only PJ_TIMER_HEAP_NO_SYNCHRONIZE - * is recognized.. - * @param ht Pointer to receive the created timer heap. - * - * @return PJ_SUCCESS, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, - pj_size_t count, - unsigned flag, - pj_timer_heap_t **ht); - -/** - * Initialize a timer entry. Application should call this function at least - * once before scheduling the entry to the timer heap, to properly initialize - * the timer entry. - * - * @param entry The timer entry to be initialized. - * @param id Arbitrary ID assigned by the user/owner of this entry. - * Applications can use this ID to distinguish multiple - * timer entries that share the same callback and user_data. - * @param user_data User data to be associated with this entry. - * Applications normally will put the instance of object that - * owns the timer entry in this field. - * @param cb Callback function to be called when the timer elapses. - * - * @return The timer entry itself. - */ -PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, - int id, - void *user_data, - pj_timer_heap_callback *cb ); - -/** - * Schedule a timer entry which will expire AFTER the specified delay. - * - * @param ht The timer heap. - * @param entry The entry to be registered. - * @param delay The interval to expire. - * @return PJ_SUCCESS, or the appropriate error code. - */ -PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, - pj_timer_entry *entry, - const pj_time_val *delay); - -/** - * Cancel a previously registered timer. - * - * @param ht The timer heap. - * @param entry The entry to be cancelled. - * @return The number of timer cancelled, which should be one if the - * entry has really been registered, or zero if no timer was - * cancelled. - */ -PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, - pj_timer_entry *entry); - -/** - * Get the number of timer entries. - * - * @param ht The timer heap. - * @return The number of timer entries. - */ -PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ); - -/** - * Get the earliest time registered in the timer heap. The timer heap - * MUST have at least one timer being scheduled (application should use - * #pj_timer_heap_count() before calling this function). - * - * @param ht The timer heap. - * @param timeval The time deadline of the earliest timer entry. - * - * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled. - */ -PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht, - pj_time_val *timeval); - -/** - * Poll the timer heap, check for expired timers and call the callback for - * each of the expired timers. - * - * @param ht The timer heap. - * @param next_delay If this parameter is not NULL, it will be filled up with - * the time delay until the next timer elapsed, or -1 in - * the sec part if no entry exist. - * @return The number of timers expired. - */ -PJ_DECL(int) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJ_TIMER_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/timer.h 7 10/14/05 12:26a Bennylp $ */ +/* (C)1993-2003 Douglas C. Schmidt + * + * This file is originaly from ACE library by Doug Schmidt + * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research + * group at Washington University, University of California, Irvine, and Vanderbilt + * University Copyright (c) 1993-2003, all rights reserved. + * + */ + +#ifndef __PJ_TIMER_H__ +#define __PJ_TIMER_H__ + +/** + * @file timer.h + * @brief Timer Heap + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_TIMER Timer Heap Management. + * @ingroup PJ_MISC + * @brief + * The timer scheduling implementation here is based on ACE library's + * ACE_Timer_Heap, with only little modification to suit our library's style + * (I even left most of the comments in the original source). + * + * To quote the original quote in ACE_Timer_Heap_T class: + * + * This implementation uses a heap-based callout queue of + * absolute times. Therefore, in the average and worst case, + * scheduling, canceling, and expiring timers is O(log N) (where + * N is the total number of timers). In addition, we can also + * preallocate as many \a ACE_Timer_Nodes as there are slots in + * the heap. This allows us to completely remove the need for + * dynamic memory allocation, which is important for real-time + * systems. + * @{ + * + * \section pj_timer_examples_sec Examples + * + * For some examples on how to use the timer heap, please see the link below. + * + * - \ref page_pjlib_timer_test + */ + + +/** + * The type for internal timer ID. + */ +typedef int pj_timer_id_t; + +/** + * Forward declaration for pj_timer_entry. + */ +struct pj_timer_entry; + +/** + * The type of callback function to be called by timer scheduler when a timer + * has expired. + * + * @param timer_heap The timer heap. + * @param entry Timer entry which timer's has expired. + */ +typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry); + + +/** + * This structure represents an entry to the timer. + */ +struct pj_timer_entry +{ + /** + * User data to be associated with this entry. + * Applications normally will put the instance of object that + * owns the timer entry in this field. + */ + void *user_data; + + /** + * Arbitrary ID assigned by the user/owner of this entry. + * Applications can use this ID to distinguish multiple + * timer entries that share the same callback and user_data. + */ + int id; + + /** + * Callback to be called when the timer expires. + */ + pj_timer_heap_callback *cb; + + /** + * Internal unique timer ID, which is assigned by the timer heap. + * Application should not touch this ID. + */ + pj_timer_id_t _timer_id; + + /** + * The future time when the timer expires, which the value is updated + * by timer heap when the timer is scheduled. + */ + pj_time_val _timer_value; +}; + + +/** + * Default flag for timer heap, indicates that synchronization will be + * used. + */ +#define PJ_TIMER_HEAP_SYNCHRONIZE (0) + +/** + * Flag to indicate that thread synchronization is NOT needed for the + * timer heap. + */ +#define PJ_TIMER_HEAP_NO_SYNCHRONIZE (1) + +/** + * Calculate memory size required to create a timer heap. + * + * @param count Number of timer entries to be supported. + * @return Memory size requirement in bytes. + */ +PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count); + +/** + * Create a timer heap. + * + * @param pool The pool where allocations in the timer heap will be + * allocated. The timer heap will dynamicly allocate + * more storate from the pool if the number of timer + * entries registered is more than the size originally + * requested when calling this function. + * @param count The maximum number of timer entries to be supported + * initially. If the application registers more entries + * during runtime, then the timer heap will resize. + * @param flag Creation flag, currently only PJ_TIMER_HEAP_NO_SYNCHRONIZE + * is recognized.. + * @param ht Pointer to receive the created timer heap. + * + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, + pj_size_t count, + unsigned flag, + pj_timer_heap_t **ht); + +/** + * Initialize a timer entry. Application should call this function at least + * once before scheduling the entry to the timer heap, to properly initialize + * the timer entry. + * + * @param entry The timer entry to be initialized. + * @param id Arbitrary ID assigned by the user/owner of this entry. + * Applications can use this ID to distinguish multiple + * timer entries that share the same callback and user_data. + * @param user_data User data to be associated with this entry. + * Applications normally will put the instance of object that + * owns the timer entry in this field. + * @param cb Callback function to be called when the timer elapses. + * + * @return The timer entry itself. + */ +PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, + int id, + void *user_data, + pj_timer_heap_callback *cb ); + +/** + * Schedule a timer entry which will expire AFTER the specified delay. + * + * @param ht The timer heap. + * @param entry The entry to be registered. + * @param delay The interval to expire. + * @return PJ_SUCCESS, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *delay); + +/** + * Cancel a previously registered timer. + * + * @param ht The timer heap. + * @param entry The entry to be cancelled. + * @return The number of timer cancelled, which should be one if the + * entry has really been registered, or zero if no timer was + * cancelled. + */ +PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry); + +/** + * Get the number of timer entries. + * + * @param ht The timer heap. + * @return The number of timer entries. + */ +PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ); + +/** + * Get the earliest time registered in the timer heap. The timer heap + * MUST have at least one timer being scheduled (application should use + * #pj_timer_heap_count() before calling this function). + * + * @param ht The timer heap. + * @param timeval The time deadline of the earliest timer entry. + * + * @return PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled. + */ +PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht, + pj_time_val *timeval); + +/** + * Poll the timer heap, check for expired timers and call the callback for + * each of the expired timers. + * + * @param ht The timer heap. + * @param next_delay If this parameter is not NULL, it will be filled up with + * the time delay until the next timer elapsed, or -1 in + * the sec part if no entry exist. + * @return The number of timers expired. + */ +PJ_DECL(int) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJ_TIMER_H__ */ + diff --git a/pjlib/include/pj/types.h b/pjlib/include/pj/types.h index 0ad77d716..352fa391e 100644 --- a/pjlib/include/pj/types.h +++ b/pjlib/include/pj/types.h @@ -1,418 +1,418 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/types.h 11 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_TYPES_H__ -#define __PJ_TYPES_H__ - - -/** - * @defgroup PJ PJ Library - */ -/** - * @file types.h - * @brief Declaration of basic types and utility. - */ -/** - * @defgroup PJ_BASIC Basic Data Types and Library Functionality. - * @ingroup PJ_DS - * @{ - */ -#include - -PJ_BEGIN_DECL - -/////////////////////////////////////////////////////////////////////////////// - -/** Unsigned 32bit integer. */ -typedef int pj_int32_t; - -/** Signed 32bit integer. */ -typedef unsigned int pj_uint32_t; - -/** Unsigned 16bit integer. */ -typedef short pj_int16_t; - -/** Signed 16bit integer. */ -typedef unsigned short pj_uint16_t; - -/** Unsigned 8bit integer. */ -typedef signed char pj_int8_t; - -/** Signed 16bit integer. */ -typedef unsigned char pj_uint8_t; - -/** Large unsigned integer. */ -typedef size_t pj_size_t; - -/** Large signed integer. */ -typedef long pj_ssize_t; - -/** Status code. */ -typedef int pj_status_t; - -/** Boolean. */ -typedef int pj_bool_t; - -/** Status is OK. */ -#define PJ_SUCCESS 0 - -/** True value. */ -#define PJ_TRUE 1 - -/** False value. */ -#define PJ_FALSE 0 - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Data structure types. - */ -/** - * This type is used as replacement to legacy C string, and used throughout - * the library. By convention, the string is NOT null terminated. - */ -struct pj_str_t -{ - /** Buffer pointer, which is by convention NOT null terminated. */ - char *ptr; - - /** The length of the string. */ - pj_ssize_t slen; -}; - - -/** - * The opaque data type for linked list, which is used as arguments throughout - * the linked list operations. - */ -typedef void pj_list_type; - -/** - * List. - */ -typedef struct pj_list pj_list; - -/** - * Opaque data type for hash tables. - */ -typedef struct pj_hash_table_t pj_hash_table_t; - -/** - * Opaque data type for hash entry (only used internally by hash table). - */ -typedef struct pj_hash_entry pj_hash_entry; - -/** - * Data type for hash search iterator. - * This structure should be opaque, however applications need to declare - * concrete variable of this type, that's why the declaration is visible here. - */ -typedef struct pj_hash_iterator_t -{ - pj_uint32_t index; /**< Internal index. */ - pj_hash_entry *entry; /**< Internal entry. */ -} pj_hash_iterator_t; - - -/** - * Forward declaration for memory pool factory. - */ -typedef struct pj_pool_factory pj_pool_factory; - -/** - * Opaque data type for memory pool. - */ -typedef struct pj_pool_t pj_pool_t; - -/** - * Forward declaration for caching pool, a pool factory implementation. - */ -typedef struct pj_caching_pool pj_caching_pool; - -/** - * This type is used as replacement to legacy C string, and used throughout - * the library. - */ -typedef struct pj_str_t pj_str_t; - -/** - * Opaque data type for I/O Queue structure. - */ -typedef struct pj_ioqueue_t pj_ioqueue_t; - -/** - * Opaque data type for key that identifies a handle registered to the - * I/O queue framework. - */ -typedef struct pj_ioqueue_key_t pj_ioqueue_key_t; - -/** - * Opaque data to identify timer heap. - */ -typedef struct pj_timer_heap_t pj_timer_heap_t; - -/** - * Forward declaration for timer entry. - */ -typedef struct pj_timer_entry pj_timer_entry; - -/** - * Opaque data type for atomic operations. - */ -typedef struct pj_atomic_t pj_atomic_t; - -/** - * Value type of an atomic variable. - */ -typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t; - -/////////////////////////////////////////////////////////////////////////////// - -/** Thread handle. */ -typedef struct pj_thread_t pj_thread_t; - -/** Lock object. */ -typedef struct pj_lock_t pj_lock_t; - -/** Mutex handle. */ -typedef struct pj_mutex_t pj_mutex_t; - -/** Semaphore handle. */ -typedef struct pj_sem_t pj_sem_t; - -/** Event object. */ -typedef struct pj_event_t pj_event_t; - -/** Unidirectional stream pipe object. */ -typedef struct pj_pipe_t pj_pipe_t; - -/** Operating system handle. */ -typedef void *pj_oshandle_t; - -/** Socket handle. */ -typedef long pj_sock_t; - -/** Generic socket address. */ -typedef void pj_sockaddr_t; - -/** Color type. */ -typedef unsigned int pj_color_t; - -/** Exception id. */ -typedef int pj_exception_id_t; - -/////////////////////////////////////////////////////////////////////////////// - -/** Utility macro to compute the number of elements in static array. */ -#define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) - -/** Maximum value for signed 32-bit integer. */ -#define PJ_MAXINT32 0x7FFFFFFFL - -/** - * Length of object names. - */ -#define PJ_MAX_OBJ_NAME 16 - -/////////////////////////////////////////////////////////////////////////////// -/* - * General. - */ -/** - * Initialize the PJ Library. - * This function must be called before using the library. The purpose of this - * function is to initialize static library data, such as character table used - * in random string generation, and to initialize operating system dependent - * functionality (such as WSAStartup() in Windows). - */ -PJ_DECL(pj_status_t) pj_init(void); - - -/** - * @} - */ -/** - * @addtogroup PJ_TIME Time Data Type and Manipulation. - * @ingroup PJ_MISC - * @{ - */ - -/** - * Representation of time value in this library. - * This type can be used to represent either an interval or a specific time - * or date. - */ -typedef struct pj_time_val -{ - /** The seconds part of the time. */ - long sec; - - /** The miliseconds fraction of the time. */ - long msec; - -} pj_time_val; - -/** - * Normalize the value in time value. - * @param t Time value to be normalized. - */ -PJ_DECL(void) pj_time_val_normalize(pj_time_val *t); - -/** - * Get the total time value in miliseconds. This is the same as - * multiplying the second part with 1000 and then add the miliseconds - * part to the result. - * - * @param t The time value. - * @return Total time in miliseconds. - * @hideinitializer - */ -#define PJ_TIME_VAL_MSEC(t) ((t).sec * 1000 + (t).msec) - -/** - * This macro will check if \a t1 is equal to \a t2. - * - * @param t1 The first time value to compare. - * @param t2 The second time value to compare. - * @return Non-zero if both time values are equal. - * @hideinitializer - */ -#define PJ_TIME_VAL_EQ(t1, t2) ((t1).sec==(t2).sec && (t1).msec==(t2).msec) - -/** - * This macro will check if \a t1 is greater than \a t2 - * - * @param t1 The first time value to compare. - * @param t2 The second time value to compare. - * @return Non-zero if t1 is greater than t2. - * @hideinitializer - */ -#define PJ_TIME_VAL_GT(t1, t2) ((t1).sec>(t2).sec || \ - ((t1).sec==(t2).sec && (t1).msec>(t2).msec)) - -/** - * This macro will check if \a t1 is greater than or equal to \a t2 - * - * @param t1 The first time value to compare. - * @param t2 The second time value to compare. - * @return Non-zero if t1 is greater than or equal to t2. - * @hideinitializer - */ -#define PJ_TIME_VAL_GTE(t1, t2) (PJ_TIME_VAL_GT(t1,t2) || \ - PJ_TIME_VAL_EQ(t1,t2)) - -/** - * This macro will check if \a t1 is less than \a t2 - * - * @param t1 The first time value to compare. - * @param t2 The second time value to compare. - * @return Non-zero if t1 is less than t2. - * @hideinitializer - */ -#define PJ_TIME_VAL_LT(t1, t2) (!(PJ_TIME_VAL_GTE(t1,t2))) - -/** - * This macro will check if \a t1 is less than or equal to \a t2. - * - * @param t1 The first time value to compare. - * @param t2 The second time value to compare. - * @return Non-zero if t1 is less than or equal to t2. - * @hideinitializer - */ -#define PJ_TIME_VAL_LTE(t1, t2) (!PJ_TIME_VAL_GT(t1, t2)) - -/** - * Add \a t2 to \a t1 and store the result in \a t1. Effectively - * - * this macro will expand as: (\a t1 += \a t2). - * @param t1 The time value to add. - * @param t2 The time value to be added to \a t1. - * @hideinitializer - */ -#define PJ_TIME_VAL_ADD(t1, t2) do { \ - (t1).sec += (t2).sec; \ - (t1).msec += (t2).msec; \ - pj_time_val_normalize(&(t1)); \ - } while (0) - - -/** - * Substract \a t2 from \a t1 and store the result in \a t1. Effectively - * this macro will expand as (\a t1 -= \a t2). - * - * @param t1 The time value to subsctract. - * @param t2 The time value to be substracted from \a t1. - * @hideinitializer - */ -#define PJ_TIME_VAL_SUB(t1, t2) do { \ - (t1).sec -= (t2).sec; \ - (t1).msec -= (t2).msec; \ - pj_time_val_normalize(&(t1)); \ - } while (0) - - -/** - * This structure represent the parsed representation of time. - * It is acquired by calling #pj_time_decode(). - */ -typedef struct pj_parsed_time -{ - /** This represents day of week where value zero means Sunday */ - int wday; - - /** This represents day of the year, 0-365, where zero means - * 1st of January. - */ - int yday; - - /** This represents day of month: 1-31 */ - int day; - - /** This represents month, with the value is 0 - 11 (zero is January) */ - int mon; - - /** This represent the actual year (unlike in ANSI libc where - * the value must be added by 1900). - */ - int year; - - /** This represents the second part, with the value is 0-59 */ - int sec; - - /** This represents the minute part, with the value is: 0-59 */ - int min; - - /** This represents the hour part, with the value is 0-23 */ - int hour; - - /** This represents the milisecond part, with the value is 0-999 */ - int msec; - -} pj_parsed_time; - - -/** - * @} // Time Management - */ - -/////////////////////////////////////////////////////////////////////////////// -/* - * Terminal. - */ -/** - * Color code combination. - */ -enum { - PJ_TERM_COLOR_R = 2, /**< Red */ - PJ_TERM_COLOR_G = 4, /**< Green */ - PJ_TERM_COLOR_B = 1, /**< Blue. */ - PJ_TERM_COLOR_BRIGHT = 8 /**< Bright mask. */ -}; - - - - -PJ_END_DECL - - -#endif /* __PJ_TYPES_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pj/types.h 11 10/14/05 12:26a Bennylp $ */ + +#ifndef __PJ_TYPES_H__ +#define __PJ_TYPES_H__ + + +/** + * @defgroup PJ PJ Library + */ +/** + * @file types.h + * @brief Declaration of basic types and utility. + */ +/** + * @defgroup PJ_BASIC Basic Data Types and Library Functionality. + * @ingroup PJ_DS + * @{ + */ +#include + +PJ_BEGIN_DECL + +/////////////////////////////////////////////////////////////////////////////// + +/** Unsigned 32bit integer. */ +typedef int pj_int32_t; + +/** Signed 32bit integer. */ +typedef unsigned int pj_uint32_t; + +/** Unsigned 16bit integer. */ +typedef short pj_int16_t; + +/** Signed 16bit integer. */ +typedef unsigned short pj_uint16_t; + +/** Unsigned 8bit integer. */ +typedef signed char pj_int8_t; + +/** Signed 16bit integer. */ +typedef unsigned char pj_uint8_t; + +/** Large unsigned integer. */ +typedef size_t pj_size_t; + +/** Large signed integer. */ +typedef long pj_ssize_t; + +/** Status code. */ +typedef int pj_status_t; + +/** Boolean. */ +typedef int pj_bool_t; + +/** Status is OK. */ +#define PJ_SUCCESS 0 + +/** True value. */ +#define PJ_TRUE 1 + +/** False value. */ +#define PJ_FALSE 0 + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Data structure types. + */ +/** + * This type is used as replacement to legacy C string, and used throughout + * the library. By convention, the string is NOT null terminated. + */ +struct pj_str_t +{ + /** Buffer pointer, which is by convention NOT null terminated. */ + char *ptr; + + /** The length of the string. */ + pj_ssize_t slen; +}; + + +/** + * The opaque data type for linked list, which is used as arguments throughout + * the linked list operations. + */ +typedef void pj_list_type; + +/** + * List. + */ +typedef struct pj_list pj_list; + +/** + * Opaque data type for hash tables. + */ +typedef struct pj_hash_table_t pj_hash_table_t; + +/** + * Opaque data type for hash entry (only used internally by hash table). + */ +typedef struct pj_hash_entry pj_hash_entry; + +/** + * Data type for hash search iterator. + * This structure should be opaque, however applications need to declare + * concrete variable of this type, that's why the declaration is visible here. + */ +typedef struct pj_hash_iterator_t +{ + pj_uint32_t index; /**< Internal index. */ + pj_hash_entry *entry; /**< Internal entry. */ +} pj_hash_iterator_t; + + +/** + * Forward declaration for memory pool factory. + */ +typedef struct pj_pool_factory pj_pool_factory; + +/** + * Opaque data type for memory pool. + */ +typedef struct pj_pool_t pj_pool_t; + +/** + * Forward declaration for caching pool, a pool factory implementation. + */ +typedef struct pj_caching_pool pj_caching_pool; + +/** + * This type is used as replacement to legacy C string, and used throughout + * the library. + */ +typedef struct pj_str_t pj_str_t; + +/** + * Opaque data type for I/O Queue structure. + */ +typedef struct pj_ioqueue_t pj_ioqueue_t; + +/** + * Opaque data type for key that identifies a handle registered to the + * I/O queue framework. + */ +typedef struct pj_ioqueue_key_t pj_ioqueue_key_t; + +/** + * Opaque data to identify timer heap. + */ +typedef struct pj_timer_heap_t pj_timer_heap_t; + +/** + * Forward declaration for timer entry. + */ +typedef struct pj_timer_entry pj_timer_entry; + +/** + * Opaque data type for atomic operations. + */ +typedef struct pj_atomic_t pj_atomic_t; + +/** + * Value type of an atomic variable. + */ +typedef PJ_ATOMIC_VALUE_TYPE pj_atomic_value_t; + +/////////////////////////////////////////////////////////////////////////////// + +/** Thread handle. */ +typedef struct pj_thread_t pj_thread_t; + +/** Lock object. */ +typedef struct pj_lock_t pj_lock_t; + +/** Mutex handle. */ +typedef struct pj_mutex_t pj_mutex_t; + +/** Semaphore handle. */ +typedef struct pj_sem_t pj_sem_t; + +/** Event object. */ +typedef struct pj_event_t pj_event_t; + +/** Unidirectional stream pipe object. */ +typedef struct pj_pipe_t pj_pipe_t; + +/** Operating system handle. */ +typedef void *pj_oshandle_t; + +/** Socket handle. */ +typedef long pj_sock_t; + +/** Generic socket address. */ +typedef void pj_sockaddr_t; + +/** Color type. */ +typedef unsigned int pj_color_t; + +/** Exception id. */ +typedef int pj_exception_id_t; + +/////////////////////////////////////////////////////////////////////////////// + +/** Utility macro to compute the number of elements in static array. */ +#define PJ_ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) + +/** Maximum value for signed 32-bit integer. */ +#define PJ_MAXINT32 0x7FFFFFFFL + +/** + * Length of object names. + */ +#define PJ_MAX_OBJ_NAME 16 + +/////////////////////////////////////////////////////////////////////////////// +/* + * General. + */ +/** + * Initialize the PJ Library. + * This function must be called before using the library. The purpose of this + * function is to initialize static library data, such as character table used + * in random string generation, and to initialize operating system dependent + * functionality (such as WSAStartup() in Windows). + */ +PJ_DECL(pj_status_t) pj_init(void); + + +/** + * @} + */ +/** + * @addtogroup PJ_TIME Time Data Type and Manipulation. + * @ingroup PJ_MISC + * @{ + */ + +/** + * Representation of time value in this library. + * This type can be used to represent either an interval or a specific time + * or date. + */ +typedef struct pj_time_val +{ + /** The seconds part of the time. */ + long sec; + + /** The miliseconds fraction of the time. */ + long msec; + +} pj_time_val; + +/** + * Normalize the value in time value. + * @param t Time value to be normalized. + */ +PJ_DECL(void) pj_time_val_normalize(pj_time_val *t); + +/** + * Get the total time value in miliseconds. This is the same as + * multiplying the second part with 1000 and then add the miliseconds + * part to the result. + * + * @param t The time value. + * @return Total time in miliseconds. + * @hideinitializer + */ +#define PJ_TIME_VAL_MSEC(t) ((t).sec * 1000 + (t).msec) + +/** + * This macro will check if \a t1 is equal to \a t2. + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if both time values are equal. + * @hideinitializer + */ +#define PJ_TIME_VAL_EQ(t1, t2) ((t1).sec==(t2).sec && (t1).msec==(t2).msec) + +/** + * This macro will check if \a t1 is greater than \a t2 + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is greater than t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_GT(t1, t2) ((t1).sec>(t2).sec || \ + ((t1).sec==(t2).sec && (t1).msec>(t2).msec)) + +/** + * This macro will check if \a t1 is greater than or equal to \a t2 + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is greater than or equal to t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_GTE(t1, t2) (PJ_TIME_VAL_GT(t1,t2) || \ + PJ_TIME_VAL_EQ(t1,t2)) + +/** + * This macro will check if \a t1 is less than \a t2 + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is less than t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_LT(t1, t2) (!(PJ_TIME_VAL_GTE(t1,t2))) + +/** + * This macro will check if \a t1 is less than or equal to \a t2. + * + * @param t1 The first time value to compare. + * @param t2 The second time value to compare. + * @return Non-zero if t1 is less than or equal to t2. + * @hideinitializer + */ +#define PJ_TIME_VAL_LTE(t1, t2) (!PJ_TIME_VAL_GT(t1, t2)) + +/** + * Add \a t2 to \a t1 and store the result in \a t1. Effectively + * + * this macro will expand as: (\a t1 += \a t2). + * @param t1 The time value to add. + * @param t2 The time value to be added to \a t1. + * @hideinitializer + */ +#define PJ_TIME_VAL_ADD(t1, t2) do { \ + (t1).sec += (t2).sec; \ + (t1).msec += (t2).msec; \ + pj_time_val_normalize(&(t1)); \ + } while (0) + + +/** + * Substract \a t2 from \a t1 and store the result in \a t1. Effectively + * this macro will expand as (\a t1 -= \a t2). + * + * @param t1 The time value to subsctract. + * @param t2 The time value to be substracted from \a t1. + * @hideinitializer + */ +#define PJ_TIME_VAL_SUB(t1, t2) do { \ + (t1).sec -= (t2).sec; \ + (t1).msec -= (t2).msec; \ + pj_time_val_normalize(&(t1)); \ + } while (0) + + +/** + * This structure represent the parsed representation of time. + * It is acquired by calling #pj_time_decode(). + */ +typedef struct pj_parsed_time +{ + /** This represents day of week where value zero means Sunday */ + int wday; + + /** This represents day of the year, 0-365, where zero means + * 1st of January. + */ + int yday; + + /** This represents day of month: 1-31 */ + int day; + + /** This represents month, with the value is 0 - 11 (zero is January) */ + int mon; + + /** This represent the actual year (unlike in ANSI libc where + * the value must be added by 1900). + */ + int year; + + /** This represents the second part, with the value is 0-59 */ + int sec; + + /** This represents the minute part, with the value is: 0-59 */ + int min; + + /** This represents the hour part, with the value is 0-23 */ + int hour; + + /** This represents the milisecond part, with the value is 0-999 */ + int msec; + +} pj_parsed_time; + + +/** + * @} // Time Management + */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * Terminal. + */ +/** + * Color code combination. + */ +enum { + PJ_TERM_COLOR_R = 2, /**< Red */ + PJ_TERM_COLOR_G = 4, /**< Green */ + PJ_TERM_COLOR_B = 1, /**< Blue. */ + PJ_TERM_COLOR_BRIGHT = 8 /**< Bright mask. */ +}; + + + + +PJ_END_DECL + + +#endif /* __PJ_TYPES_H__ */ + diff --git a/pjlib/include/pj/xml.h b/pjlib/include/pj/xml.h index 6311448e7..a202b28d2 100644 --- a/pjlib/include/pj/xml.h +++ b/pjlib/include/pj/xml.h @@ -1,155 +1,155 @@ -/* $Header: /pjproject-0.3/pjlib/include/pj/xml.h 4 10/14/05 12:26a Bennylp $ */ - -#ifndef __PJ_XML_H__ -#define __PJ_XML_H__ - -/** - * @file xml.h - * @brief PJLIB XML Parser/Helper. - */ - -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJ_XML XML Parser/Helper. - * @ingroup PJ - * @{ - */ - -/** Typedef for XML attribute. */ -typedef struct pj_xml_attr pj_xml_attr; - -/** Typedef for XML nodes. */ -typedef struct pj_xml_node pj_xml_node; - -/** This structure declares XML attribute. */ -struct pj_xml_attr -{ - PJ_DECL_LIST_MEMBER(pj_xml_attr) - pj_str_t name; /**< Attribute name. */ - pj_str_t value; /**< Attribute value. */ -}; - -/** This structure describes XML node head inside XML node structure. - */ -typedef struct pj_xml_node_head -{ - PJ_DECL_LIST_MEMBER(pj_xml_node) -} pj_xml_node_head; - -/** This structure describes XML node. */ -struct pj_xml_node -{ - PJ_DECL_LIST_MEMBER(pj_xml_node) /** List @a prev and @a next member */ - pj_str_t name; /** Node name. */ - pj_xml_attr attr_head; /** Attribute list. */ - pj_xml_node_head node_head; /** Node list. */ - pj_str_t content; /** Node content. */ -}; - -/** - * Parse XML message into XML document with a single root node. The parser - * is capable of parsing XML processing instruction construct (" +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJ_XML XML Parser/Helper. + * @ingroup PJ + * @{ + */ + +/** Typedef for XML attribute. */ +typedef struct pj_xml_attr pj_xml_attr; + +/** Typedef for XML nodes. */ +typedef struct pj_xml_node pj_xml_node; + +/** This structure declares XML attribute. */ +struct pj_xml_attr +{ + PJ_DECL_LIST_MEMBER(pj_xml_attr) + pj_str_t name; /**< Attribute name. */ + pj_str_t value; /**< Attribute value. */ +}; + +/** This structure describes XML node head inside XML node structure. + */ +typedef struct pj_xml_node_head +{ + PJ_DECL_LIST_MEMBER(pj_xml_node) +} pj_xml_node_head; + +/** This structure describes XML node. */ +struct pj_xml_node +{ + PJ_DECL_LIST_MEMBER(pj_xml_node) /** List @a prev and @a next member */ + pj_str_t name; /** Node name. */ + pj_xml_attr attr_head; /** Attribute list. */ + pj_xml_node_head node_head; /** Node list. */ + pj_str_t content; /** Node content. */ +}; + +/** + * Parse XML message into XML document with a single root node. The parser + * is capable of parsing XML processing instruction construct (" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#endif /* __PJLIB_H__ */ - +/* $Header: /pjproject-0.3/pjlib/include/pjlib.h 6 10/29/05 11:30a Bennylp $ */ +#ifndef __PJLIB_H__ +#define __PJLIB_H__ + +/** + * @file pjlib.h + * @brief Include all PJLIB header files. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#endif /* __PJLIB_H__ */ + diff --git a/pjlib/src/pj/addr_resolv_linux_kernel.c b/pjlib/src/pj/addr_resolv_linux_kernel.c index 7c085c606..c49be1c24 100644 --- a/pjlib/src/pj/addr_resolv_linux_kernel.c +++ b/pjlib/src/pj/addr_resolv_linux_kernel.c @@ -1,14 +1,14 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/addr_resolv_linux_kernel.c 1 10/05/05 4:41p Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/addr_resolv_linux_kernel.c $ - * - * 1 10/05/05 4:41p Bennylp - * Created. - * - */ -#include - -PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) -{ - return -1; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/addr_resolv_linux_kernel.c 1 10/05/05 4:41p Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/addr_resolv_linux_kernel.c $ + * + * 1 10/05/05 4:41p Bennylp + * Created. + * + */ +#include + +PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) +{ + return -1; +} + diff --git a/pjlib/src/pj/addr_resolv_sock.c b/pjlib/src/pj/addr_resolv_sock.c index 0200c65a6..deb0a6e97 100644 --- a/pjlib/src/pj/addr_resolv_sock.c +++ b/pjlib/src/pj/addr_resolv_sock.c @@ -1,44 +1,44 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/addr_resolv_sock.c 2 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/addr_resolv_sock.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/22/05 10:38a Bennylp - * Created. - * - */ -#include -#include -#include -#include -#include - - -PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) -{ - struct hostent *he; - char copy[PJ_MAX_HOSTNAME]; - - pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME); - - if (hostname->slen >= PJ_MAX_HOSTNAME) - return PJ_ENAMETOOLONG; - - pj_memcpy(copy, hostname->ptr, hostname->slen); - copy[ hostname->slen ] = '\0'; - - he = gethostbyname(copy); - if (!he) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - - phe->h_name = he->h_name; - phe->h_aliases = he->h_aliases; - phe->h_addrtype = he->h_addrtype; - phe->h_length = he->h_length; - phe->h_addr_list = he->h_addr_list; - - return PJ_SUCCESS; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/addr_resolv_sock.c 2 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/addr_resolv_sock.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/22/05 10:38a Bennylp + * Created. + * + */ +#include +#include +#include +#include +#include + + +PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe) +{ + struct hostent *he; + char copy[PJ_MAX_HOSTNAME]; + + pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME); + + if (hostname->slen >= PJ_MAX_HOSTNAME) + return PJ_ENAMETOOLONG; + + pj_memcpy(copy, hostname->ptr, hostname->slen); + copy[ hostname->slen ] = '\0'; + + he = gethostbyname(copy); + if (!he) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + + phe->h_name = he->h_name; + phe->h_aliases = he->h_aliases; + phe->h_addrtype = he->h_addrtype; + phe->h_length = he->h_length; + phe->h_addr_list = he->h_addr_list; + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/array.c b/pjlib/src/pj/array.c index edb4994d6..06a3ad1ab 100644 --- a/pjlib/src/pj/array.c +++ b/pjlib/src/pj/array.c @@ -1,63 +1,63 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/array.c 5 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/array.c $ - * - * 5 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 4 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include - -PJ_DEF(void) pj_array_insert( void *array, - unsigned elem_size, - unsigned count, - unsigned pos, - const void *value) -{ - if (count && pos < count-1) { - pj_memmove( (char*)array + (pos+1)*elem_size, - (char*)array + pos*elem_size, - (count-pos)*elem_size); - } - pj_memmove((char*)array + pos*elem_size, value, elem_size); -} - -PJ_DEF(void) pj_array_erase( void *array, - unsigned elem_size, - unsigned count, - unsigned pos) -{ - pj_assert(count != 0); - if (pos < count-1) { - pj_memmove( (char*)array + pos*elem_size, - (char*)array + (pos+1)*elem_size, - (count-pos-1)*elem_size); - } -} - -PJ_DEF(pj_status_t) pj_array_find( const void *array, - unsigned elem_size, - unsigned count, - pj_status_t (*matching)(const void *value), - void **result) -{ - unsigned i; - const char *char_array = array; - for (i=0; i +#include +#include +#include + +PJ_DEF(void) pj_array_insert( void *array, + unsigned elem_size, + unsigned count, + unsigned pos, + const void *value) +{ + if (count && pos < count-1) { + pj_memmove( (char*)array + (pos+1)*elem_size, + (char*)array + pos*elem_size, + (count-pos)*elem_size); + } + pj_memmove((char*)array + pos*elem_size, value, elem_size); +} + +PJ_DEF(void) pj_array_erase( void *array, + unsigned elem_size, + unsigned count, + unsigned pos) +{ + pj_assert(count != 0); + if (pos < count-1) { + pj_memmove( (char*)array + pos*elem_size, + (char*)array + (pos+1)*elem_size, + (count-pos-1)*elem_size); + } +} + +PJ_DEF(pj_status_t) pj_array_find( const void *array, + unsigned elem_size, + unsigned count, + pj_status_t (*matching)(const void *value), + void **result) +{ + unsigned i; + const char *char_array = array; + for (i=0; i - -.global __longjmp -.type __longjmp,%function -.align 4 -__longjmp: - movl 4(%esp), %ecx /* User's jmp_buf in %ecx. */ - movl 8(%esp), %eax /* Second argument is return value. */ - /* Save the return address now. */ - 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 - /* Jump to saved PC. */ - jmp *%edx -.size __longjmp,.-__longjmp - +/* longjmp for i386. + Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#define _ASM +#define _SETJMP_H +#define PJ_LINUX_KERNEL 1 +#include + +.global __longjmp +.type __longjmp,%function +.align 4 +__longjmp: + movl 4(%esp), %ecx /* User's jmp_buf in %ecx. */ + movl 8(%esp), %eax /* Second argument is return value. */ + /* Save the return address now. */ + 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 + /* Jump to saved PC. */ + jmp *%edx +.size __longjmp,.-__longjmp + diff --git a/pjlib/src/pj/compat/setjmp_i386.S b/pjlib/src/pj/compat/setjmp_i386.S index 6810c554f..9cdaaff74 100644 --- a/pjlib/src/pj/compat/setjmp_i386.S +++ b/pjlib/src/pj/compat/setjmp_i386.S @@ -1,61 +1,61 @@ -/* setjmp for i386, ELF version. - Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#define _ASM -#define _SETJMP_H -#define PJ_LINUX_KERNEL 1 -#include - - -.global __sigsetjmp -.type __sigsetjmp,%function -.align 4 - -__sigsetjmp: - movl 4 (%esp), %eax - /* Save registers. */ - movl %ebx, (0 *4)(%eax) - movl %esi, (1 *4)(%eax) - movl %edi, (2 *4)(%eax) - /* Save SP as it will be after we return. */ - leal 4(%esp), %ecx - movl %ecx, (4 *4)(%eax) - /* Save PC we are returning to now. */ - movl 0(%esp), %ecx - movl %ecx, (5 *4)(%eax) - /* Save caller's frame pointer. */ - movl %ebp, (3 *4)(%eax) - - /* Make a tail call to __sigjmp_save; it takes the same args. */ -#ifdef __PIC__ - /* We cannot use the PLT, because it requires that %ebx be set, but - we can't save and restore our caller's value. Instead, we do an - indirect jump through the GOT, using for the temporary register - %ecx, which is call-clobbered. */ - call .Lhere -.Lhere: - popl %ecx - addl $_GLOBAL_OFFSET_TABLE_+[.- .Lhere ], %ecx - movl __sigjmp_save @GOT (%ecx), %ecx - jmp *%ecx -#else - jmp __sigjmp_save -#endif -.size __sigsetjmp,.-__sigsetjmp - +/* setjmp for i386, ELF version. + Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#define _ASM +#define _SETJMP_H +#define PJ_LINUX_KERNEL 1 +#include + + +.global __sigsetjmp +.type __sigsetjmp,%function +.align 4 + +__sigsetjmp: + movl 4 (%esp), %eax + /* Save registers. */ + movl %ebx, (0 *4)(%eax) + movl %esi, (1 *4)(%eax) + movl %edi, (2 *4)(%eax) + /* Save SP as it will be after we return. */ + leal 4(%esp), %ecx + movl %ecx, (4 *4)(%eax) + /* Save PC we are returning to now. */ + movl 0(%esp), %ecx + movl %ecx, (5 *4)(%eax) + /* Save caller's frame pointer. */ + movl %ebp, (3 *4)(%eax) + + /* Make a tail call to __sigjmp_save; it takes the same args. */ +#ifdef __PIC__ + /* We cannot use the PLT, because it requires that %ebx be set, but + we can't save and restore our caller's value. Instead, we do an + indirect jump through the GOT, using for the temporary register + %ecx, which is call-clobbered. */ + call .Lhere +.Lhere: + popl %ecx + addl $_GLOBAL_OFFSET_TABLE_+[.- .Lhere ], %ecx + movl __sigjmp_save @GOT (%ecx), %ecx + jmp *%ecx +#else + jmp __sigjmp_save +#endif +.size __sigsetjmp,.-__sigsetjmp + diff --git a/pjlib/src/pj/compat/sigjmp.c b/pjlib/src/pj/compat/sigjmp.c index ead0e363a..4abcddfbf 100644 --- a/pjlib/src/pj/compat/sigjmp.c +++ b/pjlib/src/pj/compat/sigjmp.c @@ -1,21 +1,21 @@ -#include -#include - -int __sigjmp_save(sigjmp_buf env, int savemask) -{ - return 0; -} - -extern int __sigsetjmp(pj_jmp_buf env, int savemask); -extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn)); - -PJ_DEF(int) pj_setjmp(pj_jmp_buf env) -{ - return __sigsetjmp(env, 0); -} - -PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val) -{ - __longjmp(env, val); -} - +#include +#include + +int __sigjmp_save(sigjmp_buf env, int savemask) +{ + return 0; +} + +extern int __sigsetjmp(pj_jmp_buf env, int savemask); +extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn)); + +PJ_DEF(int) pj_setjmp(pj_jmp_buf env) +{ + return __sigsetjmp(env, 0); +} + +PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val) +{ + __longjmp(env, val); +} + diff --git a/pjlib/src/pj/compat/string.c b/pjlib/src/pj/compat/string.c index 25fd11c98..ccaf09a25 100644 --- a/pjlib/src/pj/compat/string.c +++ b/pjlib/src/pj/compat/string.c @@ -1,33 +1,33 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/compat/string.c 1 9/22/05 10:43a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/compat/string.c $ - * - * 1 9/22/05 10:43a Bennylp - * Created. - * - */ -#include -#include -#include - -PJ_DEF(int) strcasecmp(const char *s1, const char *s2) -{ - while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { - if (!*s1++) - return 0; - ++s2; - } - return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; -} - -PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len) -{ - if (!len) return 0; - - while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { - if (!*s1++ || --len <= 0) - return 0; - ++s2; - } - return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/compat/string.c 1 9/22/05 10:43a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/compat/string.c $ + * + * 1 9/22/05 10:43a Bennylp + * Created. + * + */ +#include +#include +#include + +PJ_DEF(int) strcasecmp(const char *s1, const char *s2) +{ + while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { + if (!*s1++) + return 0; + ++s2; + } + return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; +} + +PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len) +{ + if (!len) return 0; + + while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) { + if (!*s1++ || --len <= 0) + return 0; + ++s2; + } + return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1; +} + diff --git a/pjlib/src/pj/config.c b/pjlib/src/pj/config.c index 5a08c8dd8..55d9e3394 100644 --- a/pjlib/src/pj/config.c +++ b/pjlib/src/pj/config.c @@ -1,40 +1,40 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/config.c 7 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/config.c $ - * - * 7 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 6 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 5 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include - -static const char *id = "config.c"; -const char *PJ_VERSION = "0.3.0-pre1"; - -PJ_DEF(void) pj_dump_config(void) -{ - PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono")); - PJ_LOG(3, (id, "Dumping configurations:")); - PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION)); - PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG)); - PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED)); - PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG)); - PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS)); - PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL)); - PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE)); - PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER)); - PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP)); - PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME)); - PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE)); - PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ)); - PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER)); - PJ_LOG(3, (id, " PJ_(endianness) : %s", (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian"))); - PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES)); -} +/* $Header: /pjproject-0.3/pjlib/src/pj/config.c 7 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/config.c $ + * + * 7 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 6 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 5 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include + +static const char *id = "config.c"; +const char *PJ_VERSION = "0.3.0-pre1"; + +PJ_DEF(void) pj_dump_config(void) +{ + PJ_LOG(3, (id, "PJLIB (c)2005 Benny Prijono")); + PJ_LOG(3, (id, "Dumping configurations:")); + PJ_LOG(3, (id, " PJ_VERSION : %s", PJ_VERSION)); + PJ_LOG(3, (id, " PJ_DEBUG : %d", PJ_DEBUG)); + PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED : %d", PJ_FUNCTIONS_ARE_INLINED)); + PJ_LOG(3, (id, " PJ_POOL_DEBUG : %d", PJ_POOL_DEBUG)); + PJ_LOG(3, (id, " PJ_HAS_THREADS : %d", PJ_HAS_THREADS)); + PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL : %d", PJ_LOG_MAX_LEVEL)); + PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE : %d", PJ_LOG_MAX_SIZE)); + PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER : %d", PJ_LOG_USE_STACK_BUFFER)); + PJ_LOG(3, (id, " PJ_HAS_TCP : %d", PJ_HAS_TCP)); + PJ_LOG(3, (id, " PJ_MAX_HOSTNAME : %d", PJ_MAX_HOSTNAME)); + PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE : %d", PJ_HAS_SEMAPHORE)); + PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ : %d", PJ_HAS_EVENT_OBJ)); + PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER : %d", PJ_HAS_HIGH_RES_TIMER)); + PJ_LOG(3, (id, " PJ_(endianness) : %s", (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian"))); + PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES : %d", PJ_IOQUEUE_MAX_HANDLES)); +} diff --git a/pjlib/src/pj/equeue_winnt.c b/pjlib/src/pj/equeue_winnt.c index b1ed45081..ede47d10d 100644 --- a/pjlib/src/pj/equeue_winnt.c +++ b/pjlib/src/pj/equeue_winnt.c @@ -1,13 +1,13 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/equeue_winnt.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/equeue_winnt.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/05/05 6:19p Bennylp - * Created. - * - */ -#include +/* $Header: /pjproject-0.3/pjlib/src/pj/equeue_winnt.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/equeue_winnt.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/05/05 6:19p Bennylp + * Created. + * + */ +#include diff --git a/pjlib/src/pj/errno.c b/pjlib/src/pj/errno.c index 218c789f1..42979c085 100644 --- a/pjlib/src/pj/errno.c +++ b/pjlib/src/pj/errno.c @@ -1,107 +1,107 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/errno.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/errno.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/08/05 9:53a Bennylp - * Created. - * - */ -#include -#include -#include - -/* Prototype for platform specific error message, which will be defined - * in separate file. - */ -extern int platform_strerror( pj_os_err_type code, - char *buf, pj_size_t bufsize ); - -/* PJLIB's own error codes/messages */ -static const struct -{ - int code; - const char *msg; -} err_str[] = -{ - { PJ_EUNKNOWN, "Unknown Error" }, - { PJ_EPENDING, "Pending operation" }, - { PJ_ETOOMANYCONN, "Too many connecting sockets" }, - { PJ_EINVAL, "Invalid value or argument" }, - { PJ_ENAMETOOLONG, "Name too long" }, - { PJ_ENOTFOUND, "Not found" }, - { PJ_ENOMEM, "Not enough memory" }, - { PJ_EBUG, "BUG DETECTED!" }, - { PJ_ETIMEDOUT, "Operation timed out" }, - { PJ_ETOOMANY, "Too many objects of the specified type"}, - { PJ_EBUSY, "Object is busy"}, - { PJ_ENOTSUP, "Option/operation is not supported"}, - { PJ_EINVALIDOP, "Invalid operation"} -}; - -/* - * pjlib_error() - * - * Retrieve message string for PJLIB's own error code. - */ -static int pjlib_error(pj_status_t code, char *buf, pj_size_t size) -{ - unsigned i; - - for (i=0; i= size) len = size-1; - pj_memcpy(buf, err_str[i].msg, len); - buf[len] = '\0'; - return len; - } - } - - *buf++ = '?'; - *buf++ = '?'; - *buf++ = '?'; - *buf++ = '\0'; - return 3; -} - -/* - * pj_strerror() - */ -PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode, - char *buf, pj_size_t bufsize ) -{ - int len = -1; - pj_str_t errstr; - - if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) { - len = pj_snprintf( buf, bufsize, "Unknown error %d", statcode); - - } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) { - len = pjlib_error(statcode, buf, bufsize); - - } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) { - len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize); - - } else if (statcode < PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) { - len = pj_snprintf( buf, bufsize, "User error %d", statcode); - - } else { - len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode); - - } - - if (len < 1) { - *buf = '\0'; - len = 0; - } - - errstr.ptr = buf; - errstr.slen = len; - - return errstr; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/errno.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/errno.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/08/05 9:53a Bennylp + * Created. + * + */ +#include +#include +#include + +/* Prototype for platform specific error message, which will be defined + * in separate file. + */ +extern int platform_strerror( pj_os_err_type code, + char *buf, pj_size_t bufsize ); + +/* PJLIB's own error codes/messages */ +static const struct +{ + int code; + const char *msg; +} err_str[] = +{ + { PJ_EUNKNOWN, "Unknown Error" }, + { PJ_EPENDING, "Pending operation" }, + { PJ_ETOOMANYCONN, "Too many connecting sockets" }, + { PJ_EINVAL, "Invalid value or argument" }, + { PJ_ENAMETOOLONG, "Name too long" }, + { PJ_ENOTFOUND, "Not found" }, + { PJ_ENOMEM, "Not enough memory" }, + { PJ_EBUG, "BUG DETECTED!" }, + { PJ_ETIMEDOUT, "Operation timed out" }, + { PJ_ETOOMANY, "Too many objects of the specified type"}, + { PJ_EBUSY, "Object is busy"}, + { PJ_ENOTSUP, "Option/operation is not supported"}, + { PJ_EINVALIDOP, "Invalid operation"} +}; + +/* + * pjlib_error() + * + * Retrieve message string for PJLIB's own error code. + */ +static int pjlib_error(pj_status_t code, char *buf, pj_size_t size) +{ + unsigned i; + + for (i=0; i= size) len = size-1; + pj_memcpy(buf, err_str[i].msg, len); + buf[len] = '\0'; + return len; + } + } + + *buf++ = '?'; + *buf++ = '?'; + *buf++ = '?'; + *buf++ = '\0'; + return 3; +} + +/* + * pj_strerror() + */ +PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode, + char *buf, pj_size_t bufsize ) +{ + int len = -1; + pj_str_t errstr; + + if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) { + len = pj_snprintf( buf, bufsize, "Unknown error %d", statcode); + + } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) { + len = pjlib_error(statcode, buf, bufsize); + + } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) { + len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize); + + } else if (statcode < PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE) { + len = pj_snprintf( buf, bufsize, "User error %d", statcode); + + } else { + len = pj_snprintf( buf, bufsize, "Invalid error %d", statcode); + + } + + if (len < 1) { + *buf = '\0'; + len = 0; + } + + errstr.ptr = buf; + errstr.slen = len; + + return errstr; +} + diff --git a/pjlib/src/pj/except.c b/pjlib/src/pj/except.c index 0525caeee..f64ebe82b 100644 --- a/pjlib/src/pj/except.c +++ b/pjlib/src/pj/except.c @@ -1,148 +1,148 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/except.c 6 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/except.c $ - * - * 6 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 5 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 4 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include - -static long thread_local_id = -1; - -#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 - static const char *exception_id_names[PJ_MAX_EXCEPTION_ID]; -#else - /* - * Start from 1 (not 0)!!! - * Exception 0 is reserved for normal path of setjmp()!!! - */ - static int last_exception_id = 1; -#endif /* PJ_HAS_EXCEPTION_NAMES */ - - -PJ_DEF(void) pj_throw_exception_(int exception_id) -{ - struct pj_exception_state_t *handler; - - handler = pj_thread_local_get(thread_local_id); - if (handler == NULL) { - PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %d!\n", exception_id)); - pj_assert(handler != NULL); - /* This will crash the system! */ - } - pj_longjmp(handler->state, exception_id); -} - -PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec) -{ - struct pj_exception_state_t *parent_handler = NULL; - - if (thread_local_id == -1) { - pj_thread_local_alloc(&thread_local_id); - pj_assert(thread_local_id != -1); - } - parent_handler = pj_thread_local_get(thread_local_id); - rec->prev = parent_handler; - pj_thread_local_set(thread_local_id, rec); -} - -PJ_DEF(void) pj_pop_exception_handler_(void) -{ - struct pj_exception_state_t *handler; - - handler = pj_thread_local_get(thread_local_id); - pj_assert(handler != NULL); - pj_thread_local_set(thread_local_id, handler->prev); -} - -#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 -PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, - pj_exception_id_t *id) -{ - unsigned i; - - pj_enter_critical_section(); - - /* - * Start from 1 (not 0)!!! - * Exception 0 is reserved for normal path of setjmp()!!! - */ - for (i=1; i0 && id0 && id"); - - if (exception_id_names[id] == NULL) - return ""; - - return exception_id_names[id]; -} - -#else /* PJ_HAS_EXCEPTION_NAMES */ -PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, - pj_exception_id_t *id) -{ - PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY); - - *id = last_exception_id++ - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) -{ - return PJ_SUCCESS; -} - -PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) -{ - return ""; -} - -#endif /* PJ_HAS_EXCEPTION_NAMES */ - - - +/* $Header: /pjproject-0.3/pjlib/src/pj/except.c 6 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/except.c $ + * + * 6 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 5 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 4 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include + +static long thread_local_id = -1; + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 + static const char *exception_id_names[PJ_MAX_EXCEPTION_ID]; +#else + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + static int last_exception_id = 1; +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + +PJ_DEF(void) pj_throw_exception_(int exception_id) +{ + struct pj_exception_state_t *handler; + + handler = pj_thread_local_get(thread_local_id); + if (handler == NULL) { + PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %d!\n", exception_id)); + pj_assert(handler != NULL); + /* This will crash the system! */ + } + pj_longjmp(handler->state, exception_id); +} + +PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec) +{ + struct pj_exception_state_t *parent_handler = NULL; + + if (thread_local_id == -1) { + pj_thread_local_alloc(&thread_local_id); + pj_assert(thread_local_id != -1); + } + parent_handler = pj_thread_local_get(thread_local_id); + rec->prev = parent_handler; + pj_thread_local_set(thread_local_id, rec); +} + +PJ_DEF(void) pj_pop_exception_handler_(void) +{ + struct pj_exception_state_t *handler; + + handler = pj_thread_local_get(thread_local_id); + pj_assert(handler != NULL); + pj_thread_local_set(thread_local_id, handler->prev); +} + +#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0 +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + unsigned i; + + pj_enter_critical_section(); + + /* + * Start from 1 (not 0)!!! + * Exception 0 is reserved for normal path of setjmp()!!! + */ + for (i=1; i0 && id0 && id"); + + if (exception_id_names[id] == NULL) + return ""; + + return exception_id_names[id]; +} + +#else /* PJ_HAS_EXCEPTION_NAMES */ +PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name, + pj_exception_id_t *id) +{ + PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY); + + *id = last_exception_id++ + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id ) +{ + return PJ_SUCCESS; +} + +PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id) +{ + return ""; +} + +#endif /* PJ_HAS_EXCEPTION_NAMES */ + + + diff --git a/pjlib/src/pj/extra-exports.c b/pjlib/src/pj/extra-exports.c index 4cc12e3f5..83e88f967 100644 --- a/pjlib/src/pj/extra-exports.c +++ b/pjlib/src/pj/extra-exports.c @@ -1,38 +1,38 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/extra-exports.c 1 10/29/05 11:56a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/extra-exports.c $ - * - * 1 10/29/05 11:56a Bennylp - * Version 0.3-pre2 - * - */ - -/* - * This file contains code to export extra symbols from Linux kernel. - * It should be copied to Linux kernel source tree and added to - * Linux kernel combilation. - * - * This file is part of PJLIB project. - */ -#include -#include - -EXPORT_SYMBOL(sys_select); - -EXPORT_SYMBOL(sys_epoll_create); -EXPORT_SYMBOL(sys_epoll_ctl); -EXPORT_SYMBOL(sys_epoll_wait); - -EXPORT_SYMBOL(sys_socket); -EXPORT_SYMBOL(sys_bind); -EXPORT_SYMBOL(sys_getpeername); -EXPORT_SYMBOL(sys_getsockname); -EXPORT_SYMBOL(sys_sendto); -EXPORT_SYMBOL(sys_recvfrom); -EXPORT_SYMBOL(sys_getsockopt); -EXPORT_SYMBOL(sys_setsockopt); -EXPORT_SYMBOL(sys_listen); -EXPORT_SYMBOL(sys_shutdown); -EXPORT_SYMBOL(sys_connect); -EXPORT_SYMBOL(sys_accept); - +/* $Header: /pjproject-0.3/pjlib/src/pj/extra-exports.c 1 10/29/05 11:56a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/extra-exports.c $ + * + * 1 10/29/05 11:56a Bennylp + * Version 0.3-pre2 + * + */ + +/* + * This file contains code to export extra symbols from Linux kernel. + * It should be copied to Linux kernel source tree and added to + * Linux kernel combilation. + * + * This file is part of PJLIB project. + */ +#include +#include + +EXPORT_SYMBOL(sys_select); + +EXPORT_SYMBOL(sys_epoll_create); +EXPORT_SYMBOL(sys_epoll_ctl); +EXPORT_SYMBOL(sys_epoll_wait); + +EXPORT_SYMBOL(sys_socket); +EXPORT_SYMBOL(sys_bind); +EXPORT_SYMBOL(sys_getpeername); +EXPORT_SYMBOL(sys_getsockname); +EXPORT_SYMBOL(sys_sendto); +EXPORT_SYMBOL(sys_recvfrom); +EXPORT_SYMBOL(sys_getsockopt); +EXPORT_SYMBOL(sys_setsockopt); +EXPORT_SYMBOL(sys_listen); +EXPORT_SYMBOL(sys_shutdown); +EXPORT_SYMBOL(sys_connect); +EXPORT_SYMBOL(sys_accept); + diff --git a/pjlib/src/pj/fifobuf.c b/pjlib/src/pj/fifobuf.c index d0b41a157..c100129c8 100644 --- a/pjlib/src/pj/fifobuf.c +++ b/pjlib/src/pj/fifobuf.c @@ -1,182 +1,182 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/fifobuf.c 4 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/fifobuf.c $ - * - * 4 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include - -#define THIS_FILE "fifobuf" - -#define SZ sizeof(unsigned) - -PJ_DEF(void) -pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size) -{ - PJ_CHECK_STACK(); - - PJ_LOG(6, (THIS_FILE, - "fifobuf_init fifobuf=%p buffer=%p, size=%d", - fifobuf, buffer, size)); - - fifobuf->first = buffer; - fifobuf->last = fifobuf->first + size; - fifobuf->ubegin = fifobuf->uend = fifobuf->first; - fifobuf->full = 0; -} - -PJ_DEF(unsigned) -pj_fifobuf_max_size (pj_fifobuf_t *fifobuf) -{ - unsigned s1, s2; - - PJ_CHECK_STACK(); - - if (fifobuf->uend >= fifobuf->ubegin) { - s1 = fifobuf->last - fifobuf->uend; - s2 = fifobuf->ubegin - fifobuf->first; - } else { - s1 = s2 = fifobuf->ubegin - fifobuf->uend; - } - - return s1full) { - PJ_LOG(6, (THIS_FILE, - "fifobuf_alloc fifobuf=%p, size=%d: full!", - fifobuf, size)); - return NULL; - } - - /* try to allocate from the end part of the fifo */ - if (fifobuf->uend >= fifobuf->ubegin) { - available = fifobuf->last - fifobuf->uend; - if (available >= size+SZ) { - char *ptr = fifobuf->uend; - fifobuf->uend += (size+SZ); - if (fifobuf->uend == fifobuf->last) - fifobuf->uend = fifobuf->first; - if (fifobuf->uend == fifobuf->ubegin) - fifobuf->full = 1; - *(unsigned*)ptr = size+SZ; - ptr += SZ; - - PJ_LOG(6, (THIS_FILE, - "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", - fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); - return ptr; - } - } - - /* try to allocate from the start part of the fifo */ - start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first; - available = fifobuf->ubegin - start; - if (available >= size+SZ) { - char *ptr = start; - fifobuf->uend = start + size + SZ; - if (fifobuf->uend == fifobuf->ubegin) - fifobuf->full = 1; - *(unsigned*)ptr = size+SZ; - ptr += SZ; - - PJ_LOG(6, (THIS_FILE, - "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", - fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); - return ptr; - } - - PJ_LOG(6, (THIS_FILE, - "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p", - fifobuf, size, fifobuf->ubegin, fifobuf->uend)); - return NULL; -} - -PJ_DEF(pj_status_t) -pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf) -{ - char *ptr = buf; - char *endptr; - unsigned sz; - - PJ_CHECK_STACK(); - - ptr -= SZ; - sz = *(unsigned*)ptr; - - endptr = fifobuf->uend; - if (endptr == fifobuf->first) - endptr = fifobuf->last; - - if (ptr+sz != endptr) { - pj_assert(!"Invalid pointer to undo alloc"); - return -1; - } - - fifobuf->uend = ptr; - fifobuf->full = 0; - - PJ_LOG(6, (THIS_FILE, - "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", - fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); - - return 0; -} - -PJ_DEF(pj_status_t) -pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) -{ - char *ptr = buf; - char *end; - unsigned sz; - - PJ_CHECK_STACK(); - - ptr -= SZ; - if (ptr < fifobuf->first || ptr >= fifobuf->last) { - pj_assert(!"Invalid pointer to free"); - return -1; - } - - if (ptr != fifobuf->ubegin && ptr != fifobuf->first) { - pj_assert(!"Invalid free() sequence!"); - return -1; - } - - end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last; - sz = *(unsigned*)ptr; - if (ptr+sz > end) { - pj_assert(!"Invalid size!"); - return -1; - } - - fifobuf->ubegin = ptr + sz; - - /* Rollover */ - if (fifobuf->ubegin == fifobuf->last) - fifobuf->ubegin = fifobuf->first; - - /* Reset if fifobuf is empty */ - if (fifobuf->ubegin == fifobuf->uend) - fifobuf->ubegin = fifobuf->uend = fifobuf->first; - - fifobuf->full = 0; - - PJ_LOG(6, (THIS_FILE, - "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", - fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); - - return 0; -} +/* $Header: /pjproject-0.3/pjlib/src/pj/fifobuf.c 4 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/fifobuf.c $ + * + * 4 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include + +#define THIS_FILE "fifobuf" + +#define SZ sizeof(unsigned) + +PJ_DEF(void) +pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size) +{ + PJ_CHECK_STACK(); + + PJ_LOG(6, (THIS_FILE, + "fifobuf_init fifobuf=%p buffer=%p, size=%d", + fifobuf, buffer, size)); + + fifobuf->first = buffer; + fifobuf->last = fifobuf->first + size; + fifobuf->ubegin = fifobuf->uend = fifobuf->first; + fifobuf->full = 0; +} + +PJ_DEF(unsigned) +pj_fifobuf_max_size (pj_fifobuf_t *fifobuf) +{ + unsigned s1, s2; + + PJ_CHECK_STACK(); + + if (fifobuf->uend >= fifobuf->ubegin) { + s1 = fifobuf->last - fifobuf->uend; + s2 = fifobuf->ubegin - fifobuf->first; + } else { + s1 = s2 = fifobuf->ubegin - fifobuf->uend; + } + + return s1full) { + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: full!", + fifobuf, size)); + return NULL; + } + + /* try to allocate from the end part of the fifo */ + if (fifobuf->uend >= fifobuf->ubegin) { + available = fifobuf->last - fifobuf->uend; + if (available >= size+SZ) { + char *ptr = fifobuf->uend; + fifobuf->uend += (size+SZ); + if (fifobuf->uend == fifobuf->last) + fifobuf->uend = fifobuf->first; + if (fifobuf->uend == fifobuf->ubegin) + fifobuf->full = 1; + *(unsigned*)ptr = size+SZ; + ptr += SZ; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", + fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); + return ptr; + } + } + + /* try to allocate from the start part of the fifo */ + start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first; + available = fifobuf->ubegin - start; + if (available >= size+SZ) { + char *ptr = start; + fifobuf->uend = start + size + SZ; + if (fifobuf->uend == fifobuf->ubegin) + fifobuf->full = 1; + *(unsigned*)ptr = size+SZ; + ptr += SZ; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", + fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend)); + return ptr; + } + + PJ_LOG(6, (THIS_FILE, + "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p", + fifobuf, size, fifobuf->ubegin, fifobuf->uend)); + return NULL; +} + +PJ_DEF(pj_status_t) +pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf) +{ + char *ptr = buf; + char *endptr; + unsigned sz; + + PJ_CHECK_STACK(); + + ptr -= SZ; + sz = *(unsigned*)ptr; + + endptr = fifobuf->uend; + if (endptr == fifobuf->first) + endptr = fifobuf->last; + + if (ptr+sz != endptr) { + pj_assert(!"Invalid pointer to undo alloc"); + return -1; + } + + fifobuf->uend = ptr; + fifobuf->full = 0; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", + fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); + + return 0; +} + +PJ_DEF(pj_status_t) +pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf) +{ + char *ptr = buf; + char *end; + unsigned sz; + + PJ_CHECK_STACK(); + + ptr -= SZ; + if (ptr < fifobuf->first || ptr >= fifobuf->last) { + pj_assert(!"Invalid pointer to free"); + return -1; + } + + if (ptr != fifobuf->ubegin && ptr != fifobuf->first) { + pj_assert(!"Invalid free() sequence!"); + return -1; + } + + end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last; + sz = *(unsigned*)ptr; + if (ptr+sz > end) { + pj_assert(!"Invalid size!"); + return -1; + } + + fifobuf->ubegin = ptr + sz; + + /* Rollover */ + if (fifobuf->ubegin == fifobuf->last) + fifobuf->ubegin = fifobuf->first; + + /* Reset if fifobuf is empty */ + if (fifobuf->ubegin == fifobuf->uend) + fifobuf->ubegin = fifobuf->uend = fifobuf->first; + + fifobuf->full = 0; + + PJ_LOG(6, (THIS_FILE, + "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", + fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend)); + + return 0; +} diff --git a/pjlib/src/pj/guid.c b/pjlib/src/pj/guid.c index 20cb72a7e..e3195d09e 100644 --- a/pjlib/src/pj/guid.c +++ b/pjlib/src/pj/guid.c @@ -1,19 +1,19 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/guid.c 12 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/guid.c $ - * - * 12 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 11 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include - -PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str) -{ - str->ptr = pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH); - pj_generate_unique_string(str); -} +/* $Header: /pjproject-0.3/pjlib/src/pj/guid.c 12 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/guid.c $ + * + * 12 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 11 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include + +PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str) +{ + str->ptr = pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH); + pj_generate_unique_string(str); +} diff --git a/pjlib/src/pj/guid_simple.c b/pjlib/src/pj/guid_simple.c index b5cca3108..f80241732 100644 --- a/pjlib/src/pj/guid_simple.c +++ b/pjlib/src/pj/guid_simple.c @@ -1,60 +1,60 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/guid_simple.c 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/guid_simple.c $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include - -const unsigned PJ_GUID_STRING_LENGTH=20; - -static void init_mac_address(unsigned char mac_addr[16]) -{ - unsigned long *ulval1 = (unsigned long*) &mac_addr[0]; - unsigned short *usval1 = (unsigned short*) &mac_addr[4]; - - *ulval1 = pj_rand(); - *usval1 = (unsigned short) pj_rand(); -} - -PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) -{ - static int guid_initialized; - static unsigned pid; - static char str_pid[5]; - static unsigned char mac_addr[6]; - static char str_mac_addr[16]; - static unsigned clock_seq; - - PJ_CHECK_STACK(); - - if (guid_initialized == 0) { - pid = pj_getpid(); - init_mac_address(mac_addr); - clock_seq = 0; - - sprintf(str_pid, "%04x", pid); - sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x", - mac_addr[0], mac_addr[1], mac_addr[2], - mac_addr[3], mac_addr[4], mac_addr[5]); - - guid_initialized = 1; - } - - strcpy(str->ptr, str_pid); - sprintf(str->ptr+4, "%04x", clock_seq++); - pj_memcpy(str->ptr+8, str_mac_addr, 12); - str->slen = 20; - - return str; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/guid_simple.c 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/guid_simple.c $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include + +const unsigned PJ_GUID_STRING_LENGTH=20; + +static void init_mac_address(unsigned char mac_addr[16]) +{ + unsigned long *ulval1 = (unsigned long*) &mac_addr[0]; + unsigned short *usval1 = (unsigned short*) &mac_addr[4]; + + *ulval1 = pj_rand(); + *usval1 = (unsigned short) pj_rand(); +} + +PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) +{ + static int guid_initialized; + static unsigned pid; + static char str_pid[5]; + static unsigned char mac_addr[6]; + static char str_mac_addr[16]; + static unsigned clock_seq; + + PJ_CHECK_STACK(); + + if (guid_initialized == 0) { + pid = pj_getpid(); + init_mac_address(mac_addr); + clock_seq = 0; + + sprintf(str_pid, "%04x", pid); + sprintf(str_mac_addr, "%02x%02x%02x%02x%02x%02x", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + guid_initialized = 1; + } + + strcpy(str->ptr, str_pid); + sprintf(str->ptr+4, "%04x", clock_seq++); + pj_memcpy(str->ptr+8, str_mac_addr, 12); + str->slen = 20; + + return str; +} + diff --git a/pjlib/src/pj/guid_win32.c b/pjlib/src/pj/guid_win32.c index 8e3707ec3..f2a3f8556 100644 --- a/pjlib/src/pj/guid_win32.c +++ b/pjlib/src/pj/guid_win32.c @@ -1,61 +1,61 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/guid_win32.c 4 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/guid_win32.c $ - * - * 4 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 3 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include - - -const unsigned PJ_GUID_STRING_LENGTH=32; - -PJ_INLINE(void) hex2digit(unsigned value, char *p) -{ - static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - *p++ = hex[ (value & 0xF0) >> 4 ]; - *p++ = hex[ (value & 0x0F) ]; -} - -static void guid_to_str( const GUID *guid, pj_str_t *str ) -{ - unsigned i; - GUID guid_copy; - const unsigned char *src = (const unsigned char*)&guid_copy; - char *dst = str->ptr; - - pj_memcpy(&guid_copy, guid, sizeof(*guid)); - guid_copy.Data1 = pj_ntohl(guid_copy.Data1); - guid_copy.Data2 = pj_ntohs(guid_copy.Data2); - guid_copy.Data3 = pj_ntohs(guid_copy.Data3); - - for (i=0; i<16; ++i) { - hex2digit( *src, dst ); - dst += 2; - ++src; - } - str->slen = 32; -} - - -PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) -{ - GUID guid; - - PJ_CHECK_STACK(); - - CoCreateGuid(&guid); - guid_to_str( &guid, str ); - return str; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/guid_win32.c 4 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/guid_win32.c $ + * + * 4 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 3 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include + + +const unsigned PJ_GUID_STRING_LENGTH=32; + +PJ_INLINE(void) hex2digit(unsigned value, char *p) +{ + static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + *p++ = hex[ (value & 0xF0) >> 4 ]; + *p++ = hex[ (value & 0x0F) ]; +} + +static void guid_to_str( const GUID *guid, pj_str_t *str ) +{ + unsigned i; + GUID guid_copy; + const unsigned char *src = (const unsigned char*)&guid_copy; + char *dst = str->ptr; + + pj_memcpy(&guid_copy, guid, sizeof(*guid)); + guid_copy.Data1 = pj_ntohl(guid_copy.Data1); + guid_copy.Data2 = pj_ntohs(guid_copy.Data2); + guid_copy.Data3 = pj_ntohs(guid_copy.Data3); + + for (i=0; i<16; ++i) { + hex2digit( *src, dst ); + dst += 2; + ++src; + } + str->slen = 32; +} + + +PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str) +{ + GUID guid; + + PJ_CHECK_STACK(); + + CoCreateGuid(&guid); + guid_to_str( &guid, str ); + return str; +} + diff --git a/pjlib/src/pj/hash.c b/pjlib/src/pj/hash.c index 01bfaa6bd..69b577d30 100644 --- a/pjlib/src/pj/hash.c +++ b/pjlib/src/pj/hash.c @@ -1,252 +1,252 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/hash.c 8 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/hash.c $ - * - * 8 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 7 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include - -/** - * The hash multiplier used to calculate hash value. - */ -#define PJ_HASH_MULTIPLIER 33 - - -struct pj_hash_entry -{ - struct pj_hash_entry *next; - const void *key; - pj_uint32_t hash; - pj_uint32_t keylen; - void *value; -}; - - -struct pj_hash_table_t -{ - pj_hash_entry **table; - unsigned count, rows; - pj_hash_iterator_t iterator; -}; - - - -PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen) -{ - PJ_CHECK_STACK(); - - if (keylen==PJ_HASH_KEY_STRING) { - const unsigned char *p = key; - for ( ; *p; ++p ) { - hash = hash * PJ_HASH_MULTIPLIER + *p; - } - keylen = p - (const unsigned char*)key; - } else { - const unsigned char *p = key, - *end = p + keylen; - for ( ; p!=end; ++p) { - hash = hash * PJ_HASH_MULTIPLIER + *p; - } - } - return hash; -} - - -PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size) -{ - pj_hash_table_t *h; - unsigned table_size; - - h = pj_pool_alloc(pool, sizeof(pj_hash_table_t)); - h->count = 0; - - PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool))); - - /* size must be 2^n - 1. - round-up the size to this rule, except when size is 2^n, then size - will be round-down to 2^n-1. - */ - table_size = 8; - do { - table_size <<= 1; - } while (table_size <= size); - table_size -= 1; - - h->rows = table_size; - h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*)); - return h; -} - -static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht, - const void *key, unsigned keylen, - void *val) -{ - pj_uint32_t hash; - pj_hash_entry **p_entry, *entry; - - hash=0; - if (keylen==PJ_HASH_KEY_STRING) { - const unsigned char *p = key; - for ( ; *p; ++p ) { - hash = hash * PJ_HASH_MULTIPLIER + *p; - } - keylen = p - (const unsigned char*)key; - } else { - const unsigned char *p = key, - *end = p + keylen; - for ( ; p!=end; ++p) { - hash = hash * PJ_HASH_MULTIPLIER + *p; - } - } - - /* scan the linked list */ - for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry; - entry; - p_entry = &entry->next, entry = *p_entry) - { - if (entry->hash==hash && entry->keylen==keylen && - memcmp(entry->key, key, keylen)==0) - { - break; - } - } - - if (entry || val==NULL) - return p_entry; - - /* create a new entry */ - entry = pj_pool_alloc(pool, sizeof(pj_hash_entry)); - PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry, - pj_pool_get_used_size(pool), pj_pool_get_capacity(pool))); - entry->next = NULL; - entry->hash = hash; - entry->key = key; - entry->keylen = keylen; - entry->value = val; - *p_entry = entry; - - ++ht->count; - - return p_entry; -} - -PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht, - const void *key, unsigned keylen ) -{ - pj_hash_entry *entry; - entry = *find_entry( NULL, ht, key, keylen, NULL); - return entry ? entry->value : NULL; -} - -PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, - const void *key, unsigned keylen, - void *value ) -{ - pj_hash_entry **p_entry; - - p_entry = find_entry( pool, ht, key, keylen, value ); - if (*p_entry) { - if (value == NULL) { - /* delete entry */ - PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry)); - *p_entry = (*p_entry)->next; - --ht->count; - - } else { - /* overwrite */ - (*p_entry)->value = value; - PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value)); - } - } -} - -PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht ) -{ - return ht->count; -} - -PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, - pj_hash_iterator_t *it ) -{ - it->index = 0; - it->entry = NULL; - - for (; it->index < ht->rows; ++it->index) { - it->entry = ht->table[it->index]; - if (it->entry) { - break; - } - } - - return it->entry ? it : NULL; -} - -PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, - pj_hash_iterator_t *it ) -{ - it->entry = it->entry->next; - if (it->entry) { - return it; - } - - for (++it->index; it->index < ht->rows; ++it->index) { - it->entry = ht->table[it->index]; - if (it->entry) { - break; - } - } - - return it->entry ? it : NULL; -} - -PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it ) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ht); - return it->entry->value; -} - -#if 0 -void pj_hash_dump_collision( pj_hash_table_t *ht ) -{ - unsigned min=0xFFFFFFFF, max=0; - unsigned i; - char line[120]; - int len, totlen = 0; - - for (i=0; irows; ++i) { - unsigned count = 0; - pj_hash_entry *entry = ht->table[i]; - while (entry) { - ++count; - entry = entry->next; - } - if (count < min) - min = count; - if (count > max) - max = count; - len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count); - if (len < 1) - break; - totlen += len; - - if ((i+1) % 10 == 0) { - line[totlen] = '\0'; - PJ_LOG(4,(__FILE__, line)); - } - } - - PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max)); -} -#endif - - +/* $Header: /pjproject-0.3/pjlib/src/pj/hash.c 8 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/hash.c $ + * + * 8 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 7 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include + +/** + * The hash multiplier used to calculate hash value. + */ +#define PJ_HASH_MULTIPLIER 33 + + +struct pj_hash_entry +{ + struct pj_hash_entry *next; + const void *key; + pj_uint32_t hash; + pj_uint32_t keylen; + void *value; +}; + + +struct pj_hash_table_t +{ + pj_hash_entry **table; + unsigned count, rows; + pj_hash_iterator_t iterator; +}; + + + +PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, unsigned keylen) +{ + PJ_CHECK_STACK(); + + if (keylen==PJ_HASH_KEY_STRING) { + const unsigned char *p = key; + for ( ; *p; ++p ) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + keylen = p - (const unsigned char*)key; + } else { + const unsigned char *p = key, + *end = p + keylen; + for ( ; p!=end; ++p) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + } + return hash; +} + + +PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size) +{ + pj_hash_table_t *h; + unsigned table_size; + + h = pj_pool_alloc(pool, sizeof(pj_hash_table_t)); + h->count = 0; + + PJ_LOG( 5, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool))); + + /* size must be 2^n - 1. + round-up the size to this rule, except when size is 2^n, then size + will be round-down to 2^n-1. + */ + table_size = 8; + do { + table_size <<= 1; + } while (table_size <= size); + table_size -= 1; + + h->rows = table_size; + h->table = pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*)); + return h; +} + +static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, + void *val) +{ + pj_uint32_t hash; + pj_hash_entry **p_entry, *entry; + + hash=0; + if (keylen==PJ_HASH_KEY_STRING) { + const unsigned char *p = key; + for ( ; *p; ++p ) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + keylen = p - (const unsigned char*)key; + } else { + const unsigned char *p = key, + *end = p + keylen; + for ( ; p!=end; ++p) { + hash = hash * PJ_HASH_MULTIPLIER + *p; + } + } + + /* scan the linked list */ + for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry; + entry; + p_entry = &entry->next, entry = *p_entry) + { + if (entry->hash==hash && entry->keylen==keylen && + memcmp(entry->key, key, keylen)==0) + { + break; + } + } + + if (entry || val==NULL) + return p_entry; + + /* create a new entry */ + entry = pj_pool_alloc(pool, sizeof(pj_hash_entry)); + PJ_LOG(5, ("hashtbl", "%p: New p_entry %p created, pool used=%u, cap=%u", ht, entry, + pj_pool_get_used_size(pool), pj_pool_get_capacity(pool))); + entry->next = NULL; + entry->hash = hash; + entry->key = key; + entry->keylen = keylen; + entry->value = val; + *p_entry = entry; + + ++ht->count; + + return p_entry; +} + +PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht, + const void *key, unsigned keylen ) +{ + pj_hash_entry *entry; + entry = *find_entry( NULL, ht, key, keylen, NULL); + return entry ? entry->value : NULL; +} + +PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht, + const void *key, unsigned keylen, + void *value ) +{ + pj_hash_entry **p_entry; + + p_entry = find_entry( pool, ht, key, keylen, value ); + if (*p_entry) { + if (value == NULL) { + /* delete entry */ + PJ_LOG(5, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry)); + *p_entry = (*p_entry)->next; + --ht->count; + + } else { + /* overwrite */ + (*p_entry)->value = value; + PJ_LOG(5, ("hashtbl", "%p: p_entry %p value set to %p", ht, *p_entry, value)); + } + } +} + +PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht ) +{ + return ht->count; +} + +PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht, + pj_hash_iterator_t *it ) +{ + it->index = 0; + it->entry = NULL; + + for (; it->index < ht->rows; ++it->index) { + it->entry = ht->table[it->index]; + if (it->entry) { + break; + } + } + + return it->entry ? it : NULL; +} + +PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, + pj_hash_iterator_t *it ) +{ + it->entry = it->entry->next; + if (it->entry) { + return it; + } + + for (++it->index; it->index < ht->rows; ++it->index) { + it->entry = ht->table[it->index]; + if (it->entry) { + break; + } + } + + return it->entry ? it : NULL; +} + +PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ht); + return it->entry->value; +} + +#if 0 +void pj_hash_dump_collision( pj_hash_table_t *ht ) +{ + unsigned min=0xFFFFFFFF, max=0; + unsigned i; + char line[120]; + int len, totlen = 0; + + for (i=0; irows; ++i) { + unsigned count = 0; + pj_hash_entry *entry = ht->table[i]; + while (entry) { + ++count; + entry = entry->next; + } + if (count < min) + min = count; + if (count > max) + max = count; + len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count); + if (len < 1) + break; + totlen += len; + + if ((i+1) % 10 == 0) { + line[totlen] = '\0'; + PJ_LOG(4,(__FILE__, line)); + } + } + + PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max)); +} +#endif + + diff --git a/pjlib/src/pj/ioqueue_dummy.c b/pjlib/src/pj/ioqueue_dummy.c index 63abc15b3..125994da8 100644 --- a/pjlib/src/pj/ioqueue_dummy.c +++ b/pjlib/src/pj/ioqueue_dummy.c @@ -1,186 +1,186 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_dummy.c 2 10/29/05 11:31a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_dummy.c $ - * - * 2 10/29/05 11:31a Bennylp - * Changed accept and lock. - * - * 1 10/23/05 12:53p Bennylp - * Created. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define THIS_FILE "ioqueue" - -#define PJ_IOQUEUE_IS_READ_OP(op) \ - ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) -#define PJ_IOQUEUE_IS_WRITE_OP(op) \ - ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) - - -#if PJ_HAS_TCP -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) -# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) -#else -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 -# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 -#endif - -#if defined(PJ_DEBUG) && PJ_DEBUG != 0 -# define VALIDATE_FD_SET 1 -#else -# define VALIDATE_FD_SET 0 -#endif - -struct pj_ioqueue_key_t -{ - PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) - pj_sock_t fd; - pj_ioqueue_operation_e op; - void *user_data; - pj_ioqueue_callback cb; -}; - -struct pj_ioqueue_t -{ -}; - -PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, - pj_size_t max_fd, - int max_threads, - pj_ioqueue_t **ptr_ioqueue) -{ - return PJ_ENOTSUP; -} - -PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) -{ - return PJ_ENOTSUP; -} - -PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, - pj_lock_t *lock, - pj_bool_t auto_delete ) -{ - return PJ_ENOTSUP; -} - -PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, - pj_ioqueue_t *ioque, - pj_sock_t sock, - void *user_data, - const pj_ioqueue_callback *cb, - pj_ioqueue_key_t **ptr_key) -{ - return PJ_ENOTSUP; -} - -PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key) -{ - return PJ_ENOTSUP; -} - -PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) -{ - return NULL; -} - - -PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) -{ - return -1; -} - -PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen) -{ - return -1; -} - -PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags) -{ - return -1; -} - -PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags, - pj_sockaddr_t *addr, - int *addrlen) -{ - return -1; -} - -PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen) -{ - return -1; -} - -PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags) -{ - return -1; -} - -PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags, - const pj_sockaddr_t *addr, - int addrlen) -{ - return -1; -} - -#if PJ_HAS_TCP -/* - * Initiate overlapped accept() operation. - */ -PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - pj_sock_t *new_sock, - pj_sockaddr_t *local, - pj_sockaddr_t *remote, - int *addrlen) -{ - return -1; -} - -/* - * Initiate overlapped connect() operation (well, it's non-blocking actually, - * since there's no overlapped version of connect()). - */ -PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - const pj_sockaddr_t *addr, - int addrlen ) -{ - return -1; -} -#endif /* PJ_HAS_TCP */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_dummy.c 2 10/29/05 11:31a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_dummy.c $ + * + * 2 10/29/05 11:31a Bennylp + * Changed accept and lock. + * + * 1 10/23/05 12:53p Bennylp + * Created. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "ioqueue" + +#define PJ_IOQUEUE_IS_READ_OP(op) \ + ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) \ + ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif + +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; +}; + +struct pj_ioqueue_t +{ +}; + +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + int max_threads, + pj_ioqueue_t **ptr_ioqueue) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **ptr_key) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + return PJ_ENOTSUP; +} + +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + return NULL; +} + + +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags) +{ + return -1; +} + +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + return -1; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + return -1; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + return -1; +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_epoll.c b/pjlib/src/pj/ioqueue_epoll.c index 7bbfe1352..b41d05f08 100644 --- a/pjlib/src/pj/ioqueue_epoll.c +++ b/pjlib/src/pj/ioqueue_epoll.c @@ -1,852 +1,852 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_epoll.c 4 10/29/05 10:27p Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_epoll.c $ - * - * 4 10/29/05 10:27p Bennylp - * Fixed misc warnings. - * - * 3 10/29/05 11:49a Bennylp - * Fixed warnings. - * - * 2 10/29/05 11:31a Bennylp - * Changed accept and lock. - * - * 1 10/17/05 10:49p Bennylp - * Created. - * - */ - -/* - * ioqueue_epoll.c - * - * This is the implementation of IOQueue framework using /dev/epoll - * API in _both_ Linux user-mode and kernel-mode. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0 - /* - * Linux user mode - */ -# include -# include -# include - -# define epoll_data data.ptr -# define epoll_data_type void* -# define ioctl_val_type unsigned long* -# define getsockopt_val_ptr int* -# define os_getsockopt getsockopt -# define os_ioctl ioctl -# define os_read read -# define os_close close -# define os_epoll_create epoll_create -# define os_epoll_ctl epoll_ctl -# define os_epoll_wait epoll_wait -#else - /* - * Linux kernel mode. - */ -# include -# include -# if defined(MODVERSIONS) -# include -# endif -# include -# include -# include -# include -# include -# include -# include - enum EPOLL_EVENTS - { - EPOLLIN = 0x001, - EPOLLOUT = 0x004, - EPOLLERR = 0x008, - }; -# define os_epoll_create sys_epoll_create - static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) - { - long rc; - mm_segment_t oldfs = get_fs(); - set_fs(KERNEL_DS); - rc = sys_epoll_ctl(epfd, op, fd, event); - set_fs(oldfs); - if (rc) { - errno = -rc; - return -1; - } else { - return 0; - } - } - static int os_epoll_wait(int epfd, struct epoll_event *events, - int maxevents, int timeout) - { - int count; - mm_segment_t oldfs = get_fs(); - set_fs(KERNEL_DS); - count = sys_epoll_wait(epfd, events, maxevents, timeout); - set_fs(oldfs); - return count; - } -# define os_close sys_close -# define os_getsockopt pj_sock_getsockopt - static int os_read(int fd, void *buf, size_t len) - { - long rc; - mm_segment_t oldfs = get_fs(); - set_fs(KERNEL_DS); - rc = sys_read(fd, buf, len); - set_fs(oldfs); - if (rc) { - errno = -rc; - return -1; - } else { - return 0; - } - } -# define socklen_t unsigned -# define ioctl_val_type unsigned long - int ioctl(int fd, int opt, ioctl_val_type value); - static int os_ioctl(int fd, int opt, ioctl_val_type value) - { - int rc; - mm_segment_t oldfs = get_fs(); - set_fs(KERNEL_DS); - rc = ioctl(fd, opt, value); - set_fs(oldfs); - if (rc < 0) { - errno = -rc; - return rc; - } else - return rc; - } -# define getsockopt_val_ptr char* - -# define epoll_data data -# define epoll_data_type __u32 -#endif - -#define THIS_FILE "ioq_epoll" - -#define PJ_IOQUEUE_IS_READ_OP(op) ((op & PJ_IOQUEUE_OP_READ) || \ - (op & PJ_IOQUEUE_OP_RECV) || \ - (op & PJ_IOQUEUE_OP_RECV_FROM)) -#define PJ_IOQUEUE_IS_WRITE_OP(op) ((op & PJ_IOQUEUE_OP_WRITE) || \ - (op & PJ_IOQUEUE_OP_SEND) || \ - (op & PJ_IOQUEUE_OP_SEND_TO)) - - -#if PJ_HAS_TCP -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) -# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) -#else -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 -# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 -#endif - - -//#define TRACE_(expr) PJ_LOG(3,expr) -#define TRACE_(expr) - - -/* - * This describes each key. - */ -struct pj_ioqueue_key_t -{ - PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) - pj_sock_t fd; - pj_ioqueue_operation_e op; - void *user_data; - pj_ioqueue_callback cb; - - void *rd_buf; - unsigned rd_flags; - pj_size_t rd_buflen; - void *wr_buf; - pj_size_t wr_buflen; - - pj_sockaddr_t *rmt_addr; - int *rmt_addrlen; - - pj_sockaddr_t *local_addr; - int *local_addrlen; - - pj_sock_t *accept_fd; -}; - -/* - * This describes the I/O queue. - */ -struct pj_ioqueue_t -{ - pj_lock_t *lock; - pj_bool_t auto_delete_lock; - unsigned max, count; - pj_ioqueue_key_t hlist; - int epfd; -}; - -/* - * pj_ioqueue_create() - * - * Create select ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, - pj_size_t max_fd, - int max_threads, - pj_ioqueue_t **p_ioqueue) -{ - pj_ioqueue_t *ioque; - pj_status_t rc; - - PJ_UNUSED_ARG(max_threads); - - if (max_fd > PJ_IOQUEUE_MAX_HANDLES) { - pj_assert(!"max_fd too large"); - return PJ_EINVAL; - } - - ioque = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); - ioque->max = max_fd; - ioque->count = 0; - pj_list_init(&ioque->hlist); - - rc = pj_lock_create_recursive_mutex(pool, "ioq%p", &ioque->lock); - if (rc != PJ_SUCCESS) - return rc; - - ioque->auto_delete_lock = PJ_TRUE; - ioque->epfd = os_epoll_create(max_fd); - if (ioque->epfd < 0) { - return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); - } - - PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioque)); - - *p_ioqueue = ioque; - return PJ_SUCCESS; -} - -/* - * pj_ioqueue_destroy() - * - * Destroy ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) -{ - PJ_ASSERT_RETURN(ioque, PJ_EINVAL); - PJ_ASSERT_RETURN(ioque->epfd > 0, PJ_EINVALIDOP); - - pj_lock_acquire(ioque->lock); - os_close(ioque->epfd); - ioque->epfd = 0; - if (ioque->auto_delete_lock) - pj_lock_destroy(ioque->lock); - - return PJ_SUCCESS; -} - -/* - * pj_ioqueue_set_lock() - */ -PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, - pj_lock_t *lock, - pj_bool_t auto_delete ) -{ - PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL); - - if (ioque->auto_delete_lock) { - pj_lock_destroy(ioque->lock); - } - - ioque->lock = lock; - ioque->auto_delete_lock = auto_delete; - - return PJ_SUCCESS; -} - - -/* - * pj_ioqueue_register_sock() - * - * Register a socket to ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, - pj_ioqueue_t *ioque, - pj_sock_t sock, - void *user_data, - const pj_ioqueue_callback *cb, - pj_ioqueue_key_t **p_key) -{ - pj_ioqueue_key_t *key = NULL; - pj_uint32_t value; - struct epoll_event ev; - int status; - pj_status_t rc = PJ_SUCCESS; - - PJ_ASSERT_RETURN(pool && ioque && sock != PJ_INVALID_SOCKET && - cb && p_key, PJ_EINVAL); - - pj_lock_acquire(ioque->lock); - - if (ioque->count >= ioque->max) { - rc = PJ_ETOOMANY; - TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files")); - goto on_return; - } - - /* Set socket to nonblocking. */ - value = 1; - if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) { - TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d", - rc)); - rc = pj_get_netos_error(); - goto on_return; - } - - /* Create key. */ - key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); - key->fd = sock; - key->user_data = user_data; - pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); - - /* os_epoll_ctl. */ - ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; - ev.epoll_data = (epoll_data_type)key; - status = os_epoll_ctl(ioque->epfd, EPOLL_CTL_ADD, sock, &ev); - if (status < 0) { - rc = pj_get_os_error(); - TRACE_((THIS_FILE, - "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d", - status)); - goto on_return; - } - - /* Register */ - pj_list_insert_before(&ioque->hlist, key); - ++ioque->count; - -on_return: - *p_key = key; - pj_lock_release(ioque->lock); - - return rc; -} - -/* - * pj_ioqueue_unregister() - * - * Unregister handle from ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key) -{ - struct epoll_event ev; - int status; - - PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL); - - pj_lock_acquire(ioque->lock); - - pj_assert(ioque->count > 0); - --ioque->count; - pj_list_erase(key); - - ev.events = 0; - ev.epoll_data = (epoll_data_type)key; - status = os_epoll_ctl( ioque->epfd, EPOLL_CTL_DEL, key->fd, &ev); - if (status != 0) { - pj_status_t rc = pj_get_os_error(); - pj_lock_release(ioque->lock); - return rc; - } - - pj_lock_release(ioque->lock); - return PJ_SUCCESS; -} - -/* - * pj_ioqueue_get_user_data() - * - * Obtain value associated with a key. - */ -PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) -{ - PJ_ASSERT_RETURN(key != NULL, NULL); - return key->user_data; -} - - -/* - * pj_ioqueue_poll() - * - */ -PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) -{ - int i, count, processed; - struct epoll_event events[16]; - int msec; - - PJ_CHECK_STACK(); - - msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000; - - count = os_epoll_wait( ioque->epfd, events, PJ_ARRAY_SIZE(events), msec); - if (count <= 0) - return count; - - /* Lock ioqueue. */ - pj_lock_acquire(ioque->lock); - - processed = 0; - - for (i=0; iop))) { - pj_ssize_t bytes_read = h->rd_buflen; - - if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) { - rc = pj_sock_recvfrom( h->fd, h->rd_buf, &bytes_read, 0, - h->rmt_addr, h->rmt_addrlen); - } else if ((h->op & PJ_IOQUEUE_OP_RECV)) { - rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0); - } else { - bytes_read = os_read( h->fd, h->rd_buf, bytes_read); - rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); - } - - if (rc != PJ_SUCCESS) { - bytes_read = -rc; - } - - h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV | - PJ_IOQUEUE_OP_RECV_FROM); - - /* Call callback. */ - (*h->cb.on_read_complete)(h, bytes_read); - - ++processed; - } - /* - * Check for completion of accept() operation. - */ - else if ((events[i].events & EPOLLIN) && - (h->op & PJ_IOQUEUE_OP_ACCEPT)) - { - /* accept() must be the only operation specified on - * server socket - */ - pj_assert( h->op == PJ_IOQUEUE_OP_ACCEPT); - - rc = pj_sock_accept( h->fd, h->accept_fd, - h->rmt_addr, h->rmt_addrlen); - if (rc==PJ_SUCCESS && h->local_addr) { - rc = pj_sock_getsockname(*h->accept_fd, h->local_addr, - h->local_addrlen); - } - - h->op &= ~(PJ_IOQUEUE_OP_ACCEPT); - - /* Call callback. */ - (*h->cb.on_accept_complete)(h, *h->accept_fd, rc); - - ++processed; - } - - /* - * Check for completion of write operations. - */ - if ((events[i].events & EPOLLOUT) && PJ_IOQUEUE_IS_WRITE_OP(h->op)) { - /* Completion of write(), send(), or sendto() operation. */ - - /* Clear operation. */ - h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND | - PJ_IOQUEUE_OP_SEND_TO); - - /* Call callback. */ - /* All data must have been sent? */ - (*h->cb.on_write_complete)(h, h->wr_buflen); - - ++processed; - } -#if PJ_HAS_TCP - /* - * Check for completion of connect() operation. - */ - else if ((events[i].events & EPOLLOUT) && - (h->op & PJ_IOQUEUE_OP_CONNECT)) - { - /* Completion of connect() operation */ - pj_ssize_t bytes_transfered; - - /* from connect(2): - * On Linux, use getsockopt to read the SO_ERROR option at - * level SOL_SOCKET to determine whether connect() completed - * successfully (if SO_ERROR is zero). - */ - int value; - socklen_t vallen = sizeof(value); - int gs_rc = os_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, - (getsockopt_val_ptr)&value, &vallen); - if (gs_rc != 0) { - /* Argh!! What to do now??? - * Just indicate that the socket is connected. The - * application will get error as soon as it tries to use - * the socket to send/receive. - */ - bytes_transfered = 0; - } else { - bytes_transfered = value; - } - - /* Clear operation. */ - h->op &= (~PJ_IOQUEUE_OP_CONNECT); - - /* Call callback. */ - (*h->cb.on_connect_complete)(h, bytes_transfered); - - ++processed; - } -#endif /* PJ_HAS_TCP */ - - /* - * Check for error condition. - */ - if (events[i].events & EPOLLERR) { - if (h->op & PJ_IOQUEUE_OP_CONNECT) { - h->op &= ~PJ_IOQUEUE_OP_CONNECT; - - /* Call callback. */ - (*h->cb.on_connect_complete)(h, -1); - - ++processed; - } - } - } - - pj_lock_release(ioque->lock); - - return processed; -} - -/* - * pj_ioqueue_read() - * - * Start asynchronous read from the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen) -{ - PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for reading before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), - PJ_EBUSY); - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_READ; - key->rd_flags = 0; - key->rd_buf = buffer; - key->rd_buflen = buflen; - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - - -/* - * pj_ioqueue_recv() - * - * Start asynchronous recv() from the socket. - */ -PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags ) -{ - PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for reading before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), - PJ_EBUSY); - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_RECV; - key->rd_buf = buffer; - key->rd_buflen = buflen; - key->rd_flags = flags; - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - -/* - * pj_ioqueue_recvfrom() - * - * Start asynchronous recvfrom() from the socket. - */ -PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags, - pj_sockaddr_t *addr, - int *addrlen) -{ - PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for reading before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), - PJ_EBUSY); - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_RECV_FROM; - key->rd_buf = buffer; - key->rd_buflen = buflen; - key->rd_flags = flags; - key->rmt_addr = addr; - key->rmt_addrlen = addrlen; - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - -/* - * pj_ioqueue_write() - * - * Start asynchronous write() to the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen) -{ - pj_status_t rc; - pj_ssize_t sent; - - PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for writing before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), - PJ_EBUSY); - - sent = datalen; - /* sent would be -1 after pj_sock_send() if it returns error. */ - rc = pj_sock_send(key->fd, data, &sent, 0); - if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return rc; - } - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_WRITE; - key->wr_buf = NULL; - key->wr_buflen = datalen; - - pj_lock_release(ioque->lock); - - return PJ_EPENDING; -} - -/* - * pj_ioqueue_send() - * - * Start asynchronous send() to the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags) -{ - pj_status_t rc; - pj_ssize_t sent; - - PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for writing before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), - PJ_EBUSY); - - sent = datalen; - /* sent would be -1 after pj_sock_send() if it returns error. */ - rc = pj_sock_send(key->fd, data, &sent, flags); - if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return rc; - } - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_SEND; - key->wr_buf = NULL; - key->wr_buflen = datalen; - - pj_lock_release(ioque->lock); - - return PJ_EPENDING; -} - - -/* - * pj_ioqueue_sendto() - * - * Start asynchronous write() to the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags, - const pj_sockaddr_t *addr, - int addrlen) -{ - pj_status_t rc; - pj_ssize_t sent; - - PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for writing before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), - PJ_EBUSY); - - sent = datalen; - /* sent would be -1 after pj_sock_sendto() if it returns error. */ - rc = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); - if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return rc; - } - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_SEND_TO; - key->wr_buf = NULL; - key->wr_buflen = datalen; - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - -#if PJ_HAS_TCP -/* - * Initiate overlapped accept() operation. - */ -PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - pj_sock_t *new_sock, - pj_sockaddr_t *local, - pj_sockaddr_t *remote, - int *addrlen) -{ - /* check parameters. All must be specified! */ - pj_assert(ioqueue && key && new_sock); - - /* Server socket must have no other operation! */ - pj_assert(key->op == 0); - - pj_lock_acquire(ioqueue->lock); - - key->op = PJ_IOQUEUE_OP_ACCEPT; - key->accept_fd = new_sock; - key->rmt_addr = remote; - key->rmt_addrlen = addrlen; - key->local_addr = local; - key->local_addrlen = addrlen; /* use same addr. as rmt_addrlen */ - - pj_lock_release(ioqueue->lock); - return PJ_EPENDING; -} - -/* - * Initiate overlapped connect() operation (well, it's non-blocking actually, - * since there's no overlapped version of connect()). - */ -PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - const pj_sockaddr_t *addr, - int addrlen ) -{ - pj_status_t rc; - - /* check parameters. All must be specified! */ - PJ_ASSERT_RETURN(ioqueue && key && addr && addrlen, PJ_EINVAL); - - /* Connecting socket must have no other operation! */ - PJ_ASSERT_RETURN(key->op == 0, PJ_EBUSY); - - rc = pj_sock_connect(key->fd, addr, addrlen); - if (rc == PJ_SUCCESS) { - /* Connected! */ - return PJ_SUCCESS; - } else { - if (rc == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) || - rc == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) - { - /* Pending! */ - pj_lock_acquire(ioqueue->lock); - key->op = PJ_IOQUEUE_OP_CONNECT; - pj_lock_release(ioqueue->lock); - return PJ_EPENDING; - } else { - /* Error! */ - return rc; - } - } -} -#endif /* PJ_HAS_TCP */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_epoll.c 4 10/29/05 10:27p Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_epoll.c $ + * + * 4 10/29/05 10:27p Bennylp + * Fixed misc warnings. + * + * 3 10/29/05 11:49a Bennylp + * Fixed warnings. + * + * 2 10/29/05 11:31a Bennylp + * Changed accept and lock. + * + * 1 10/17/05 10:49p Bennylp + * Created. + * + */ + +/* + * ioqueue_epoll.c + * + * This is the implementation of IOQueue framework using /dev/epoll + * API in _both_ Linux user-mode and kernel-mode. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0 + /* + * Linux user mode + */ +# include +# include +# include + +# define epoll_data data.ptr +# define epoll_data_type void* +# define ioctl_val_type unsigned long* +# define getsockopt_val_ptr int* +# define os_getsockopt getsockopt +# define os_ioctl ioctl +# define os_read read +# define os_close close +# define os_epoll_create epoll_create +# define os_epoll_ctl epoll_ctl +# define os_epoll_wait epoll_wait +#else + /* + * Linux kernel mode. + */ +# include +# include +# if defined(MODVERSIONS) +# include +# endif +# include +# include +# include +# include +# include +# include +# include + enum EPOLL_EVENTS + { + EPOLLIN = 0x001, + EPOLLOUT = 0x004, + EPOLLERR = 0x008, + }; +# define os_epoll_create sys_epoll_create + static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) + { + long rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = sys_epoll_ctl(epfd, op, fd, event); + set_fs(oldfs); + if (rc) { + errno = -rc; + return -1; + } else { + return 0; + } + } + static int os_epoll_wait(int epfd, struct epoll_event *events, + int maxevents, int timeout) + { + int count; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + count = sys_epoll_wait(epfd, events, maxevents, timeout); + set_fs(oldfs); + return count; + } +# define os_close sys_close +# define os_getsockopt pj_sock_getsockopt + static int os_read(int fd, void *buf, size_t len) + { + long rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = sys_read(fd, buf, len); + set_fs(oldfs); + if (rc) { + errno = -rc; + return -1; + } else { + return 0; + } + } +# define socklen_t unsigned +# define ioctl_val_type unsigned long + int ioctl(int fd, int opt, ioctl_val_type value); + static int os_ioctl(int fd, int opt, ioctl_val_type value) + { + int rc; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + rc = ioctl(fd, opt, value); + set_fs(oldfs); + if (rc < 0) { + errno = -rc; + return rc; + } else + return rc; + } +# define getsockopt_val_ptr char* + +# define epoll_data data +# define epoll_data_type __u32 +#endif + +#define THIS_FILE "ioq_epoll" + +#define PJ_IOQUEUE_IS_READ_OP(op) ((op & PJ_IOQUEUE_OP_READ) || \ + (op & PJ_IOQUEUE_OP_RECV) || \ + (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) ((op & PJ_IOQUEUE_OP_WRITE) || \ + (op & PJ_IOQUEUE_OP_SEND) || \ + (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + + +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(expr) + + +/* + * This describes each key. + */ +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; + + void *rd_buf; + unsigned rd_flags; + pj_size_t rd_buflen; + void *wr_buf; + pj_size_t wr_buflen; + + pj_sockaddr_t *rmt_addr; + int *rmt_addrlen; + + pj_sockaddr_t *local_addr; + int *local_addrlen; + + pj_sock_t *accept_fd; +}; + +/* + * This describes the I/O queue. + */ +struct pj_ioqueue_t +{ + pj_lock_t *lock; + pj_bool_t auto_delete_lock; + unsigned max, count; + pj_ioqueue_key_t hlist; + int epfd; +}; + +/* + * pj_ioqueue_create() + * + * Create select ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + int max_threads, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioque; + pj_status_t rc; + + PJ_UNUSED_ARG(max_threads); + + if (max_fd > PJ_IOQUEUE_MAX_HANDLES) { + pj_assert(!"max_fd too large"); + return PJ_EINVAL; + } + + ioque = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); + ioque->max = max_fd; + ioque->count = 0; + pj_list_init(&ioque->hlist); + + rc = pj_lock_create_recursive_mutex(pool, "ioq%p", &ioque->lock); + if (rc != PJ_SUCCESS) + return rc; + + ioque->auto_delete_lock = PJ_TRUE; + ioque->epfd = os_epoll_create(max_fd); + if (ioque->epfd < 0) { + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + } + + PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioque)); + + *p_ioqueue = ioque; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + * + * Destroy ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + PJ_ASSERT_RETURN(ioque, PJ_EINVAL); + PJ_ASSERT_RETURN(ioque->epfd > 0, PJ_EINVALIDOP); + + pj_lock_acquire(ioque->lock); + os_close(ioque->epfd); + ioque->epfd = 0; + if (ioque->auto_delete_lock) + pj_lock_destroy(ioque->lock); + + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_set_lock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL); + + if (ioque->auto_delete_lock) { + pj_lock_destroy(ioque->lock); + } + + ioque->lock = lock; + ioque->auto_delete_lock = auto_delete; + + return PJ_SUCCESS; +} + + +/* + * pj_ioqueue_register_sock() + * + * Register a socket to ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key) +{ + pj_ioqueue_key_t *key = NULL; + pj_uint32_t value; + struct epoll_event ev; + int status; + pj_status_t rc = PJ_SUCCESS; + + PJ_ASSERT_RETURN(pool && ioque && sock != PJ_INVALID_SOCKET && + cb && p_key, PJ_EINVAL); + + pj_lock_acquire(ioque->lock); + + if (ioque->count >= ioque->max) { + rc = PJ_ETOOMANY; + TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files")); + goto on_return; + } + + /* Set socket to nonblocking. */ + value = 1; + if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) { + TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d", + rc)); + rc = pj_get_netos_error(); + goto on_return; + } + + /* Create key. */ + key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); + key->fd = sock; + key->user_data = user_data; + pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); + + /* os_epoll_ctl. */ + ev.events = EPOLLIN | EPOLLOUT | EPOLLERR; + ev.epoll_data = (epoll_data_type)key; + status = os_epoll_ctl(ioque->epfd, EPOLL_CTL_ADD, sock, &ev); + if (status < 0) { + rc = pj_get_os_error(); + TRACE_((THIS_FILE, + "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d", + status)); + goto on_return; + } + + /* Register */ + pj_list_insert_before(&ioque->hlist, key); + ++ioque->count; + +on_return: + *p_key = key; + pj_lock_release(ioque->lock); + + return rc; +} + +/* + * pj_ioqueue_unregister() + * + * Unregister handle from ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + struct epoll_event ev; + int status; + + PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL); + + pj_lock_acquire(ioque->lock); + + pj_assert(ioque->count > 0); + --ioque->count; + pj_list_erase(key); + + ev.events = 0; + ev.epoll_data = (epoll_data_type)key; + status = os_epoll_ctl( ioque->epfd, EPOLL_CTL_DEL, key->fd, &ev); + if (status != 0) { + pj_status_t rc = pj_get_os_error(); + pj_lock_release(ioque->lock); + return rc; + } + + pj_lock_release(ioque->lock); + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_get_user_data() + * + * Obtain value associated with a key. + */ +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key != NULL, NULL); + return key->user_data; +} + + +/* + * pj_ioqueue_poll() + * + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + int i, count, processed; + struct epoll_event events[16]; + int msec; + + PJ_CHECK_STACK(); + + msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000; + + count = os_epoll_wait( ioque->epfd, events, PJ_ARRAY_SIZE(events), msec); + if (count <= 0) + return count; + + /* Lock ioqueue. */ + pj_lock_acquire(ioque->lock); + + processed = 0; + + for (i=0; iop))) { + pj_ssize_t bytes_read = h->rd_buflen; + + if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) { + rc = pj_sock_recvfrom( h->fd, h->rd_buf, &bytes_read, 0, + h->rmt_addr, h->rmt_addrlen); + } else if ((h->op & PJ_IOQUEUE_OP_RECV)) { + rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0); + } else { + bytes_read = os_read( h->fd, h->rd_buf, bytes_read); + rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); + } + + if (rc != PJ_SUCCESS) { + bytes_read = -rc; + } + + h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV | + PJ_IOQUEUE_OP_RECV_FROM); + + /* Call callback. */ + (*h->cb.on_read_complete)(h, bytes_read); + + ++processed; + } + /* + * Check for completion of accept() operation. + */ + else if ((events[i].events & EPOLLIN) && + (h->op & PJ_IOQUEUE_OP_ACCEPT)) + { + /* accept() must be the only operation specified on + * server socket + */ + pj_assert( h->op == PJ_IOQUEUE_OP_ACCEPT); + + rc = pj_sock_accept( h->fd, h->accept_fd, + h->rmt_addr, h->rmt_addrlen); + if (rc==PJ_SUCCESS && h->local_addr) { + rc = pj_sock_getsockname(*h->accept_fd, h->local_addr, + h->local_addrlen); + } + + h->op &= ~(PJ_IOQUEUE_OP_ACCEPT); + + /* Call callback. */ + (*h->cb.on_accept_complete)(h, *h->accept_fd, rc); + + ++processed; + } + + /* + * Check for completion of write operations. + */ + if ((events[i].events & EPOLLOUT) && PJ_IOQUEUE_IS_WRITE_OP(h->op)) { + /* Completion of write(), send(), or sendto() operation. */ + + /* Clear operation. */ + h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND | + PJ_IOQUEUE_OP_SEND_TO); + + /* Call callback. */ + /* All data must have been sent? */ + (*h->cb.on_write_complete)(h, h->wr_buflen); + + ++processed; + } +#if PJ_HAS_TCP + /* + * Check for completion of connect() operation. + */ + else if ((events[i].events & EPOLLOUT) && + (h->op & PJ_IOQUEUE_OP_CONNECT)) + { + /* Completion of connect() operation */ + pj_ssize_t bytes_transfered; + + /* from connect(2): + * On Linux, use getsockopt to read the SO_ERROR option at + * level SOL_SOCKET to determine whether connect() completed + * successfully (if SO_ERROR is zero). + */ + int value; + socklen_t vallen = sizeof(value); + int gs_rc = os_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, + (getsockopt_val_ptr)&value, &vallen); + if (gs_rc != 0) { + /* Argh!! What to do now??? + * Just indicate that the socket is connected. The + * application will get error as soon as it tries to use + * the socket to send/receive. + */ + bytes_transfered = 0; + } else { + bytes_transfered = value; + } + + /* Clear operation. */ + h->op &= (~PJ_IOQUEUE_OP_CONNECT); + + /* Call callback. */ + (*h->cb.on_connect_complete)(h, bytes_transfered); + + ++processed; + } +#endif /* PJ_HAS_TCP */ + + /* + * Check for error condition. + */ + if (events[i].events & EPOLLERR) { + if (h->op & PJ_IOQUEUE_OP_CONNECT) { + h->op &= ~PJ_IOQUEUE_OP_CONNECT; + + /* Call callback. */ + (*h->cb.on_connect_complete)(h, -1); + + ++processed; + } + } + } + + pj_lock_release(ioque->lock); + + return processed; +} + +/* + * pj_ioqueue_read() + * + * Start asynchronous read from the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for reading before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), + PJ_EBUSY); + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_READ; + key->rd_flags = 0; + key->rd_buf = buffer; + key->rd_buflen = buflen; + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_recv() + * + * Start asynchronous recv() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags ) +{ + PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for reading before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), + PJ_EBUSY); + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_RECV; + key->rd_buf = buffer; + key->rd_buflen = buflen; + key->rd_flags = flags; + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + +/* + * pj_ioqueue_recvfrom() + * + * Start asynchronous recvfrom() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for reading before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), + PJ_EBUSY); + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_RECV_FROM; + key->rd_buf = buffer; + key->rd_buflen = buflen; + key->rd_flags = flags; + key->rmt_addr = addr; + key->rmt_addrlen = addrlen; + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + +/* + * pj_ioqueue_write() + * + * Start asynchronous write() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + pj_status_t rc; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for writing before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), + PJ_EBUSY); + + sent = datalen; + /* sent would be -1 after pj_sock_send() if it returns error. */ + rc = pj_sock_send(key->fd, data, &sent, 0); + if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return rc; + } + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_WRITE; + key->wr_buf = NULL; + key->wr_buflen = datalen; + + pj_lock_release(ioque->lock); + + return PJ_EPENDING; +} + +/* + * pj_ioqueue_send() + * + * Start asynchronous send() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags) +{ + pj_status_t rc; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for writing before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), + PJ_EBUSY); + + sent = datalen; + /* sent would be -1 after pj_sock_send() if it returns error. */ + rc = pj_sock_send(key->fd, data, &sent, flags); + if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return rc; + } + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_SEND; + key->wr_buf = NULL; + key->wr_buflen = datalen; + + pj_lock_release(ioque->lock); + + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_sendto() + * + * Start asynchronous write() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + pj_status_t rc; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for writing before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), + PJ_EBUSY); + + sent = datalen; + /* sent would be -1 after pj_sock_sendto() if it returns error. */ + rc = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); + if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return rc; + } + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_SEND_TO; + key->wr_buf = NULL; + key->wr_buflen = datalen; + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + /* check parameters. All must be specified! */ + pj_assert(ioqueue && key && new_sock); + + /* Server socket must have no other operation! */ + pj_assert(key->op == 0); + + pj_lock_acquire(ioqueue->lock); + + key->op = PJ_IOQUEUE_OP_ACCEPT; + key->accept_fd = new_sock; + key->rmt_addr = remote; + key->rmt_addrlen = addrlen; + key->local_addr = local; + key->local_addrlen = addrlen; /* use same addr. as rmt_addrlen */ + + pj_lock_release(ioqueue->lock); + return PJ_EPENDING; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + pj_status_t rc; + + /* check parameters. All must be specified! */ + PJ_ASSERT_RETURN(ioqueue && key && addr && addrlen, PJ_EINVAL); + + /* Connecting socket must have no other operation! */ + PJ_ASSERT_RETURN(key->op == 0, PJ_EBUSY); + + rc = pj_sock_connect(key->fd, addr, addrlen); + if (rc == PJ_SUCCESS) { + /* Connected! */ + return PJ_SUCCESS; + } else { + if (rc == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) || + rc == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) + { + /* Pending! */ + pj_lock_acquire(ioqueue->lock); + key->op = PJ_IOQUEUE_OP_CONNECT; + pj_lock_release(ioqueue->lock); + return PJ_EPENDING; + } else { + /* Error! */ + return rc; + } + } +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_linux_kernel.c b/pjlib/src/pj/ioqueue_linux_kernel.c index b83381188..debf2cbde 100644 --- a/pjlib/src/pj/ioqueue_linux_kernel.c +++ b/pjlib/src/pj/ioqueue_linux_kernel.c @@ -1,150 +1,150 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_linux_kernel.c 1 10/05/05 4:42p Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_linux_kernel.c $ - * - * 1 10/05/05 4:42p Bennylp - * Created. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define THIS_FILE "ioqueue" - -#define PJ_IOQUEUE_IS_READ_OP(op) \ - ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) -#define PJ_IOQUEUE_IS_WRITE_OP(op) \ - ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) - - -#if PJ_HAS_TCP -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) -# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) -#else -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 -# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 -#endif - -#if defined(PJ_DEBUG) && PJ_DEBUG != 0 -# define VALIDATE_FD_SET 1 -#else -# define VALIDATE_FD_SET 0 -#endif - -struct pj_ioqueue_key_t -{ - PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) - pj_sock_t fd; - pj_ioqueue_operation_e op; - void *user_data; - pj_ioqueue_callback cb; -}; - -struct pj_ioqueue_t -{ -}; - -PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd) -{ - return NULL; -} - -PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) -{ - return 0; -} - -PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool, - pj_ioqueue_t *ioque, - pj_oshandle_t sock, - void *user_data, - const pj_ioqueue_callback *cb) -{ - return NULL; -} - -PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key) -{ - return -1; -} - -PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) -{ - return NULL; -} - - -PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) -{ - return -1; -} - -PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen) -{ - return -1; -} - -PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - pj_sockaddr_t *addr, - int *addrlen) -{ - return -1; -} - -PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen) -{ - return -1; -} - -PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - const pj_sockaddr_t *addr, - int addrlen) -{ - return -1; -} - -#if PJ_HAS_TCP -/* - * Initiate overlapped accept() operation. - */ -PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - pj_sock_t *new_sock, - pj_sockaddr_t *local, - pj_sockaddr_t *remote, - int *addrlen) -{ - return -1; -} - -/* - * Initiate overlapped connect() operation (well, it's non-blocking actually, - * since there's no overlapped version of connect()). - */ -PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - const pj_sockaddr_t *addr, - int addrlen ) -{ - return -1; -} -#endif /* PJ_HAS_TCP */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_linux_kernel.c 1 10/05/05 4:42p Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_linux_kernel.c $ + * + * 1 10/05/05 4:42p Bennylp + * Created. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "ioqueue" + +#define PJ_IOQUEUE_IS_READ_OP(op) \ + ((op & PJ_IOQUEUE_OP_READ) || (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) \ + ((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif + +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; +}; + +struct pj_ioqueue_t +{ +}; + +PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd) +{ + return NULL; +} + +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + return 0; +} + +PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_oshandle_t sock, + void *user_data, + const pj_ioqueue_callback *cb) +{ + return NULL; +} + +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + return -1; +} + +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + return NULL; +} + + +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + pj_sockaddr_t *addr, + int *addrlen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + return -1; +} + +PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + const pj_sockaddr_t *addr, + int addrlen) +{ + return -1; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + return -1; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + return -1; +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_select.c b/pjlib/src/pj/ioqueue_select.c index 615c758e2..a16aba2d3 100644 --- a/pjlib/src/pj/ioqueue_select.c +++ b/pjlib/src/pj/ioqueue_select.c @@ -1,947 +1,964 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_select.c 15 10/29/05 10:27p Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_select.c $ - * - * 15 10/29/05 10:27p Bennylp - * Fixed misc warnings. - * - * 14 10/29/05 11:31a Bennylp - * Changed accept and lock. - * - * 13 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 12 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 11 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ - -/* - * sock_select.c - * - * This is the implementation of IOQueue using pj_sock_select(). - * It runs anywhere where pj_sock_select() is available (currently - * Win32, Linux, Linux kernel, etc.). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * ISSUES with ioqueue_select() - * - * EAGAIN/EWOULDBLOCK error in recv(): - * - when multiple threads are working with the ioqueue, application - * may receive EAGAIN or EWOULDBLOCK in the receive callback. - * This error happens because more than one thread is watching for - * the same descriptor set, so when all of them call recv() or recvfrom() - * simultaneously, only one will succeed and the rest will get the error. - * - */ -#define THIS_FILE "ioq_select" - -#define PJ_IOQUEUE_IS_READ_OP(op) ((op & PJ_IOQUEUE_OP_READ) || \ - (op & PJ_IOQUEUE_OP_RECV) || \ - (op & PJ_IOQUEUE_OP_RECV_FROM)) -#define PJ_IOQUEUE_IS_WRITE_OP(op) ((op & PJ_IOQUEUE_OP_WRITE) || \ - (op & PJ_IOQUEUE_OP_SEND) || \ - (op & PJ_IOQUEUE_OP_SEND_TO)) - - -#if PJ_HAS_TCP -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) -# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) -#else -# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 -# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 -#endif - -/* - * During debugging build, VALIDATE_FD_SET is set. - * This will check the validity of the fd_sets. - */ -#if defined(PJ_DEBUG) && PJ_DEBUG != 0 -# define VALIDATE_FD_SET 1 -#else -# define VALIDATE_FD_SET 0 -#endif - -/* - * This describes each key. - */ -struct pj_ioqueue_key_t -{ - PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) - pj_sock_t fd; - pj_ioqueue_operation_e op; - void *user_data; - pj_ioqueue_callback cb; - - void *rd_buf; - unsigned rd_flags; - pj_size_t rd_buflen; - void *wr_buf; - pj_size_t wr_buflen; - - pj_sockaddr_t *rmt_addr; - int *rmt_addrlen; - - pj_sockaddr_t *local_addr; - int *local_addrlen; - - pj_sock_t *accept_fd; -}; - -/* - * This describes the I/O queue itself. - */ -struct pj_ioqueue_t -{ - pj_lock_t *lock; - pj_bool_t auto_delete_lock; - unsigned max, count; - pj_ioqueue_key_t hlist; - pj_fd_set_t rfdset; - pj_fd_set_t wfdset; -#if PJ_HAS_TCP - pj_fd_set_t xfdset; -#endif -}; - -/* - * pj_ioqueue_create() - * - * Create select ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, - pj_size_t max_fd, - int max_threads, - pj_ioqueue_t **p_ioqueue) -{ - pj_ioqueue_t *ioque; - pj_status_t rc; - - PJ_UNUSED_ARG(max_threads); - - if (max_fd > PJ_IOQUEUE_MAX_HANDLES) { - pj_assert(!"max_fd too large"); - return PJ_EINVAL; - } - - ioque = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); - ioque->max = max_fd; - ioque->count = 0; - PJ_FD_ZERO(&ioque->rfdset); - PJ_FD_ZERO(&ioque->wfdset); -#if PJ_HAS_TCP - PJ_FD_ZERO(&ioque->xfdset); -#endif - pj_list_init(&ioque->hlist); - - rc = pj_lock_create_recursive_mutex(pool, "ioq%p", &ioque->lock); - if (rc != PJ_SUCCESS) - return rc; - - ioque->auto_delete_lock = PJ_TRUE; - - PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioque)); - - *p_ioqueue = ioque; - return PJ_SUCCESS; -} - -/* - * pj_ioqueue_destroy() - * - * Destroy ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) -{ - pj_status_t rc = PJ_SUCCESS; - - PJ_ASSERT_RETURN(ioque, PJ_EINVAL); - - if (ioque->auto_delete_lock) - rc = pj_lock_destroy(ioque->lock); - - return rc; -} - - -/* - * pj_ioqueue_set_lock() - */ -PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, - pj_lock_t *lock, - pj_bool_t auto_delete ) -{ - PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL); - - if (ioque->auto_delete_lock) { - pj_lock_destroy(ioque->lock); - } - - ioque->lock = lock; - ioque->auto_delete_lock = auto_delete; - - return PJ_SUCCESS; -} - - -/* - * pj_ioqueue_register_sock() - * - * Register a handle to ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, - pj_ioqueue_t *ioque, - pj_sock_t sock, - void *user_data, - const pj_ioqueue_callback *cb, - pj_ioqueue_key_t **p_key) -{ - pj_ioqueue_key_t *key = NULL; - pj_uint32_t value; - pj_status_t rc = PJ_SUCCESS; - - PJ_ASSERT_RETURN(pool && ioque && sock != PJ_INVALID_SOCKET && - cb && p_key, PJ_EINVAL); - - pj_lock_acquire(ioque->lock); - - if (ioque->count >= ioque->max) { - rc = PJ_ETOOMANY; - goto on_return; - } - - /* Set socket to nonblocking. */ - value = 1; -#ifdef PJ_WIN32 - if (ioctlsocket(sock, FIONBIO, (unsigned long*)&value)) { -#else - if (ioctl(sock, FIONBIO, &value)) { -#endif - rc = pj_get_netos_error(); - goto on_return; - } - - /* Create key. */ - key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); - key->fd = sock; - key->user_data = user_data; - - /* Save callback. */ - pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); - - /* Register */ - pj_list_insert_before(&ioque->hlist, key); - ++ioque->count; - -on_return: - *p_key = key; - pj_lock_release(ioque->lock); - - return rc; -} - -/* - * pj_ioqueue_unregister() - * - * Unregister handle from ioqueue. - */ -PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key) -{ - PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL); - - pj_lock_acquire(ioque->lock); - - pj_assert(ioque->count > 0); - --ioque->count; - pj_list_erase(key); - PJ_FD_CLR(key->fd, &ioque->rfdset); - PJ_FD_CLR(key->fd, &ioque->wfdset); -#if PJ_HAS_TCP - PJ_FD_CLR(key->fd, &ioque->xfdset); -#endif - - pj_lock_release(ioque->lock); - return PJ_SUCCESS; -} - -/* - * pj_ioqueue_get_user_data() - * - * Obtain value associated with a key. - */ -PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) -{ - PJ_ASSERT_RETURN(key != NULL, NULL); - return key->user_data; -} - - -/* This supposed to check whether the fd_set values are consistent - * with the operation currently set in each key. - */ -#if VALIDATE_FD_SET -static void validate_sets(const pj_ioqueue_t *ioque, - const pj_fd_set_t *rfdset, - const pj_fd_set_t *wfdset, - const pj_fd_set_t *xfdset) -{ - pj_ioqueue_key_t *key; - - key = ioque->hlist.next; - while (key != &ioque->hlist) { - if ((key->op & PJ_IOQUEUE_OP_READ) - || (key->op & PJ_IOQUEUE_OP_RECV) - || (key->op & PJ_IOQUEUE_OP_RECV_FROM) -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 - || (key->op & PJ_IOQUEUE_OP_ACCEPT) -#endif - ) - { - pj_assert(PJ_FD_ISSET(key->fd, rfdset)); - } - else { - pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0); - } - if ((key->op & PJ_IOQUEUE_OP_WRITE) - || (key->op & PJ_IOQUEUE_OP_SEND) - || (key->op & PJ_IOQUEUE_OP_SEND_TO) -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 - || (key->op & PJ_IOQUEUE_OP_CONNECT) -#endif - ) - { - pj_assert(PJ_FD_ISSET(key->fd, wfdset)); - } - else { - pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0); - } -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 - if (key->op & PJ_IOQUEUE_OP_CONNECT) - { - pj_assert(PJ_FD_ISSET(key->fd, xfdset)); - } - else { - pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0); - } -#endif /* PJ_HAS_TCP */ - - key = key->next; - } -} -#endif /* VALIDATE_FD_SET */ - - -/* - * pj_ioqueue_poll() - * - * Few things worth written: - * - * - we used to do only one callback called per poll, but it didn't go - * very well. The reason is because on some situation, the write - * callback gets called all the time, thus doesn't give the read - * callback to get called. This happens, for example, when user - * submit write operation inside the write callback. - * As the result, we changed the behaviour so that now multiple - * callbacks are called in a single poll. It should be fast too, - * just that we need to be carefull with the ioqueue data structs. - * - * - to guarantee preemptiveness etc, the poll function must strictly - * work on fd_set copy of the ioqueue (not the original one). - */ -PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) -{ - pj_fd_set_t rfdset, wfdset, xfdset; - int count; - pj_ioqueue_key_t *h; - - /* Lock ioqueue before making fd_set copies */ - pj_lock_acquire(ioque->lock); - - if (PJ_FD_COUNT(&ioque->rfdset)==0 && - PJ_FD_COUNT(&ioque->wfdset)==0 && - PJ_FD_COUNT(&ioque->xfdset)==0) - { - pj_lock_release(ioque->lock); - if (timeout) - pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout)); - return 0; - } - - /* Copy ioqueue's pj_fd_set_t to local variables. */ - pj_memcpy(&rfdset, &ioque->rfdset, sizeof(pj_fd_set_t)); - pj_memcpy(&wfdset, &ioque->wfdset, sizeof(pj_fd_set_t)); -#if PJ_HAS_TCP - pj_memcpy(&xfdset, &ioque->xfdset, sizeof(pj_fd_set_t)); -#else - PJ_FD_ZERO(&xfdset); -#endif - -#if VALIDATE_FD_SET - validate_sets(ioque, &rfdset, &wfdset, &xfdset); -#endif - - /* Unlock ioqueue before select(). */ - pj_lock_release(ioque->lock); - - count = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout); - - if (count <= 0) - return count; - - /* Lock ioqueue again before scanning for signalled sockets. */ - pj_lock_acquire(ioque->lock); - -#if PJ_HAS_TCP - /* Scan for exception socket */ - h = ioque->hlist.next; -do_except_scan: - for ( ; h!=&ioque->hlist; h = h->next) { - if ((h->op & PJ_IOQUEUE_OP_CONNECT) && PJ_FD_ISSET(h->fd, &xfdset)) - break; - } - if (h != &ioque->hlist) { - /* 'connect()' should be the only operation. */ - pj_assert((h->op == PJ_IOQUEUE_OP_CONNECT)); - - /* Clear operation. */ - h->op &= ~(PJ_IOQUEUE_OP_CONNECT); - PJ_FD_CLR(h->fd, &ioque->wfdset); - PJ_FD_CLR(h->fd, &ioque->xfdset); - PJ_FD_CLR(h->fd, &wfdset); - PJ_FD_CLR(h->fd, &xfdset); - - /* Call callback. */ - if (h->cb.on_connect_complete) - (*h->cb.on_connect_complete)(h, -1); - - /* Re-scan exception list. */ - goto do_except_scan; - } -#endif /* PJ_HAS_TCP */ - - /* Scan for readable socket. */ - h = ioque->hlist.next; -do_readable_scan: - for ( ; h!=&ioque->hlist; h = h->next) { - if ((PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op)) && - PJ_FD_ISSET(h->fd, &rfdset)) - { - break; - } - } - if (h != &ioque->hlist) { - pj_status_t rc; - - pj_assert(PJ_IOQUEUE_IS_READ_OP(h->op) || - PJ_IOQUEUE_IS_ACCEPT_OP(h->op)); - -# if PJ_HAS_TCP - if ((h->op & PJ_IOQUEUE_OP_ACCEPT)) { - /* accept() must be the only operation specified on server socket */ - pj_assert(h->op == PJ_IOQUEUE_OP_ACCEPT); - - rc=pj_sock_accept(h->fd, h->accept_fd, h->rmt_addr, h->rmt_addrlen); - if (rc==0 && h->local_addr) { - rc = pj_sock_getsockname(*h->accept_fd, h->local_addr, - h->local_addrlen); - } - - h->op &= ~(PJ_IOQUEUE_OP_ACCEPT); - PJ_FD_CLR(h->fd, &ioque->rfdset); - - /* Call callback. */ - if (h->cb.on_accept_complete) - (*h->cb.on_accept_complete)(h, *h->accept_fd, rc); - - /* Re-scan readable sockets. */ - goto do_readable_scan; - } - else { -# endif - pj_ssize_t bytes_read = h->rd_buflen; - - if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) { - rc = pj_sock_recvfrom(h->fd, h->rd_buf, &bytes_read, 0, - h->rmt_addr, h->rmt_addrlen); - } else if ((h->op & PJ_IOQUEUE_OP_RECV)) { - rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0); - } else { - /* - * User has specified pj_ioqueue_read(). - * On Win32, we should do ReadFile(). But because we got - * here because of select() anyway, user must have put a - * socket descriptor on h->fd, which in this case we can - * just call pj_sock_recv() instead of ReadFile(). - * On Unix, user may put a file in h->fd, so we'll have - * to call read() here. - * This may not compile on systems which doesn't have - * read(). That's why we only specify PJ_LINUX here so - * that error is easier to catch. - */ -# if defined(PJ_WIN32) && PJ_WIN32 != 0 - rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0); -# elif defined(PJ_LINUX) && PJ_LINUX != 0 - bytes_read = read(h->fd, h->rd_buf, bytes_read); - rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); -# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 - bytes_read = sys_read(h->fd, h->rd_buf, bytes_read); - rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read; -# else -# error "Check this man!" -# endif - } - - if (rc != PJ_SUCCESS) { -# if defined(PJ_WIN32) && PJ_WIN32 != 0 - /* On Win32, for UDP, WSAECONNRESET on the receive side - * indicates that previous sending has triggered ICMP Port - * Unreachable message. - * But we wouldn't know at this point which one of previous - * key that has triggered the error, since UDP socket can - * be shared! - * So we'll just ignore it! - */ - - if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) { - PJ_LOG(4,(THIS_FILE, - "Ignored ICMP port unreach. on key=%p", h)); - } -# endif - - /* In any case we would report this to caller. */ - bytes_read = -rc; - } - - h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV | - PJ_IOQUEUE_OP_RECV_FROM); - PJ_FD_CLR(h->fd, &ioque->rfdset); - PJ_FD_CLR(h->fd, &rfdset); - - /* Call callback. */ - if (h->cb.on_read_complete) - (*h->cb.on_read_complete)(h, bytes_read); - - /* Re-scan readable sockets. */ - goto do_readable_scan; - - } - } - - /* Scan for writable socket */ - h = ioque->hlist.next; -do_writable_scan: - for ( ; h!=&ioque->hlist; h = h->next) { - if ((PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op)) - && PJ_FD_ISSET(h->fd, &wfdset)) - { - break; - } - } - if (h != &ioque->hlist) { - pj_assert(PJ_IOQUEUE_IS_WRITE_OP(h->op) || - PJ_IOQUEUE_IS_CONNECT_OP(h->op)); - -#if PJ_HAS_TCP - if ((h->op & PJ_IOQUEUE_OP_CONNECT)) { - /* Completion of connect() operation */ - pj_ssize_t bytes_transfered; - -#if defined(PJ_LINUX) || defined(PJ_LINUX_KERNEL) - /* from connect(2): - * On Linux, use getsockopt to read the SO_ERROR option at - * level SOL_SOCKET to determine whether connect() completed - * successfully (if SO_ERROR is zero). - */ - int value; - socklen_t vallen = sizeof(value); - int gs_rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, - &value, &vallen); - if (gs_rc != 0) { - /* Argh!! What to do now??? - * Just indicate that the socket is connected. The - * application will get error as soon as it tries to use - * the socket to send/receive. - */ - bytes_transfered = 0; - } else { - bytes_transfered = value; - } -#elif defined(PJ_WIN32) - bytes_transfered = 0; /* success */ -#else -# error "Got to check this one!" -#endif - - /* Clear operation. */ - h->op &= (~PJ_IOQUEUE_OP_CONNECT); - PJ_FD_CLR(h->fd, &ioque->wfdset); - PJ_FD_CLR(h->fd, &ioque->xfdset); - - /* Call callback. */ - if (h->cb.on_connect_complete) - (*h->cb.on_connect_complete)(h, bytes_transfered); - - /* Re-scan writable sockets. */ - goto do_writable_scan; - - } else -#endif /* PJ_HAS_TCP */ - { - /* Completion of write(), send(), or sendto() operation. */ - - /* Clear operation. */ - h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND | - PJ_IOQUEUE_OP_SEND_TO); - PJ_FD_CLR(h->fd, &ioque->wfdset); - PJ_FD_CLR(h->fd, &wfdset); - - /* Call callback. */ - /* All data must have been sent? */ - if (h->cb.on_write_complete) - (*h->cb.on_write_complete)(h, h->wr_buflen); - - /* Re-scan writable sockets. */ - goto do_writable_scan; - } - } - - /* Shouldn't happen. */ - /* For strange reason on WinXP select() can return 1 while there is no - * pj_fd_set_t signaled. */ - /* pj_assert(0); */ - - //count = 0; - - pj_lock_release(ioque->lock); - return count; -} - -/* - * pj_ioqueue_read() - * - * Start asynchronous read from the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen) -{ - PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for reading before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), - PJ_EBUSY); - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_READ; - key->rd_flags = 0; - key->rd_buf = buffer; - key->rd_buflen = buflen; - PJ_FD_SET(key->fd, &ioque->rfdset); - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - - -/* - * pj_ioqueue_recv() - * - * Start asynchronous recv() from the socket. - */ -PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags ) -{ - PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for reading before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), - PJ_EBUSY); - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_RECV; - key->rd_buf = buffer; - key->rd_buflen = buflen; - key->rd_flags = flags; - PJ_FD_SET(key->fd, &ioque->rfdset); - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - -/* - * pj_ioqueue_recvfrom() - * - * Start asynchronous recvfrom() from the socket. - */ -PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags, - pj_sockaddr_t *addr, - int *addrlen) -{ - PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for reading before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV) == 0 && - (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), - PJ_EBUSY); - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_RECV_FROM; - key->rd_buf = buffer; - key->rd_buflen = buflen; - key->rd_flags = flags; - key->rmt_addr = addr; - key->rmt_addrlen = addrlen; - PJ_FD_SET(key->fd, &ioque->rfdset); - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - -/* - * pj_ioqueue_write() - * - * Start asynchronous write() to the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen) -{ - pj_status_t rc; - pj_ssize_t sent; - - PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for writing before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), - PJ_EBUSY); - - sent = datalen; - /* sent would be -1 after pj_sock_send() if it returns error. */ - rc = pj_sock_send(key->fd, data, &sent, 0); - if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return rc; - } - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_WRITE; - key->wr_buf = NULL; - key->wr_buflen = datalen; - PJ_FD_SET(key->fd, &ioque->wfdset); - - pj_lock_release(ioque->lock); - - return PJ_EPENDING; -} - -/* - * pj_ioqueue_send() - * - * Start asynchronous send() to the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags) -{ - pj_status_t rc; - pj_ssize_t sent; - - PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for writing before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), - PJ_EBUSY); - - sent = datalen; - /* sent would be -1 after pj_sock_send() if it returns error. */ - rc = pj_sock_send(key->fd, data, &sent, flags); - if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return rc; - } - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_SEND; - key->wr_buf = NULL; - key->wr_buflen = datalen; - PJ_FD_SET(key->fd, &ioque->wfdset); - - pj_lock_release(ioque->lock); - - return PJ_EPENDING; -} - - -/* - * pj_ioqueue_sendto() - * - * Start asynchronous write() to the descriptor. - */ -PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags, - const pj_sockaddr_t *addr, - int addrlen) -{ - pj_status_t rc; - pj_ssize_t sent; - - PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); - PJ_CHECK_STACK(); - - /* For consistency with other ioqueue implementation, we would reject - * if descriptor has already been submitted for writing before. - */ - PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND) == 0 && - (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), - PJ_EBUSY); - - sent = datalen; - /* sent would be -1 after pj_sock_sendto() if it returns error. */ - rc = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); - if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { - return rc; - } - - pj_lock_acquire(ioque->lock); - - key->op |= PJ_IOQUEUE_OP_SEND_TO; - key->wr_buf = NULL; - key->wr_buflen = datalen; - PJ_FD_SET(key->fd, &ioque->wfdset); - - pj_lock_release(ioque->lock); - return PJ_EPENDING; -} - -#if PJ_HAS_TCP -/* - * Initiate overlapped accept() operation. - */ -PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - pj_sock_t *new_sock, - pj_sockaddr_t *local, - pj_sockaddr_t *remote, - int *addrlen) -{ - /* check parameters. All must be specified! */ - pj_assert(ioqueue && key && new_sock); - - /* Server socket must have no other operation! */ - pj_assert(key->op == 0); - - pj_lock_acquire(ioqueue->lock); - - key->op = PJ_IOQUEUE_OP_ACCEPT; - key->accept_fd = new_sock; - key->rmt_addr = remote; - key->rmt_addrlen = addrlen; - key->local_addr = local; - key->local_addrlen = addrlen; /* use same addr. as rmt_addrlen */ - - PJ_FD_SET(key->fd, &ioqueue->rfdset); - - pj_lock_release(ioqueue->lock); - return PJ_EPENDING; -} - -/* - * Initiate overlapped connect() operation (well, it's non-blocking actually, - * since there's no overlapped version of connect()). - */ -PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - const pj_sockaddr_t *addr, - int addrlen ) -{ - pj_status_t rc; - - /* check parameters. All must be specified! */ - PJ_ASSERT_RETURN(ioqueue && key && addr && addrlen, PJ_EINVAL); - - /* Connecting socket must have no other operation! */ - PJ_ASSERT_RETURN(key->op == 0, PJ_EBUSY); - - rc = pj_sock_connect(key->fd, addr, addrlen); - if (rc == PJ_SUCCESS) { - /* Connected! */ - return PJ_SUCCESS; - } else { - if (rc == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) || - rc == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) - { - /* Pending! */ - pj_lock_acquire(ioqueue->lock); - key->op = PJ_IOQUEUE_OP_CONNECT; - PJ_FD_SET(key->fd, &ioqueue->wfdset); - PJ_FD_SET(key->fd, &ioqueue->xfdset); - pj_lock_release(ioqueue->lock); - return PJ_EPENDING; - } else { - /* Error! */ - return rc; - } - } -} -#endif /* PJ_HAS_TCP */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_select.c 15 10/29/05 10:27p Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_select.c $ + * + * 15 10/29/05 10:27p Bennylp + * Fixed misc warnings. + * + * 14 10/29/05 11:31a Bennylp + * Changed accept and lock. + * + * 13 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 12 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 11 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ + +/* + * sock_select.c + * + * This is the implementation of IOQueue using pj_sock_select(). + * It runs anywhere where pj_sock_select() is available (currently + * Win32, Linux, Linux kernel, etc.). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * ISSUES with ioqueue_select() + * + * EAGAIN/EWOULDBLOCK error in recv(): + * - when multiple threads are working with the ioqueue, application + * may receive EAGAIN or EWOULDBLOCK in the receive callback. + * This error happens because more than one thread is watching for + * the same descriptor set, so when all of them call recv() or recvfrom() + * simultaneously, only one will succeed and the rest will get the error. + * + */ +#define THIS_FILE "ioq_select" + +#define PJ_IOQUEUE_IS_READ_OP(op) ((op & PJ_IOQUEUE_OP_READ) || \ + (op & PJ_IOQUEUE_OP_RECV) || \ + (op & PJ_IOQUEUE_OP_RECV_FROM)) +#define PJ_IOQUEUE_IS_WRITE_OP(op) ((op & PJ_IOQUEUE_OP_WRITE) || \ + (op & PJ_IOQUEUE_OP_SEND) || \ + (op & PJ_IOQUEUE_OP_SEND_TO)) + + +#if PJ_HAS_TCP +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) (op & PJ_IOQUEUE_OP_ACCEPT) +# define PJ_IOQUEUE_IS_CONNECT_OP(op) (op & PJ_IOQUEUE_OP_CONNECT) +#else +# define PJ_IOQUEUE_IS_ACCEPT_OP(op) 0 +# define PJ_IOQUEUE_IS_CONNECT_OP(op) 0 +#endif + +/* + * During debugging build, VALIDATE_FD_SET is set. + * This will check the validity of the fd_sets. + */ +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +# define VALIDATE_FD_SET 1 +#else +# define VALIDATE_FD_SET 0 +#endif + +/* + * This describes each key. + */ +struct pj_ioqueue_key_t +{ + PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t) + pj_sock_t fd; + pj_ioqueue_operation_e op; + void *user_data; + pj_ioqueue_callback cb; + + void *rd_buf; + unsigned rd_flags; + pj_size_t rd_buflen; + void *wr_buf; + pj_size_t wr_buflen; + + pj_sockaddr_t *rmt_addr; + int *rmt_addrlen; + + pj_sockaddr_t *local_addr; + int *local_addrlen; + + pj_sock_t *accept_fd; +}; + +/* + * This describes the I/O queue itself. + */ +struct pj_ioqueue_t +{ + pj_lock_t *lock; + pj_bool_t auto_delete_lock; + unsigned max, count; + pj_ioqueue_key_t hlist; + pj_fd_set_t rfdset; + pj_fd_set_t wfdset; +#if PJ_HAS_TCP + pj_fd_set_t xfdset; +#endif +}; + +/* + * pj_ioqueue_create() + * + * Create select ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + int max_threads, + pj_ioqueue_t **p_ioqueue) +{ + pj_ioqueue_t *ioque; + pj_status_t rc; + + PJ_UNUSED_ARG(max_threads); + + if (max_fd > PJ_IOQUEUE_MAX_HANDLES) { + pj_assert(!"max_fd too large"); + return PJ_EINVAL; + } + + ioque = pj_pool_alloc(pool, sizeof(pj_ioqueue_t)); + ioque->max = max_fd; + ioque->count = 0; + PJ_FD_ZERO(&ioque->rfdset); + PJ_FD_ZERO(&ioque->wfdset); +#if PJ_HAS_TCP + PJ_FD_ZERO(&ioque->xfdset); +#endif + pj_list_init(&ioque->hlist); + + rc = pj_lock_create_recursive_mutex(pool, "ioq%p", &ioque->lock); + if (rc != PJ_SUCCESS) + return rc; + + ioque->auto_delete_lock = PJ_TRUE; + + PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioque)); + + *p_ioqueue = ioque; + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_destroy() + * + * Destroy ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque) +{ + pj_status_t rc = PJ_SUCCESS; + + PJ_ASSERT_RETURN(ioque, PJ_EINVAL); + + if (ioque->auto_delete_lock) + rc = pj_lock_destroy(ioque->lock); + + return rc; +} + + +/* + * pj_ioqueue_set_lock() + */ +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL); + + if (ioque->auto_delete_lock) { + pj_lock_destroy(ioque->lock); + } + + ioque->lock = lock; + ioque->auto_delete_lock = auto_delete; + + return PJ_SUCCESS; +} + + +/* + * pj_ioqueue_register_sock() + * + * Register a handle to ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t sock, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **p_key) +{ + pj_ioqueue_key_t *key = NULL; + pj_uint32_t value; + pj_status_t rc = PJ_SUCCESS; + + PJ_ASSERT_RETURN(pool && ioque && sock != PJ_INVALID_SOCKET && + cb && p_key, PJ_EINVAL); + + pj_lock_acquire(ioque->lock); + + if (ioque->count >= ioque->max) { + rc = PJ_ETOOMANY; + goto on_return; + } + + /* Set socket to nonblocking. */ + value = 1; +#ifdef PJ_WIN32 + if (ioctlsocket(sock, FIONBIO, (unsigned long*)&value)) { +#else + if (ioctl(sock, FIONBIO, &value)) { +#endif + rc = pj_get_netos_error(); + goto on_return; + } + + /* Create key. */ + key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); + key->fd = sock; + key->user_data = user_data; + + /* Save callback. */ + pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback)); + + /* Register */ + pj_list_insert_before(&ioque->hlist, key); + ++ioque->count; + +on_return: + *p_key = key; + pj_lock_release(ioque->lock); + + return rc; +} + +/* + * pj_ioqueue_unregister() + * + * Unregister handle from ioqueue. + */ +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key) +{ + PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL); + + pj_lock_acquire(ioque->lock); + + pj_assert(ioque->count > 0); + --ioque->count; + pj_list_erase(key); + PJ_FD_CLR(key->fd, &ioque->rfdset); + PJ_FD_CLR(key->fd, &ioque->wfdset); +#if PJ_HAS_TCP + PJ_FD_CLR(key->fd, &ioque->xfdset); +#endif + + pj_lock_release(ioque->lock); + return PJ_SUCCESS; +} + +/* + * pj_ioqueue_get_user_data() + * + * Obtain value associated with a key. + */ +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key != NULL, NULL); + return key->user_data; +} + + +/* This supposed to check whether the fd_set values are consistent + * with the operation currently set in each key. + */ +#if VALIDATE_FD_SET +static void validate_sets(const pj_ioqueue_t *ioque, + const pj_fd_set_t *rfdset, + const pj_fd_set_t *wfdset, + const pj_fd_set_t *xfdset) +{ + pj_ioqueue_key_t *key; + + key = ioque->hlist.next; + while (key != &ioque->hlist) { + if ((key->op & PJ_IOQUEUE_OP_READ) + || (key->op & PJ_IOQUEUE_OP_RECV) + || (key->op & PJ_IOQUEUE_OP_RECV_FROM) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + || (key->op & PJ_IOQUEUE_OP_ACCEPT) +#endif + ) + { + pj_assert(PJ_FD_ISSET(key->fd, rfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0); + } + if ((key->op & PJ_IOQUEUE_OP_WRITE) + || (key->op & PJ_IOQUEUE_OP_SEND) + || (key->op & PJ_IOQUEUE_OP_SEND_TO) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + || (key->op & PJ_IOQUEUE_OP_CONNECT) +#endif + ) + { + pj_assert(PJ_FD_ISSET(key->fd, wfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0); + } +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 + if (key->op & PJ_IOQUEUE_OP_CONNECT) + { + pj_assert(PJ_FD_ISSET(key->fd, xfdset)); + } + else { + pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0); + } +#endif /* PJ_HAS_TCP */ + + key = key->next; + } +} +#endif /* VALIDATE_FD_SET */ + + +/* + * pj_ioqueue_poll() + * + * Few things worth written: + * + * - we used to do only one callback called per poll, but it didn't go + * very well. The reason is because on some situation, the write + * callback gets called all the time, thus doesn't give the read + * callback to get called. This happens, for example, when user + * submit write operation inside the write callback. + * As the result, we changed the behaviour so that now multiple + * callbacks are called in a single poll. It should be fast too, + * just that we need to be carefull with the ioqueue data structs. + * + * - to guarantee preemptiveness etc, the poll function must strictly + * work on fd_set copy of the ioqueue (not the original one). + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + pj_fd_set_t rfdset, wfdset, xfdset; + int count; + pj_ioqueue_key_t *h; + + /* Lock ioqueue before making fd_set copies */ + pj_lock_acquire(ioque->lock); + + if (PJ_FD_COUNT(&ioque->rfdset)==0 && + PJ_FD_COUNT(&ioque->wfdset)==0 && + PJ_FD_COUNT(&ioque->xfdset)==0) + { + pj_lock_release(ioque->lock); + if (timeout) + pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout)); + return 0; + } + + /* Copy ioqueue's pj_fd_set_t to local variables. */ + pj_memcpy(&rfdset, &ioque->rfdset, sizeof(pj_fd_set_t)); + pj_memcpy(&wfdset, &ioque->wfdset, sizeof(pj_fd_set_t)); +#if PJ_HAS_TCP + pj_memcpy(&xfdset, &ioque->xfdset, sizeof(pj_fd_set_t)); +#else + PJ_FD_ZERO(&xfdset); +#endif + +#if VALIDATE_FD_SET + validate_sets(ioque, &rfdset, &wfdset, &xfdset); +#endif + + /* Unlock ioqueue before select(). */ + pj_lock_release(ioque->lock); + + count = pj_sock_select(FD_SETSIZE, &rfdset, &wfdset, &xfdset, timeout); + + if (count <= 0) + return count; + + /* Lock ioqueue again before scanning for signalled sockets. */ + pj_lock_acquire(ioque->lock); + +#if PJ_HAS_TCP + /* Scan for exception socket */ + h = ioque->hlist.next; +do_except_scan: + for ( ; h!=&ioque->hlist; h = h->next) { + if ((h->op & PJ_IOQUEUE_OP_CONNECT) && PJ_FD_ISSET(h->fd, &xfdset)) + break; + } + if (h != &ioque->hlist) { + /* 'connect()' should be the only operation. */ + pj_assert((h->op == PJ_IOQUEUE_OP_CONNECT)); + + /* Clear operation. */ + h->op &= ~(PJ_IOQUEUE_OP_CONNECT); + PJ_FD_CLR(h->fd, &ioque->wfdset); + PJ_FD_CLR(h->fd, &ioque->xfdset); + PJ_FD_CLR(h->fd, &wfdset); + PJ_FD_CLR(h->fd, &xfdset); + + /* Call callback. */ + if (h->cb.on_connect_complete) + (*h->cb.on_connect_complete)(h, -1); + + /* Re-scan exception list. */ + goto do_except_scan; + } +#endif /* PJ_HAS_TCP */ + + /* Scan for readable socket. */ + h = ioque->hlist.next; +do_readable_scan: + for ( ; h!=&ioque->hlist; h = h->next) { + if ((PJ_IOQUEUE_IS_READ_OP(h->op) || PJ_IOQUEUE_IS_ACCEPT_OP(h->op)) && + PJ_FD_ISSET(h->fd, &rfdset)) + { + break; + } + } + if (h != &ioque->hlist) { + pj_status_t rc; + + pj_assert(PJ_IOQUEUE_IS_READ_OP(h->op) || + PJ_IOQUEUE_IS_ACCEPT_OP(h->op)); + +# if PJ_HAS_TCP + if ((h->op & PJ_IOQUEUE_OP_ACCEPT)) { + /* accept() must be the only operation specified on server socket */ + pj_assert(h->op == PJ_IOQUEUE_OP_ACCEPT); + + rc=pj_sock_accept(h->fd, h->accept_fd, h->rmt_addr, h->rmt_addrlen); + if (rc==0 && h->local_addr) { + rc = pj_sock_getsockname(*h->accept_fd, h->local_addr, + h->local_addrlen); + } + + h->op &= ~(PJ_IOQUEUE_OP_ACCEPT); + PJ_FD_CLR(h->fd, &ioque->rfdset); + + /* Call callback. */ + if (h->cb.on_accept_complete) + (*h->cb.on_accept_complete)(h, *h->accept_fd, rc); + + /* Re-scan readable sockets. */ + goto do_readable_scan; + } + else { +# endif + pj_ssize_t bytes_read = h->rd_buflen; + + if ((h->op & PJ_IOQUEUE_OP_RECV_FROM)) { + rc = pj_sock_recvfrom(h->fd, h->rd_buf, &bytes_read, 0, + h->rmt_addr, h->rmt_addrlen); + } else if ((h->op & PJ_IOQUEUE_OP_RECV)) { + rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0); + } else { + /* + * User has specified pj_ioqueue_read(). + * On Win32, we should do ReadFile(). But because we got + * here because of select() anyway, user must have put a + * socket descriptor on h->fd, which in this case we can + * just call pj_sock_recv() instead of ReadFile(). + * On Unix, user may put a file in h->fd, so we'll have + * to call read() here. + * This may not compile on systems which doesn't have + * read(). That's why we only specify PJ_LINUX here so + * that error is easier to catch. + */ +# if defined(PJ_WIN32) && PJ_WIN32 != 0 + rc = pj_sock_recv(h->fd, h->rd_buf, &bytes_read, 0); +# elif (defined(PJ_LINUX) && PJ_LINUX != 0) || \ + (defined(PJ_SUNOS) && PJ_SUNOS != 0) + bytes_read = read(h->fd, h->rd_buf, bytes_read); + rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error(); +# elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0 + bytes_read = sys_read(h->fd, h->rd_buf, bytes_read); + rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read; +# else +# error "Implement read() for this platform!" +# endif + } + + if (rc != PJ_SUCCESS) { +# if defined(PJ_WIN32) && PJ_WIN32 != 0 + /* On Win32, for UDP, WSAECONNRESET on the receive side + * indicates that previous sending has triggered ICMP Port + * Unreachable message. + * But we wouldn't know at this point which one of previous + * key that has triggered the error, since UDP socket can + * be shared! + * So we'll just ignore it! + */ + + if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) { + PJ_LOG(4,(THIS_FILE, + "Ignored ICMP port unreach. on key=%p", h)); + } +# endif + + /* In any case we would report this to caller. */ + bytes_read = -rc; + } + + h->op &= ~(PJ_IOQUEUE_OP_READ | PJ_IOQUEUE_OP_RECV | + PJ_IOQUEUE_OP_RECV_FROM); + PJ_FD_CLR(h->fd, &ioque->rfdset); + PJ_FD_CLR(h->fd, &rfdset); + + /* Call callback. */ + if (h->cb.on_read_complete) + (*h->cb.on_read_complete)(h, bytes_read); + + /* Re-scan readable sockets. */ + goto do_readable_scan; + + } + } + + /* Scan for writable socket */ + h = ioque->hlist.next; +do_writable_scan: + for ( ; h!=&ioque->hlist; h = h->next) { + if ((PJ_IOQUEUE_IS_WRITE_OP(h->op) || PJ_IOQUEUE_IS_CONNECT_OP(h->op)) + && PJ_FD_ISSET(h->fd, &wfdset)) + { + break; + } + } + if (h != &ioque->hlist) { + pj_assert(PJ_IOQUEUE_IS_WRITE_OP(h->op) || + PJ_IOQUEUE_IS_CONNECT_OP(h->op)); + +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0 + if ((h->op & PJ_IOQUEUE_OP_CONNECT)) { + /* Completion of connect() operation */ + pj_ssize_t bytes_transfered; + +#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \ + (defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0) + /* from connect(2): + * On Linux, use getsockopt to read the SO_ERROR option at + * level SOL_SOCKET to determine whether connect() completed + * successfully (if SO_ERROR is zero). + */ + int value; + socklen_t vallen = sizeof(value); + int gs_rc = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, + &value, &vallen); + if (gs_rc != 0) { + /* Argh!! What to do now??? + * Just indicate that the socket is connected. The + * application will get error as soon as it tries to use + * the socket to send/receive. + */ + bytes_transfered = 0; + } else { + bytes_transfered = value; + } +#elif defined(PJ_WIN32) && PJ_WIN32!=0 + bytes_transfered = 0; /* success */ +#else + /* Excellent information in D.J. Bernstein page: + * http://cr.yp.to/docs/connect.html + * + * Seems like the most portable way of detecting connect() + * failure is to call getpeername(). If socket is connected, + * getpeername() will return 0. If the socket is not connected, + * it will return ENOTCONN, and read(fd, &ch, 1) will produce + * the right errno through error slippage. This is a combination + * of suggestions from Douglas C. Schmidt and Ken Keys. + */ + int gp_rc; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + gp_rc = getpeername(h->fd, (struct sockaddr*)&addr, &addrlen); + bytes_transfered = gp_rc; +#endif + + /* Clear operation. */ + h->op &= (~PJ_IOQUEUE_OP_CONNECT); + PJ_FD_CLR(h->fd, &ioque->wfdset); + PJ_FD_CLR(h->fd, &ioque->xfdset); + + /* Call callback. */ + if (h->cb.on_connect_complete) + (*h->cb.on_connect_complete)(h, bytes_transfered); + + /* Re-scan writable sockets. */ + goto do_writable_scan; + + } else +#endif /* PJ_HAS_TCP */ + { + /* Completion of write(), send(), or sendto() operation. */ + + /* Clear operation. */ + h->op &= ~(PJ_IOQUEUE_OP_WRITE | PJ_IOQUEUE_OP_SEND | + PJ_IOQUEUE_OP_SEND_TO); + PJ_FD_CLR(h->fd, &ioque->wfdset); + PJ_FD_CLR(h->fd, &wfdset); + + /* Call callback. */ + /* All data must have been sent? */ + if (h->cb.on_write_complete) + (*h->cb.on_write_complete)(h, h->wr_buflen); + + /* Re-scan writable sockets. */ + goto do_writable_scan; + } + } + + /* Shouldn't happen. */ + /* For strange reason on WinXP select() can return 1 while there is no + * pj_fd_set_t signaled. */ + /* pj_assert(0); */ + + //count = 0; + + pj_lock_release(ioque->lock); + return count; +} + +/* + * pj_ioqueue_read() + * + * Start asynchronous read from the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for reading before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), + PJ_EBUSY); + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_READ; + key->rd_flags = 0; + key->rd_buf = buffer; + key->rd_buflen = buflen; + PJ_FD_SET(key->fd, &ioque->rfdset); + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_recv() + * + * Start asynchronous recv() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags ) +{ + PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for reading before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), + PJ_EBUSY); + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_RECV; + key->rd_buf = buffer; + key->rd_buflen = buflen; + key->rd_flags = flags; + PJ_FD_SET(key->fd, &ioque->rfdset); + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + +/* + * pj_ioqueue_recvfrom() + * + * Start asynchronous recvfrom() from the socket. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + PJ_ASSERT_RETURN(ioque && key && buffer, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for reading before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_READ) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV) == 0 && + (key->op & PJ_IOQUEUE_OP_RECV_FROM) == 0), + PJ_EBUSY); + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_RECV_FROM; + key->rd_buf = buffer; + key->rd_buflen = buflen; + key->rd_flags = flags; + key->rmt_addr = addr; + key->rmt_addrlen = addrlen; + PJ_FD_SET(key->fd, &ioque->rfdset); + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + +/* + * pj_ioqueue_write() + * + * Start asynchronous write() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + pj_status_t rc; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for writing before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), + PJ_EBUSY); + + sent = datalen; + /* sent would be -1 after pj_sock_send() if it returns error. */ + rc = pj_sock_send(key->fd, data, &sent, 0); + if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return rc; + } + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_WRITE; + key->wr_buf = NULL; + key->wr_buflen = datalen; + PJ_FD_SET(key->fd, &ioque->wfdset); + + pj_lock_release(ioque->lock); + + return PJ_EPENDING; +} + +/* + * pj_ioqueue_send() + * + * Start asynchronous send() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags) +{ + pj_status_t rc; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for writing before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), + PJ_EBUSY); + + sent = datalen; + /* sent would be -1 after pj_sock_send() if it returns error. */ + rc = pj_sock_send(key->fd, data, &sent, flags); + if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return rc; + } + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_SEND; + key->wr_buf = NULL; + key->wr_buflen = datalen; + PJ_FD_SET(key->fd, &ioque->wfdset); + + pj_lock_release(ioque->lock); + + return PJ_EPENDING; +} + + +/* + * pj_ioqueue_sendto() + * + * Start asynchronous write() to the descriptor. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + pj_status_t rc; + pj_ssize_t sent; + + PJ_ASSERT_RETURN(ioque && key && data, PJ_EINVAL); + PJ_CHECK_STACK(); + + /* For consistency with other ioqueue implementation, we would reject + * if descriptor has already been submitted for writing before. + */ + PJ_ASSERT_RETURN(((key->op & PJ_IOQUEUE_OP_WRITE) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND) == 0 && + (key->op & PJ_IOQUEUE_OP_SEND_TO) == 0), + PJ_EBUSY); + + sent = datalen; + /* sent would be -1 after pj_sock_sendto() if it returns error. */ + rc = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen); + if (rc != PJ_SUCCESS && rc != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + return rc; + } + + pj_lock_acquire(ioque->lock); + + key->op |= PJ_IOQUEUE_OP_SEND_TO; + key->wr_buf = NULL; + key->wr_buflen = datalen; + PJ_FD_SET(key->fd, &ioque->wfdset); + + pj_lock_release(ioque->lock); + return PJ_EPENDING; +} + +#if PJ_HAS_TCP +/* + * Initiate overlapped accept() operation. + */ +PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + /* check parameters. All must be specified! */ + pj_assert(ioqueue && key && new_sock); + + /* Server socket must have no other operation! */ + pj_assert(key->op == 0); + + pj_lock_acquire(ioqueue->lock); + + key->op = PJ_IOQUEUE_OP_ACCEPT; + key->accept_fd = new_sock; + key->rmt_addr = remote; + key->rmt_addrlen = addrlen; + key->local_addr = local; + key->local_addrlen = addrlen; /* use same addr. as rmt_addrlen */ + + PJ_FD_SET(key->fd, &ioqueue->rfdset); + + pj_lock_release(ioqueue->lock); + return PJ_EPENDING; +} + +/* + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + pj_status_t rc; + + /* check parameters. All must be specified! */ + PJ_ASSERT_RETURN(ioqueue && key && addr && addrlen, PJ_EINVAL); + + /* Connecting socket must have no other operation! */ + PJ_ASSERT_RETURN(key->op == 0, PJ_EBUSY); + + rc = pj_sock_connect(key->fd, addr, addrlen); + if (rc == PJ_SUCCESS) { + /* Connected! */ + return PJ_SUCCESS; + } else { + if (rc == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) || + rc == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) + { + /* Pending! */ + pj_lock_acquire(ioqueue->lock); + key->op = PJ_IOQUEUE_OP_CONNECT; + PJ_FD_SET(key->fd, &ioqueue->wfdset); + PJ_FD_SET(key->fd, &ioqueue->xfdset); + pj_lock_release(ioqueue->lock); + return PJ_EPENDING; + } else { + /* Error! */ + return rc; + } + } +} +#endif /* PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/ioqueue_winnt.c b/pjlib/src/pj/ioqueue_winnt.c index 93116c9dd..2a8fd7e83 100644 --- a/pjlib/src/pj/ioqueue_winnt.c +++ b/pjlib/src/pj/ioqueue_winnt.c @@ -1,852 +1,852 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_winnt.c 11 10/29/05 11:31a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_winnt.c $ - * - * 11 10/29/05 11:31a Bennylp - * Changed accept and lock. - * - * 10 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 9 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 -# include -#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 -# include -#endif - -#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0 -# include -#endif - - -#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+20) - -/* - * OVERLAP structure for send and receive. - */ -typedef struct ioqueue_overlapped -{ - WSAOVERLAPPED overlapped; - pj_ioqueue_operation_e operation; - WSABUF wsabuf; -} ioqueue_overlapped; - -#if PJ_HAS_TCP -/* - * OVERLAP structure for accept. - */ -typedef struct ioqueue_accept_rec -{ - WSAOVERLAPPED overlapped; - pj_ioqueue_operation_e operation; - pj_sock_t newsock; - pj_sock_t *newsock_ptr; - int *addrlen; - void *remote; - void *local; - char accept_buf[2 * ACCEPT_ADDR_LEN]; -} ioqueue_accept_rec; -#endif - -/* - * Structure for individual socket. - */ -struct pj_ioqueue_key_t -{ - HANDLE hnd; - void *user_data; - ioqueue_overlapped recv_overlapped; - ioqueue_overlapped send_overlapped; -#if PJ_HAS_TCP - int connecting; - ioqueue_accept_rec accept_overlapped; -#endif - pj_ioqueue_callback cb; -}; - -/* - * IO Queue structure. - */ -struct pj_ioqueue_t -{ - HANDLE iocp; - pj_lock_t *lock; - pj_bool_t auto_delete_lock; - unsigned event_count; - HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1]; -#if PJ_HAS_TCP - unsigned connecting_count; - HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1]; - pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1]; -#endif -}; - - -#if PJ_HAS_TCP -/* - * Process the socket when the overlapped accept() completed. - */ -static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped) -{ - struct sockaddr *local; - struct sockaddr *remote; - int locallen, remotelen; - - PJ_CHECK_STACK(); - - /* Operation complete immediately. */ - GetAcceptExSockaddrs( accept_overlapped->accept_buf, - 0, - ACCEPT_ADDR_LEN, - ACCEPT_ADDR_LEN, - &local, - &locallen, - &remote, - &remotelen); - pj_memcpy(accept_overlapped->local, local, locallen); - pj_memcpy(accept_overlapped->remote, remote, locallen); - *accept_overlapped->addrlen = locallen; - if (accept_overlapped->newsock_ptr) - *accept_overlapped->newsock_ptr = accept_overlapped->newsock; - accept_overlapped->operation = 0; - accept_overlapped->newsock = PJ_INVALID_SOCKET; -} - -static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos) -{ - pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos]; - HANDLE hEvent = ioqueue->connecting_handles[pos]; - unsigned long optval; - - /* Remove key from array of connecting handles. */ - pj_array_erase(ioqueue->connecting_keys, sizeof(key), - ioqueue->connecting_count, pos); - pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE), - ioqueue->connecting_count, pos); - --ioqueue->connecting_count; - - /* Disassociate the socket from the event. */ - WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0); - - /* Put event object to pool. */ - if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) { - ioqueue->event_pool[ioqueue->event_count++] = hEvent; - } else { - /* Shouldn't happen. There should be no more pending connections - * than max. - */ - pj_assert(0); - CloseHandle(hEvent); - } - - /* Set socket to blocking again. */ - optval = 0; - if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) { - DWORD dwStatus; - dwStatus = WSAGetLastError(); - } -} - -/* - * Poll for the completion of non-blocking connect(). - * If there's a completion, the function return the key of the completed - * socket, and 'result' argument contains the connect() result. If connect() - * succeeded, 'result' will have value zero, otherwise will have the error - * code. - */ -static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue, - pj_ssize_t *connect_err ) -{ - pj_ioqueue_key_t *key = NULL; - - if (ioqueue->connecting_count) { - DWORD result; - - pj_lock_acquire(ioqueue->lock); - result = WaitForMultipleObjects(ioqueue->connecting_count, - ioqueue->connecting_handles, - FALSE, 0); - if (result >= WAIT_OBJECT_0 && - result < WAIT_OBJECT_0+ioqueue->connecting_count) - { - WSANETWORKEVENTS net_events; - - /* Got completed connect(). */ - unsigned pos = result - WAIT_OBJECT_0; - key = ioqueue->connecting_keys[pos]; - - /* See whether connect has succeeded. */ - WSAEnumNetworkEvents((pj_sock_t)key->hnd, - ioqueue->connecting_handles[pos], - &net_events); - *connect_err = net_events.iErrorCode[FD_CONNECT_BIT]; - - /* Erase socket from pending connect. */ - erase_connecting_socket(ioqueue, pos); - } - pj_lock_release(ioqueue->lock); - } - return key; -} -#endif - - -PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, - pj_size_t max_fd, - int max_threads, - pj_ioqueue_t **ioqueue) -{ - pj_ioqueue_t *ioq; - pj_status_t rc; - - PJ_UNUSED_ARG(max_fd); - PJ_ASSERT_RETURN(pool && ioqueue, PJ_EINVAL); - - ioq = pj_pool_zalloc(pool, sizeof(*ioq)); - ioq->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, max_threads); - if (ioq->iocp == NULL) - return PJ_RETURN_OS_ERROR(GetLastError()); - - rc = pj_lock_create_simple_mutex(pool, NULL, &ioq->lock); - if (rc != PJ_SUCCESS) { - CloseHandle(ioq->iocp); - return rc; - } - - ioq->auto_delete_lock = PJ_TRUE; - - *ioqueue = ioq; - - PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioq)); - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque ) -{ - unsigned i; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(ioque, PJ_EINVAL); - - /* Destroy events in the pool */ - for (i=0; ievent_count; ++i) { - CloseHandle(ioque->event_pool[i]); - } - ioque->event_count = 0; - - if (ioque->auto_delete_lock) - pj_lock_destroy(ioque->lock); - - if (CloseHandle(ioque->iocp) == TRUE) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, - pj_lock_t *lock, - pj_bool_t auto_delete ) -{ - PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL); - - if (ioque->auto_delete_lock) { - pj_lock_destroy(ioque->lock); - } - - ioque->lock = lock; - ioque->auto_delete_lock = auto_delete; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, - pj_ioqueue_t *ioque, - pj_sock_t hnd, - void *user_data, - const pj_ioqueue_callback *cb, - pj_ioqueue_key_t **key ) -{ - HANDLE hioq; - pj_ioqueue_key_t *rec; - - PJ_ASSERT_RETURN(pool && ioque && cb && key, PJ_EINVAL); - - rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); - rec->hnd = (HANDLE)hnd; - rec->user_data = user_data; - pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); -#if PJ_HAS_TCP - rec->accept_overlapped.newsock = PJ_INVALID_SOCKET; -#endif - hioq = CreateIoCompletionPort((HANDLE)hnd, ioque->iocp, (DWORD)rec, 0); - if (!hioq) { - return PJ_RETURN_OS_ERROR(GetLastError()); - } - - *key = rec; - return PJ_SUCCESS; -} - - - -PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key ) -{ - PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL); - -#if PJ_HAS_TCP - if (key->connecting) { - unsigned pos; - - /* Erase from connecting_handles */ - pj_lock_acquire(ioque->lock); - for (pos=0; pos < ioque->connecting_count; ++pos) { - if (ioque->connecting_keys[pos] == key) { - erase_connecting_socket(ioque, pos); - if (key->accept_overlapped.newsock_ptr) { - /* ??? shouldn't it be newsock instead of newsock_ptr??? */ - closesocket(*key->accept_overlapped.newsock_ptr); - } - break; - } - } - pj_lock_release(ioque->lock); - key->connecting = 0; - } -#endif - return PJ_SUCCESS; -} - -PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) -{ - PJ_ASSERT_RETURN(key, NULL); - return key->user_data; -} - -/* - * Poll for events. - */ -PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) -{ - DWORD dwMsec, dwBytesTransfered, dwKey; - ioqueue_overlapped *ov; - pj_ioqueue_key_t *key; - pj_ssize_t size_status; - BOOL rc; - - PJ_ASSERT_RETURN(ioque, -PJ_EINVAL); - - /* Check the connecting array. */ -#if PJ_HAS_TCP - key = check_connecting(ioque, &size_status); - if (key != NULL) { - key->cb.on_connect_complete(key, (int)size_status); - return 1; - } -#endif - - /* Calculate miliseconds timeout for GetQueuedCompletionStatus */ - dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE; - - /* Poll for completion status. */ - rc = GetQueuedCompletionStatus(ioque->iocp, &dwBytesTransfered, &dwKey, - (OVERLAPPED**)&ov, dwMsec); - - /* The return value is: - * - nonzero if event was dequeued. - * - zero and ov==NULL if no event was dequeued. - * - zero and ov!=NULL if event for failed I/O was dequeued. - */ - if (ov) { - /* Event was dequeued for either successfull or failed I/O */ - key = (pj_ioqueue_key_t*)dwKey; - size_status = dwBytesTransfered; - switch (ov->operation) { - case PJ_IOQUEUE_OP_READ: - case PJ_IOQUEUE_OP_RECV: - case PJ_IOQUEUE_OP_RECV_FROM: - key->recv_overlapped.operation = 0; - if (key->cb.on_read_complete) - key->cb.on_read_complete(key, size_status); - break; - case PJ_IOQUEUE_OP_WRITE: - case PJ_IOQUEUE_OP_SEND: - case PJ_IOQUEUE_OP_SEND_TO: - key->send_overlapped.operation = 0; - if (key->cb.on_write_complete) - key->cb.on_write_complete(key, size_status); - break; -#if PJ_HAS_TCP - case PJ_IOQUEUE_OP_ACCEPT: - /* special case for accept. */ - ioqueue_on_accept_complete((ioqueue_accept_rec*)ov); - if (key->cb.on_accept_complete) - key->cb.on_accept_complete(key, key->accept_overlapped.newsock, - 0); - break; - case PJ_IOQUEUE_OP_CONNECT: -#endif - case PJ_IOQUEUE_OP_NONE: - pj_assert(0); - break; - } - return 1; - } - - if (GetLastError()==WAIT_TIMEOUT) { - /* Check the connecting array. */ -#if PJ_HAS_TCP - key = check_connecting(ioque, &size_status); - if (key != NULL) { - key->cb.on_connect_complete(key, (int)size_status); - return 1; - } -#endif - return 0; - } - return -1; -} - -/* - * pj_ioqueue_read() - * - * Initiate overlapped ReadFile operation. - */ -PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen) -{ - BOOL rc; - DWORD bytesRead; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ioque); - - if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) { - pj_assert(!"Operation already pending for this descriptor"); - return PJ_EBUSY; - } - - pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped)); - key->recv_overlapped.operation = PJ_IOQUEUE_OP_READ; - - rc = ReadFile(key->hnd, buffer, buflen, &bytesRead, - &key->recv_overlapped.overlapped); - if (rc == FALSE) { - DWORD dwStatus = GetLastError(); - if (dwStatus==ERROR_IO_PENDING) - return PJ_EPENDING; - else - return PJ_STATUS_FROM_OS(dwStatus); - } else { - /* - * This is workaround to a probable bug in Win2000 (probably NT too). - * Even if 'rc' is TRUE, which indicates operation has completed, - * GetQueuedCompletionStatus still will return the key. - * So as work around, we always return PJ_EPENDING here. - */ - return PJ_EPENDING; - } -} - -/* - * pj_ioqueue_recv() - * - * Initiate overlapped WSARecv() operation. - */ -PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags ) -{ - int rc; - DWORD bytesRead; - DWORD dwFlags = 0; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ioque); - - if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) { - pj_assert(!"Operation already pending for this socket"); - return PJ_EBUSY; - } - - pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped)); - key->recv_overlapped.operation = PJ_IOQUEUE_OP_READ; - - key->recv_overlapped.wsabuf.buf = buffer; - key->recv_overlapped.wsabuf.len = buflen; - - dwFlags = flags; - - rc = WSARecv((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1, - &bytesRead, &dwFlags, - &key->recv_overlapped.overlapped, NULL); - if (rc == SOCKET_ERROR) { - DWORD dwStatus = WSAGetLastError(); - if (dwStatus==WSA_IO_PENDING) - return PJ_EPENDING; - else - return PJ_STATUS_FROM_OS(dwStatus); - } else { - /* Must always return pending status. - * See comments on pj_ioqueue_read - * return bytesRead; - */ - return PJ_EPENDING; - } -} - -/* - * pj_ioqueue_recvfrom() - * - * Initiate overlapped RecvFrom() operation. - */ -PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - void *buffer, - pj_size_t buflen, - unsigned flags, - pj_sockaddr_t *addr, - int *addrlen) -{ - BOOL rc; - DWORD bytesRead; - DWORD dwFlags; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ioque); - - if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) { - pj_assert(!"Operation already pending for this socket"); - return PJ_EBUSY; - } - - pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped)); - key->recv_overlapped.operation = PJ_IOQUEUE_OP_RECV_FROM; - key->recv_overlapped.wsabuf.buf = buffer; - key->recv_overlapped.wsabuf.len = buflen; - dwFlags = flags; - rc = WSARecvFrom((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1, - &bytesRead, &dwFlags, - addr, addrlen, - &key->recv_overlapped.overlapped, NULL); - if (rc == SOCKET_ERROR) { - DWORD dwStatus = WSAGetLastError(); - if (dwStatus==WSA_IO_PENDING) - return PJ_EPENDING; - else - return PJ_STATUS_FROM_OS(dwStatus); - } else { - /* Must always return pending status. - * See comments on pj_ioqueue_read - * return bytesRead; - */ - return PJ_EPENDING; - } -} - -/* - * pj_ioqueue_write() - * - * Initiate overlapped WriteFile() operation. - */ -PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen) -{ - BOOL rc; - DWORD bytesWritten; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ioque); - - if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) { - pj_assert(!"Operation already pending for this descriptor"); - return PJ_EBUSY; - } - - pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped)); - key->send_overlapped.operation = PJ_IOQUEUE_OP_WRITE; - rc = WriteFile(key->hnd, data, datalen, &bytesWritten, - &key->send_overlapped.overlapped); - - if (rc == FALSE) { - DWORD dwStatus = GetLastError(); - if (dwStatus==ERROR_IO_PENDING) - return PJ_EPENDING; - else - return PJ_STATUS_FROM_OS(dwStatus); - } else { - /* Must always return pending status. - * See comments on pj_ioqueue_read - * return bytesWritten; - */ - return PJ_EPENDING; - } -} - - -/* - * pj_ioqueue_send() - * - * Initiate overlapped Send operation. - */ -PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags ) -{ - int rc; - DWORD bytesWritten; - DWORD dwFlags; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ioque); - - if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) { - pj_assert(!"Operation already pending for this socket"); - return PJ_EBUSY; - } - - pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped)); - key->send_overlapped.operation = PJ_IOQUEUE_OP_WRITE; - key->send_overlapped.wsabuf.buf = (void*)data; - key->send_overlapped.wsabuf.len = datalen; - dwFlags = flags; - rc = WSASend((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1, - &bytesWritten, dwFlags, - &key->send_overlapped.overlapped, NULL); - if (rc == SOCKET_ERROR) { - DWORD dwStatus = WSAGetLastError(); - if (dwStatus==WSA_IO_PENDING) - return PJ_EPENDING; - else - return PJ_STATUS_FROM_OS(dwStatus); - } else { - /* Must always return pending status. - * See comments on pj_ioqueue_read - * return bytesRead; - */ - return PJ_EPENDING; - } -} - - -/* - * pj_ioqueue_sendto() - * - * Initiate overlapped SendTo operation. - */ -PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, - pj_ioqueue_key_t *key, - const void *data, - pj_size_t datalen, - unsigned flags, - const pj_sockaddr_t *addr, - int addrlen) -{ - BOOL rc; - DWORD bytesSent; - DWORD dwFlags; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ioque); - - if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) { - pj_assert(!"Operation already pending for this socket"); - return PJ_EBUSY; - } - - pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped)); - key->send_overlapped.operation = PJ_IOQUEUE_OP_SEND_TO; - key->send_overlapped.wsabuf.buf = (char*)data; - key->send_overlapped.wsabuf.len = datalen; - dwFlags = flags; - rc = WSASendTo((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1, - &bytesSent, dwFlags, addr, - addrlen, &key->send_overlapped.overlapped, NULL); - if (rc == SOCKET_ERROR) { - DWORD dwStatus = WSAGetLastError(); - if (dwStatus==WSA_IO_PENDING) - return PJ_EPENDING; - else - return PJ_STATUS_FROM_OS(dwStatus); - } else { - // Must always return pending status. - // See comments on pj_ioqueue_read - // return bytesSent; - return PJ_EPENDING; - } -} - -#if PJ_HAS_TCP - -/* - * pj_ioqueue_accept() - * - * Initiate overlapped accept() operation. - */ -PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - pj_sock_t *new_sock, - pj_sockaddr_t *local, - pj_sockaddr_t *remote, - int *addrlen) -{ - BOOL rc; - DWORD bytesReceived; - pj_status_t status; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(ioqueue); - - if (key->accept_overlapped.operation != PJ_IOQUEUE_OP_NONE) { - pj_assert(!"Operation already pending for this socket"); - return PJ_EBUSY; - } - - if (key->accept_overlapped.newsock == PJ_INVALID_SOCKET) { - pj_sock_t sock; - status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &sock); - if (status != PJ_SUCCESS) - return status; - - key->accept_overlapped.newsock = sock; - } - key->accept_overlapped.operation = PJ_IOQUEUE_OP_ACCEPT; - key->accept_overlapped.addrlen = addrlen; - key->accept_overlapped.local = local; - key->accept_overlapped.remote = remote; - key->accept_overlapped.newsock_ptr = new_sock; - pj_memset(&key->accept_overlapped.overlapped, 0, - sizeof(key->accept_overlapped.overlapped)); - - rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)key->accept_overlapped.newsock, - key->accept_overlapped.accept_buf, - 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN, - &bytesReceived, - &key->accept_overlapped.overlapped); - - if (rc == TRUE) { - ioqueue_on_accept_complete(&key->accept_overlapped); - if (key->cb.on_accept_complete) - key->cb.on_accept_complete(key, key->accept_overlapped.newsock, 0); - return PJ_SUCCESS; - } else { - DWORD dwStatus = WSAGetLastError(); - if (dwStatus==WSA_IO_PENDING) - return PJ_EPENDING; - else - return PJ_STATUS_FROM_OS(dwStatus); - } -} - - -/* - * pj_ioqueue_connect() - * - * Initiate overlapped connect() operation (well, it's non-blocking actually, - * since there's no overlapped version of connect()). - */ -PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, - pj_ioqueue_key_t *key, - const pj_sockaddr_t *addr, - int addrlen ) -{ - unsigned long optval = 1; - HANDLE hEvent; - - PJ_CHECK_STACK(); - - /* Set socket to non-blocking. */ - if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) { - return PJ_RETURN_OS_ERROR(WSAGetLastError()); - } - - /* Initiate connect() */ - if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) { - DWORD dwStatus; - dwStatus = WSAGetLastError(); - if (dwStatus != WSAEWOULDBLOCK) { - /* Permanent error */ - return PJ_RETURN_OS_ERROR(dwStatus); - } else { - /* Pending operation. This is what we're looking for. */ - } - } else { - /* Connect has completed immediately! */ - /* Restore to blocking mode. */ - optval = 0; - if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) { - return PJ_RETURN_OS_ERROR(WSAGetLastError()); - } - - key->cb.on_connect_complete(key, 0); - return PJ_SUCCESS; - } - - /* Add to the array of connecting socket to be polled */ - pj_lock_acquire(ioqueue->lock); - - if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) { - pj_lock_release(ioqueue->lock); - return PJ_ETOOMANYCONN; - } - - /* Get or create event object. */ - if (ioqueue->event_count) { - hEvent = ioqueue->event_pool[ioqueue->event_count - 1]; - --ioqueue->event_count; - } else { - hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if (hEvent == NULL) { - DWORD dwStatus = GetLastError(); - pj_lock_release(ioqueue->lock); - return PJ_STATUS_FROM_OS(dwStatus); - } - } - - /* Mark key as connecting. - * We can't use array index since key can be removed dynamically. - */ - key->connecting = 1; - - /* Associate socket events to the event object. */ - if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) { - CloseHandle(hEvent); - pj_lock_release(ioqueue->lock); - return PJ_RETURN_OS_ERROR(WSAGetLastError()); - } - - /* Add to array. */ - ioqueue->connecting_keys[ ioqueue->connecting_count ] = key; - ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent; - ioqueue->connecting_count++; - - pj_lock_release(ioqueue->lock); - - return PJ_EPENDING; -} -#endif /* #if PJ_HAS_TCP */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/ioqueue_winnt.c 11 10/29/05 11:31a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/ioqueue_winnt.c $ + * + * 11 10/29/05 11:31a Bennylp + * Changed accept and lock. + * + * 10 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 9 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include +#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include +#endif + +#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0 +# include +#endif + + +#define ACCEPT_ADDR_LEN (sizeof(pj_sockaddr_in)+20) + +/* + * OVERLAP structure for send and receive. + */ +typedef struct ioqueue_overlapped +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; + WSABUF wsabuf; +} ioqueue_overlapped; + +#if PJ_HAS_TCP +/* + * OVERLAP structure for accept. + */ +typedef struct ioqueue_accept_rec +{ + WSAOVERLAPPED overlapped; + pj_ioqueue_operation_e operation; + pj_sock_t newsock; + pj_sock_t *newsock_ptr; + int *addrlen; + void *remote; + void *local; + char accept_buf[2 * ACCEPT_ADDR_LEN]; +} ioqueue_accept_rec; +#endif + +/* + * Structure for individual socket. + */ +struct pj_ioqueue_key_t +{ + HANDLE hnd; + void *user_data; + ioqueue_overlapped recv_overlapped; + ioqueue_overlapped send_overlapped; +#if PJ_HAS_TCP + int connecting; + ioqueue_accept_rec accept_overlapped; +#endif + pj_ioqueue_callback cb; +}; + +/* + * IO Queue structure. + */ +struct pj_ioqueue_t +{ + HANDLE iocp; + pj_lock_t *lock; + pj_bool_t auto_delete_lock; + unsigned event_count; + HANDLE event_pool[MAXIMUM_WAIT_OBJECTS+1]; +#if PJ_HAS_TCP + unsigned connecting_count; + HANDLE connecting_handles[MAXIMUM_WAIT_OBJECTS+1]; + pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1]; +#endif +}; + + +#if PJ_HAS_TCP +/* + * Process the socket when the overlapped accept() completed. + */ +static void ioqueue_on_accept_complete(ioqueue_accept_rec *accept_overlapped) +{ + struct sockaddr *local; + struct sockaddr *remote; + int locallen, remotelen; + + PJ_CHECK_STACK(); + + /* Operation complete immediately. */ + GetAcceptExSockaddrs( accept_overlapped->accept_buf, + 0, + ACCEPT_ADDR_LEN, + ACCEPT_ADDR_LEN, + &local, + &locallen, + &remote, + &remotelen); + pj_memcpy(accept_overlapped->local, local, locallen); + pj_memcpy(accept_overlapped->remote, remote, locallen); + *accept_overlapped->addrlen = locallen; + if (accept_overlapped->newsock_ptr) + *accept_overlapped->newsock_ptr = accept_overlapped->newsock; + accept_overlapped->operation = 0; + accept_overlapped->newsock = PJ_INVALID_SOCKET; +} + +static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos) +{ + pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos]; + HANDLE hEvent = ioqueue->connecting_handles[pos]; + unsigned long optval; + + /* Remove key from array of connecting handles. */ + pj_array_erase(ioqueue->connecting_keys, sizeof(key), + ioqueue->connecting_count, pos); + pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE), + ioqueue->connecting_count, pos); + --ioqueue->connecting_count; + + /* Disassociate the socket from the event. */ + WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0); + + /* Put event object to pool. */ + if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) { + ioqueue->event_pool[ioqueue->event_count++] = hEvent; + } else { + /* Shouldn't happen. There should be no more pending connections + * than max. + */ + pj_assert(0); + CloseHandle(hEvent); + } + + /* Set socket to blocking again. */ + optval = 0; + if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) { + DWORD dwStatus; + dwStatus = WSAGetLastError(); + } +} + +/* + * Poll for the completion of non-blocking connect(). + * If there's a completion, the function return the key of the completed + * socket, and 'result' argument contains the connect() result. If connect() + * succeeded, 'result' will have value zero, otherwise will have the error + * code. + */ +static pj_ioqueue_key_t *check_connecting( pj_ioqueue_t *ioqueue, + pj_ssize_t *connect_err ) +{ + pj_ioqueue_key_t *key = NULL; + + if (ioqueue->connecting_count) { + DWORD result; + + pj_lock_acquire(ioqueue->lock); + result = WaitForMultipleObjects(ioqueue->connecting_count, + ioqueue->connecting_handles, + FALSE, 0); + if (result >= WAIT_OBJECT_0 && + result < WAIT_OBJECT_0+ioqueue->connecting_count) + { + WSANETWORKEVENTS net_events; + + /* Got completed connect(). */ + unsigned pos = result - WAIT_OBJECT_0; + key = ioqueue->connecting_keys[pos]; + + /* See whether connect has succeeded. */ + WSAEnumNetworkEvents((pj_sock_t)key->hnd, + ioqueue->connecting_handles[pos], + &net_events); + *connect_err = net_events.iErrorCode[FD_CONNECT_BIT]; + + /* Erase socket from pending connect. */ + erase_connecting_socket(ioqueue, pos); + } + pj_lock_release(ioqueue->lock); + } + return key; +} +#endif + + +PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, + pj_size_t max_fd, + int max_threads, + pj_ioqueue_t **ioqueue) +{ + pj_ioqueue_t *ioq; + pj_status_t rc; + + PJ_UNUSED_ARG(max_fd); + PJ_ASSERT_RETURN(pool && ioqueue, PJ_EINVAL); + + ioq = pj_pool_zalloc(pool, sizeof(*ioq)); + ioq->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, max_threads); + if (ioq->iocp == NULL) + return PJ_RETURN_OS_ERROR(GetLastError()); + + rc = pj_lock_create_simple_mutex(pool, NULL, &ioq->lock); + if (rc != PJ_SUCCESS) { + CloseHandle(ioq->iocp); + return rc; + } + + ioq->auto_delete_lock = PJ_TRUE; + + *ioqueue = ioq; + + PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioq)); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioque ) +{ + unsigned i; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(ioque, PJ_EINVAL); + + /* Destroy events in the pool */ + for (i=0; ievent_count; ++i) { + CloseHandle(ioque->event_pool[i]); + } + ioque->event_count = 0; + + if (ioque->auto_delete_lock) + pj_lock_destroy(ioque->lock); + + if (CloseHandle(ioque->iocp) == TRUE) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, + pj_lock_t *lock, + pj_bool_t auto_delete ) +{ + PJ_ASSERT_RETURN(ioque && lock, PJ_EINVAL); + + if (ioque->auto_delete_lock) { + pj_lock_destroy(ioque->lock); + } + + ioque->lock = lock; + ioque->auto_delete_lock = auto_delete; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool, + pj_ioqueue_t *ioque, + pj_sock_t hnd, + void *user_data, + const pj_ioqueue_callback *cb, + pj_ioqueue_key_t **key ) +{ + HANDLE hioq; + pj_ioqueue_key_t *rec; + + PJ_ASSERT_RETURN(pool && ioque && cb && key, PJ_EINVAL); + + rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t)); + rec->hnd = (HANDLE)hnd; + rec->user_data = user_data; + pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback)); +#if PJ_HAS_TCP + rec->accept_overlapped.newsock = PJ_INVALID_SOCKET; +#endif + hioq = CreateIoCompletionPort((HANDLE)hnd, ioque->iocp, (DWORD)rec, 0); + if (!hioq) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } + + *key = rec; + return PJ_SUCCESS; +} + + + +PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(ioque && key, PJ_EINVAL); + +#if PJ_HAS_TCP + if (key->connecting) { + unsigned pos; + + /* Erase from connecting_handles */ + pj_lock_acquire(ioque->lock); + for (pos=0; pos < ioque->connecting_count; ++pos) { + if (ioque->connecting_keys[pos] == key) { + erase_connecting_socket(ioque, pos); + if (key->accept_overlapped.newsock_ptr) { + /* ??? shouldn't it be newsock instead of newsock_ptr??? */ + closesocket(*key->accept_overlapped.newsock_ptr); + } + break; + } + } + pj_lock_release(ioque->lock); + key->connecting = 0; + } +#endif + return PJ_SUCCESS; +} + +PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key ) +{ + PJ_ASSERT_RETURN(key, NULL); + return key->user_data; +} + +/* + * Poll for events. + */ +PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout) +{ + DWORD dwMsec, dwBytesTransfered, dwKey; + ioqueue_overlapped *ov; + pj_ioqueue_key_t *key; + pj_ssize_t size_status; + BOOL rc; + + PJ_ASSERT_RETURN(ioque, -PJ_EINVAL); + + /* Check the connecting array. */ +#if PJ_HAS_TCP + key = check_connecting(ioque, &size_status); + if (key != NULL) { + key->cb.on_connect_complete(key, (int)size_status); + return 1; + } +#endif + + /* Calculate miliseconds timeout for GetQueuedCompletionStatus */ + dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE; + + /* Poll for completion status. */ + rc = GetQueuedCompletionStatus(ioque->iocp, &dwBytesTransfered, &dwKey, + (OVERLAPPED**)&ov, dwMsec); + + /* The return value is: + * - nonzero if event was dequeued. + * - zero and ov==NULL if no event was dequeued. + * - zero and ov!=NULL if event for failed I/O was dequeued. + */ + if (ov) { + /* Event was dequeued for either successfull or failed I/O */ + key = (pj_ioqueue_key_t*)dwKey; + size_status = dwBytesTransfered; + switch (ov->operation) { + case PJ_IOQUEUE_OP_READ: + case PJ_IOQUEUE_OP_RECV: + case PJ_IOQUEUE_OP_RECV_FROM: + key->recv_overlapped.operation = 0; + if (key->cb.on_read_complete) + key->cb.on_read_complete(key, size_status); + break; + case PJ_IOQUEUE_OP_WRITE: + case PJ_IOQUEUE_OP_SEND: + case PJ_IOQUEUE_OP_SEND_TO: + key->send_overlapped.operation = 0; + if (key->cb.on_write_complete) + key->cb.on_write_complete(key, size_status); + break; +#if PJ_HAS_TCP + case PJ_IOQUEUE_OP_ACCEPT: + /* special case for accept. */ + ioqueue_on_accept_complete((ioqueue_accept_rec*)ov); + if (key->cb.on_accept_complete) + key->cb.on_accept_complete(key, key->accept_overlapped.newsock, + 0); + break; + case PJ_IOQUEUE_OP_CONNECT: +#endif + case PJ_IOQUEUE_OP_NONE: + pj_assert(0); + break; + } + return 1; + } + + if (GetLastError()==WAIT_TIMEOUT) { + /* Check the connecting array. */ +#if PJ_HAS_TCP + key = check_connecting(ioque, &size_status); + if (key != NULL) { + key->cb.on_connect_complete(key, (int)size_status); + return 1; + } +#endif + return 0; + } + return -1; +} + +/* + * pj_ioqueue_read() + * + * Initiate overlapped ReadFile operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen) +{ + BOOL rc; + DWORD bytesRead; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ioque); + + if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) { + pj_assert(!"Operation already pending for this descriptor"); + return PJ_EBUSY; + } + + pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped)); + key->recv_overlapped.operation = PJ_IOQUEUE_OP_READ; + + rc = ReadFile(key->hnd, buffer, buflen, &bytesRead, + &key->recv_overlapped.overlapped); + if (rc == FALSE) { + DWORD dwStatus = GetLastError(); + if (dwStatus==ERROR_IO_PENDING) + return PJ_EPENDING; + else + return PJ_STATUS_FROM_OS(dwStatus); + } else { + /* + * This is workaround to a probable bug in Win2000 (probably NT too). + * Even if 'rc' is TRUE, which indicates operation has completed, + * GetQueuedCompletionStatus still will return the key. + * So as work around, we always return PJ_EPENDING here. + */ + return PJ_EPENDING; + } +} + +/* + * pj_ioqueue_recv() + * + * Initiate overlapped WSARecv() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags ) +{ + int rc; + DWORD bytesRead; + DWORD dwFlags = 0; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ioque); + + if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) { + pj_assert(!"Operation already pending for this socket"); + return PJ_EBUSY; + } + + pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped)); + key->recv_overlapped.operation = PJ_IOQUEUE_OP_READ; + + key->recv_overlapped.wsabuf.buf = buffer; + key->recv_overlapped.wsabuf.len = buflen; + + dwFlags = flags; + + rc = WSARecv((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1, + &bytesRead, &dwFlags, + &key->recv_overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus==WSA_IO_PENDING) + return PJ_EPENDING; + else + return PJ_STATUS_FROM_OS(dwStatus); + } else { + /* Must always return pending status. + * See comments on pj_ioqueue_read + * return bytesRead; + */ + return PJ_EPENDING; + } +} + +/* + * pj_ioqueue_recvfrom() + * + * Initiate overlapped RecvFrom() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + void *buffer, + pj_size_t buflen, + unsigned flags, + pj_sockaddr_t *addr, + int *addrlen) +{ + BOOL rc; + DWORD bytesRead; + DWORD dwFlags; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ioque); + + if (key->recv_overlapped.operation != PJ_IOQUEUE_OP_NONE) { + pj_assert(!"Operation already pending for this socket"); + return PJ_EBUSY; + } + + pj_memset(&key->recv_overlapped, 0, sizeof(key->recv_overlapped)); + key->recv_overlapped.operation = PJ_IOQUEUE_OP_RECV_FROM; + key->recv_overlapped.wsabuf.buf = buffer; + key->recv_overlapped.wsabuf.len = buflen; + dwFlags = flags; + rc = WSARecvFrom((SOCKET)key->hnd, &key->recv_overlapped.wsabuf, 1, + &bytesRead, &dwFlags, + addr, addrlen, + &key->recv_overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus==WSA_IO_PENDING) + return PJ_EPENDING; + else + return PJ_STATUS_FROM_OS(dwStatus); + } else { + /* Must always return pending status. + * See comments on pj_ioqueue_read + * return bytesRead; + */ + return PJ_EPENDING; + } +} + +/* + * pj_ioqueue_write() + * + * Initiate overlapped WriteFile() operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen) +{ + BOOL rc; + DWORD bytesWritten; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ioque); + + if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) { + pj_assert(!"Operation already pending for this descriptor"); + return PJ_EBUSY; + } + + pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped)); + key->send_overlapped.operation = PJ_IOQUEUE_OP_WRITE; + rc = WriteFile(key->hnd, data, datalen, &bytesWritten, + &key->send_overlapped.overlapped); + + if (rc == FALSE) { + DWORD dwStatus = GetLastError(); + if (dwStatus==ERROR_IO_PENDING) + return PJ_EPENDING; + else + return PJ_STATUS_FROM_OS(dwStatus); + } else { + /* Must always return pending status. + * See comments on pj_ioqueue_read + * return bytesWritten; + */ + return PJ_EPENDING; + } +} + + +/* + * pj_ioqueue_send() + * + * Initiate overlapped Send operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags ) +{ + int rc; + DWORD bytesWritten; + DWORD dwFlags; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ioque); + + if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) { + pj_assert(!"Operation already pending for this socket"); + return PJ_EBUSY; + } + + pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped)); + key->send_overlapped.operation = PJ_IOQUEUE_OP_WRITE; + key->send_overlapped.wsabuf.buf = (void*)data; + key->send_overlapped.wsabuf.len = datalen; + dwFlags = flags; + rc = WSASend((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1, + &bytesWritten, dwFlags, + &key->send_overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus==WSA_IO_PENDING) + return PJ_EPENDING; + else + return PJ_STATUS_FROM_OS(dwStatus); + } else { + /* Must always return pending status. + * See comments on pj_ioqueue_read + * return bytesRead; + */ + return PJ_EPENDING; + } +} + + +/* + * pj_ioqueue_sendto() + * + * Initiate overlapped SendTo operation. + */ +PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque, + pj_ioqueue_key_t *key, + const void *data, + pj_size_t datalen, + unsigned flags, + const pj_sockaddr_t *addr, + int addrlen) +{ + BOOL rc; + DWORD bytesSent; + DWORD dwFlags; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ioque); + + if (key->send_overlapped.operation != PJ_IOQUEUE_OP_NONE) { + pj_assert(!"Operation already pending for this socket"); + return PJ_EBUSY; + } + + pj_memset(&key->send_overlapped, 0, sizeof(key->send_overlapped)); + key->send_overlapped.operation = PJ_IOQUEUE_OP_SEND_TO; + key->send_overlapped.wsabuf.buf = (char*)data; + key->send_overlapped.wsabuf.len = datalen; + dwFlags = flags; + rc = WSASendTo((SOCKET)key->hnd, &key->send_overlapped.wsabuf, 1, + &bytesSent, dwFlags, addr, + addrlen, &key->send_overlapped.overlapped, NULL); + if (rc == SOCKET_ERROR) { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus==WSA_IO_PENDING) + return PJ_EPENDING; + else + return PJ_STATUS_FROM_OS(dwStatus); + } else { + // Must always return pending status. + // See comments on pj_ioqueue_read + // return bytesSent; + return PJ_EPENDING; + } +} + +#if PJ_HAS_TCP + +/* + * pj_ioqueue_accept() + * + * Initiate overlapped accept() operation. + */ +PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + pj_sock_t *new_sock, + pj_sockaddr_t *local, + pj_sockaddr_t *remote, + int *addrlen) +{ + BOOL rc; + DWORD bytesReceived; + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(ioqueue); + + if (key->accept_overlapped.operation != PJ_IOQUEUE_OP_NONE) { + pj_assert(!"Operation already pending for this socket"); + return PJ_EBUSY; + } + + if (key->accept_overlapped.newsock == PJ_INVALID_SOCKET) { + pj_sock_t sock; + status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &sock); + if (status != PJ_SUCCESS) + return status; + + key->accept_overlapped.newsock = sock; + } + key->accept_overlapped.operation = PJ_IOQUEUE_OP_ACCEPT; + key->accept_overlapped.addrlen = addrlen; + key->accept_overlapped.local = local; + key->accept_overlapped.remote = remote; + key->accept_overlapped.newsock_ptr = new_sock; + pj_memset(&key->accept_overlapped.overlapped, 0, + sizeof(key->accept_overlapped.overlapped)); + + rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)key->accept_overlapped.newsock, + key->accept_overlapped.accept_buf, + 0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN, + &bytesReceived, + &key->accept_overlapped.overlapped); + + if (rc == TRUE) { + ioqueue_on_accept_complete(&key->accept_overlapped); + if (key->cb.on_accept_complete) + key->cb.on_accept_complete(key, key->accept_overlapped.newsock, 0); + return PJ_SUCCESS; + } else { + DWORD dwStatus = WSAGetLastError(); + if (dwStatus==WSA_IO_PENDING) + return PJ_EPENDING; + else + return PJ_STATUS_FROM_OS(dwStatus); + } +} + + +/* + * pj_ioqueue_connect() + * + * Initiate overlapped connect() operation (well, it's non-blocking actually, + * since there's no overlapped version of connect()). + */ +PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue, + pj_ioqueue_key_t *key, + const pj_sockaddr_t *addr, + int addrlen ) +{ + unsigned long optval = 1; + HANDLE hEvent; + + PJ_CHECK_STACK(); + + /* Set socket to non-blocking. */ + if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) { + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Initiate connect() */ + if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) { + DWORD dwStatus; + dwStatus = WSAGetLastError(); + if (dwStatus != WSAEWOULDBLOCK) { + /* Permanent error */ + return PJ_RETURN_OS_ERROR(dwStatus); + } else { + /* Pending operation. This is what we're looking for. */ + } + } else { + /* Connect has completed immediately! */ + /* Restore to blocking mode. */ + optval = 0; + if (ioctlsocket((pj_sock_t)key->hnd, FIONBIO, &optval) != 0) { + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + key->cb.on_connect_complete(key, 0); + return PJ_SUCCESS; + } + + /* Add to the array of connecting socket to be polled */ + pj_lock_acquire(ioqueue->lock); + + if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) { + pj_lock_release(ioqueue->lock); + return PJ_ETOOMANYCONN; + } + + /* Get or create event object. */ + if (ioqueue->event_count) { + hEvent = ioqueue->event_pool[ioqueue->event_count - 1]; + --ioqueue->event_count; + } else { + hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (hEvent == NULL) { + DWORD dwStatus = GetLastError(); + pj_lock_release(ioqueue->lock); + return PJ_STATUS_FROM_OS(dwStatus); + } + } + + /* Mark key as connecting. + * We can't use array index since key can be removed dynamically. + */ + key->connecting = 1; + + /* Associate socket events to the event object. */ + if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) { + CloseHandle(hEvent); + pj_lock_release(ioqueue->lock); + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Add to array. */ + ioqueue->connecting_keys[ ioqueue->connecting_count ] = key; + ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent; + ioqueue->connecting_count++; + + pj_lock_release(ioqueue->lock); + + return PJ_EPENDING; +} +#endif /* #if PJ_HAS_TCP */ + diff --git a/pjlib/src/pj/list.c b/pjlib/src/pj/list.c index 82b9e83a0..eef10f5d1 100644 --- a/pjlib/src/pj/list.c +++ b/pjlib/src/pj/list.c @@ -1,18 +1,18 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/list.c 5 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/list.c $ - * - * 5 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 4 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include - -#if !PJ_FUNCTIONS_ARE_INLINED -# include -#endif - - +/* $Header: /pjproject-0.3/pjlib/src/pj/list.c 5 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/list.c $ + * + * 5 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 4 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include + +#if !PJ_FUNCTIONS_ARE_INLINED +# include +#endif + + diff --git a/pjlib/src/pj/lock.c b/pjlib/src/pj/lock.c index 10b967a82..69d587d50 100644 --- a/pjlib/src/pj/lock.c +++ b/pjlib/src/pj/lock.c @@ -1,190 +1,190 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/lock.c 3 10/29/05 11:51a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/lock.c $ - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/05/05 6:35p Bennylp - * Created. - */ -#include -#include -#include -#include -#include -#include - - -typedef void LOCK_OBJ; - -/* - * Lock structure. - */ -struct pj_lock_t -{ - LOCK_OBJ *lock_object; - - pj_status_t (*acquire) (LOCK_OBJ*); - pj_status_t (*tryacquire) (LOCK_OBJ*); - pj_status_t (*release) (LOCK_OBJ*); - pj_status_t (*destroy) (LOCK_OBJ*); -}; - -typedef pj_status_t (*FPTR)(LOCK_OBJ*); - -/****************************************************************************** - * Implementation of lock object with mutex. - */ -static pj_lock_t mutex_lock_template = -{ - NULL, - (FPTR) &pj_mutex_lock, - (FPTR) &pj_mutex_trylock, - (FPTR) &pj_mutex_unlock, - (FPTR) &pj_mutex_destroy -}; - -static pj_status_t create_mutex_lock( pj_pool_t *pool, - const char *name, - int type, - pj_lock_t **lock ) -{ - pj_lock_t *p_lock; - pj_status_t rc; - - PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); - - p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t)); - if (!p_lock) - return PJ_ENOMEM; - - pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t)); - rc = pj_mutex_create(pool, name, type, (pj_mutex_t**)&p_lock->lock_object); - if (rc != PJ_SUCCESS) - return rc; - - *lock = p_lock; - return PJ_SUCCESS; -} - - -PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, - const char *name, - pj_lock_t **lock ) -{ - return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock); -} - -PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, - const char *name, - pj_lock_t **lock ) -{ - return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock); -} - - -/****************************************************************************** - * Implementation of NULL lock object. - */ -static pj_status_t null_op(void *arg) -{ - PJ_UNUSED_ARG(arg); - return PJ_SUCCESS; -} - -static pj_lock_t null_lock_template = -{ - NULL, - &null_op, - &null_op, - &null_op, - &null_op -}; - -PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, - const char *name, - pj_lock_t **lock ) -{ - PJ_UNUSED_ARG(name); - PJ_UNUSED_ARG(pool); - - PJ_ASSERT_RETURN(lock, PJ_EINVAL); - - *lock = &null_lock_template; - return PJ_SUCCESS; -} - - -/****************************************************************************** - * Implementation of semaphore lock object. - */ -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 - -static pj_lock_t sem_lock_template = -{ - NULL, - (FPTR) &pj_sem_wait, - (FPTR) &pj_sem_trywait, - (FPTR) &pj_sem_post, - (FPTR) &pj_sem_destroy -}; - -PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, - const char *name, - unsigned initial, - unsigned max, - pj_lock_t **lock ) -{ - pj_lock_t *p_lock; - pj_status_t rc; - - PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); - - p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t)); - if (!p_lock) - return PJ_ENOMEM; - - pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t)); - rc = pj_sem_create( pool, name, initial, max, - (pj_sem_t**)&p_lock->lock_object); - if (rc != PJ_SUCCESS) - return rc; - - *lock = p_lock; - - return PJ_SUCCESS; -} - - -#endif /* PJ_HAS_SEMAPHORE */ - - -PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock ) -{ - PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); - return (*lock->acquire)(lock->lock_object); -} - -PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ) -{ - PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); - return (*lock->tryacquire)(lock->lock_object); -} - -PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock ) -{ - PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); - return (*lock->release)(lock->lock_object); -} - -PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock ) -{ - PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); - return (*lock->destroy)(lock->lock_object); -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/lock.c 3 10/29/05 11:51a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/lock.c $ + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/05/05 6:35p Bennylp + * Created. + */ +#include +#include +#include +#include +#include +#include + + +typedef void LOCK_OBJ; + +/* + * Lock structure. + */ +struct pj_lock_t +{ + LOCK_OBJ *lock_object; + + pj_status_t (*acquire) (LOCK_OBJ*); + pj_status_t (*tryacquire) (LOCK_OBJ*); + pj_status_t (*release) (LOCK_OBJ*); + pj_status_t (*destroy) (LOCK_OBJ*); +}; + +typedef pj_status_t (*FPTR)(LOCK_OBJ*); + +/****************************************************************************** + * Implementation of lock object with mutex. + */ +static pj_lock_t mutex_lock_template = +{ + NULL, + (FPTR) &pj_mutex_lock, + (FPTR) &pj_mutex_trylock, + (FPTR) &pj_mutex_unlock, + (FPTR) &pj_mutex_destroy +}; + +static pj_status_t create_mutex_lock( pj_pool_t *pool, + const char *name, + int type, + pj_lock_t **lock ) +{ + pj_lock_t *p_lock; + pj_status_t rc; + + PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); + + p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t)); + if (!p_lock) + return PJ_ENOMEM; + + pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t)); + rc = pj_mutex_create(pool, name, type, (pj_mutex_t**)&p_lock->lock_object); + if (rc != PJ_SUCCESS) + return rc; + + *lock = p_lock; + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock); +} + +PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock); +} + + +/****************************************************************************** + * Implementation of NULL lock object. + */ +static pj_status_t null_op(void *arg) +{ + PJ_UNUSED_ARG(arg); + return PJ_SUCCESS; +} + +static pj_lock_t null_lock_template = +{ + NULL, + &null_op, + &null_op, + &null_op, + &null_op +}; + +PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool, + const char *name, + pj_lock_t **lock ) +{ + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(pool); + + PJ_ASSERT_RETURN(lock, PJ_EINVAL); + + *lock = &null_lock_template; + return PJ_SUCCESS; +} + + +/****************************************************************************** + * Implementation of semaphore lock object. + */ +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +static pj_lock_t sem_lock_template = +{ + NULL, + (FPTR) &pj_sem_wait, + (FPTR) &pj_sem_trywait, + (FPTR) &pj_sem_post, + (FPTR) &pj_sem_destroy +}; + +PJ_DEF(pj_status_t) pj_lock_create_semaphore( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_lock_t **lock ) +{ + pj_lock_t *p_lock; + pj_status_t rc; + + PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL); + + p_lock = pj_pool_alloc(pool, sizeof(pj_lock_t)); + if (!p_lock) + return PJ_ENOMEM; + + pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t)); + rc = pj_sem_create( pool, name, initial, max, + (pj_sem_t**)&p_lock->lock_object); + if (rc != PJ_SUCCESS) + return rc; + + *lock = p_lock; + + return PJ_SUCCESS; +} + + +#endif /* PJ_HAS_SEMAPHORE */ + + +PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->acquire)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->tryacquire)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->release)(lock->lock_object); +} + +PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock ) +{ + PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL); + return (*lock->destroy)(lock->lock_object); +} + diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c index 7f79e55c6..59d58b2cb 100644 --- a/pjlib/src/pj/log.c +++ b/pjlib/src/pj/log.c @@ -1,217 +1,217 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/log.c 7 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/log.c $ - * - * 7 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 6 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include - -#if PJ_LOG_MAX_LEVEL >= 1 - -static int log_max_level = PJ_LOG_MAX_LEVEL; -static pj_log_func *log_writer = &pj_log_write; -static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | - PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE; - -#if PJ_LOG_USE_STACK_BUFFER==0 -static char log_buffer[PJ_LOG_MAX_SIZE]; -#endif - -PJ_DEF(void) pj_log_set_decor(unsigned decor) -{ - log_decor = decor; -} - -PJ_DEF(unsigned) pj_log_get_decor(void) -{ - return log_decor; -} - -PJ_DEF(void) pj_log_set_level(int level) -{ - log_max_level = level; -} - -PJ_DEF(int) pj_log_get_level(void) -{ - return log_max_level; -} - -PJ_DEF(void) pj_log_set_log_func( pj_log_func *func ) -{ - log_writer = func; -} - -PJ_DEF(pj_log_func*) pj_log_get_log_func(void) -{ - return log_writer; -} - -static void pj_log(const char *sender, int level, - const char *format, va_list marker) -{ - pj_time_val now; - pj_parsed_time ptime; - char *pre; -#if PJ_LOG_USE_STACK_BUFFER - char log_buffer[PJ_LOG_MAX_SIZE]; -#endif - int len; - - PJ_CHECK_STACK(); - - if (level > log_max_level) - return; - - /* Get current date/time. */ - pj_gettimeofday(&now); - pj_time_decode(&now, &ptime); - - pre = log_buffer; - if (log_decor & PJ_LOG_HAS_DAY_NAME) { - static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat"}; - strcpy(pre, wdays[ptime.wday]); - pre += 3; - } - if (log_decor & PJ_LOG_HAS_YEAR) { - *pre++ = ' '; - pre += pj_utoa(ptime.year, pre); - } - if (log_decor & PJ_LOG_HAS_MONTH) { - *pre++ = '-'; - pre += pj_utoa_pad(ptime.mon, pre, 2, '0'); - } - if (log_decor & PJ_LOG_HAS_DAY_OF_MON) { - *pre++ = ' '; - pre += pj_utoa_pad(ptime.day, pre, 2, '0'); - } - if (log_decor & PJ_LOG_HAS_TIME) { - *pre++ = ' '; - pre += pj_utoa_pad(ptime.hour, pre, 2, '0'); - *pre++ = ':'; - pre += pj_utoa_pad(ptime.min, pre, 2, '0'); - *pre++ = ':'; - pre += pj_utoa_pad(ptime.sec, pre, 2, '0'); - } - if (log_decor & PJ_LOG_HAS_MICRO_SEC) { - *pre++ = '.'; - pre += pj_utoa_pad(ptime.msec, pre, 3, '0'); - } - if (log_decor & PJ_LOG_HAS_SENDER) { - enum { SENDER_WIDTH = 12 }; - int sender_len = strlen(sender); - *pre++ = ' '; - if (sender_len <= SENDER_WIDTH) { - while (sender_len < SENDER_WIDTH) - *pre++ = ' ', ++sender_len; - while (*sender) - *pre++ = *sender++; - } else { - int i; - for (i=0; i 0 && len < sizeof(log_buffer)-1) { - if (log_decor & PJ_LOG_HAS_NEWLINE) { - log_buffer[len++] = '\n'; - } - log_buffer[len++] = '\0'; - } else { - len = sizeof(log_buffer)-1; - if (log_decor & PJ_LOG_HAS_NEWLINE) { - log_buffer[sizeof(log_buffer)-2] = '\n'; - } - log_buffer[sizeof(log_buffer)-1] = '\0'; - } - - if (log_writer) - (*log_writer)(level, log_buffer, len); -} - -PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - pj_log(obj, 0, format, arg); - va_end(arg); -} - -PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - pj_log(obj, 1, format, arg); - va_end(arg); -} -#endif /* PJ_LOG_MAX_LEVEL >= 1 */ - -#if PJ_LOG_MAX_LEVEL >= 2 -PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - pj_log(obj, 2, format, arg); - va_end(arg); -} -#endif - -#if PJ_LOG_MAX_LEVEL >= 3 -PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - pj_log(obj, 3, format, arg); - va_end(arg); -} -#endif - -#if PJ_LOG_MAX_LEVEL >= 4 -PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - pj_log(obj, 4, format, arg); - va_end(arg); -} -#endif - -#if PJ_LOG_MAX_LEVEL >= 5 -PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - pj_log(obj, 5, format, arg); - va_end(arg); -} -#endif - -#if PJ_LOG_MAX_LEVEL >= 6 -PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...) -{ - va_list arg; - va_start(arg, format); - pj_log(obj, 6, format, arg); - va_end(arg); -} -#endif - +/* $Header: /pjproject-0.3/pjlib/src/pj/log.c 7 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/log.c $ + * + * 7 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 6 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include + +#if PJ_LOG_MAX_LEVEL >= 1 + +static int log_max_level = PJ_LOG_MAX_LEVEL; +static pj_log_func *log_writer = &pj_log_write; +static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC | + PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE; + +#if PJ_LOG_USE_STACK_BUFFER==0 +static char log_buffer[PJ_LOG_MAX_SIZE]; +#endif + +PJ_DEF(void) pj_log_set_decor(unsigned decor) +{ + log_decor = decor; +} + +PJ_DEF(unsigned) pj_log_get_decor(void) +{ + return log_decor; +} + +PJ_DEF(void) pj_log_set_level(int level) +{ + log_max_level = level; +} + +PJ_DEF(int) pj_log_get_level(void) +{ + return log_max_level; +} + +PJ_DEF(void) pj_log_set_log_func( pj_log_func *func ) +{ + log_writer = func; +} + +PJ_DEF(pj_log_func*) pj_log_get_log_func(void) +{ + return log_writer; +} + +static void pj_log(const char *sender, int level, + const char *format, va_list marker) +{ + pj_time_val now; + pj_parsed_time ptime; + char *pre; +#if PJ_LOG_USE_STACK_BUFFER + char log_buffer[PJ_LOG_MAX_SIZE]; +#endif + int len; + + PJ_CHECK_STACK(); + + if (level > log_max_level) + return; + + /* Get current date/time. */ + pj_gettimeofday(&now); + pj_time_decode(&now, &ptime); + + pre = log_buffer; + if (log_decor & PJ_LOG_HAS_DAY_NAME) { + static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + strcpy(pre, wdays[ptime.wday]); + pre += 3; + } + if (log_decor & PJ_LOG_HAS_YEAR) { + *pre++ = ' '; + pre += pj_utoa(ptime.year, pre); + } + if (log_decor & PJ_LOG_HAS_MONTH) { + *pre++ = '-'; + pre += pj_utoa_pad(ptime.mon, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_DAY_OF_MON) { + *pre++ = ' '; + pre += pj_utoa_pad(ptime.day, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_TIME) { + *pre++ = ' '; + pre += pj_utoa_pad(ptime.hour, pre, 2, '0'); + *pre++ = ':'; + pre += pj_utoa_pad(ptime.min, pre, 2, '0'); + *pre++ = ':'; + pre += pj_utoa_pad(ptime.sec, pre, 2, '0'); + } + if (log_decor & PJ_LOG_HAS_MICRO_SEC) { + *pre++ = '.'; + pre += pj_utoa_pad(ptime.msec, pre, 3, '0'); + } + if (log_decor & PJ_LOG_HAS_SENDER) { + enum { SENDER_WIDTH = 12 }; + int sender_len = strlen(sender); + *pre++ = ' '; + if (sender_len <= SENDER_WIDTH) { + while (sender_len < SENDER_WIDTH) + *pre++ = ' ', ++sender_len; + while (*sender) + *pre++ = *sender++; + } else { + int i; + for (i=0; i 0 && len < sizeof(log_buffer)-1) { + if (log_decor & PJ_LOG_HAS_NEWLINE) { + log_buffer[len++] = '\n'; + } + log_buffer[len++] = '\0'; + } else { + len = sizeof(log_buffer)-1; + if (log_decor & PJ_LOG_HAS_NEWLINE) { + log_buffer[sizeof(log_buffer)-2] = '\n'; + } + log_buffer[sizeof(log_buffer)-1] = '\0'; + } + + if (log_writer) + (*log_writer)(level, log_buffer, len); +} + +PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 0, format, arg); + va_end(arg); +} + +PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 1, format, arg); + va_end(arg); +} +#endif /* PJ_LOG_MAX_LEVEL >= 1 */ + +#if PJ_LOG_MAX_LEVEL >= 2 +PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 2, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 3 +PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 3, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 4 +PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 4, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 5 +PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 5, format, arg); + va_end(arg); +} +#endif + +#if PJ_LOG_MAX_LEVEL >= 6 +PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...) +{ + va_list arg; + va_start(arg, format); + pj_log(obj, 6, format, arg); + va_end(arg); +} +#endif + diff --git a/pjlib/src/pj/log_writer_printk.c b/pjlib/src/pj/log_writer_printk.c index b18a30274..3f535c6bf 100644 --- a/pjlib/src/pj/log_writer_printk.c +++ b/pjlib/src/pj/log_writer_printk.c @@ -1,20 +1,20 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/log_writer_printk.c 2 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/log_writer_printk.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/22/05 10:37a Bennylp - * Created. - * - */ -#include -#include - -PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) -{ - PJ_CHECK_STACK(); - printk(KERN_INFO "%s", buffer); -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/log_writer_printk.c 2 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/log_writer_printk.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/22/05 10:37a Bennylp + * Created. + * + */ +#include +#include + +PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) +{ + PJ_CHECK_STACK(); + printk(KERN_INFO "%s", buffer); +} + diff --git a/pjlib/src/pj/log_writer_stdout.c b/pjlib/src/pj/log_writer_stdout.c index 30a7a6f1b..abaeb6d09 100644 --- a/pjlib/src/pj/log_writer_stdout.c +++ b/pjlib/src/pj/log_writer_stdout.c @@ -1,66 +1,66 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/log_writer_stdout.c 5 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/log_writer_stdout.c $ - * - * 5 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 4 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 3 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include - -#define CLR_FATAL (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R) -#define CLR_WARNING (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G) -#define CLR_INFO (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \ - PJ_TERM_COLOR_B) -#define CLR_DEFAULT (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B) - -static void term_set_color(int level) -{ -#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 - unsigned attr = 0; - switch (level) { - case 0: - case 1: attr = CLR_FATAL; - break; - case 2: attr = CLR_WARNING; - break; - case 3: attr = CLR_INFO; - break; - default: - attr = CLR_DEFAULT; - break; - } - - pj_term_set_color(attr); -#endif -} - -static void term_restore_color(void) -{ -#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 - pj_term_set_color(CLR_DEFAULT); -#endif -} - - -PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(len); - - /* Copy to terminal/file. */ - term_set_color(level); - fputs(buffer, stdout); - term_restore_color(); - - fflush(stdout); -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/log_writer_stdout.c 5 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/log_writer_stdout.c $ + * + * 5 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 4 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 3 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include + +#define CLR_FATAL (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R) +#define CLR_WARNING (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G) +#define CLR_INFO (PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | \ + PJ_TERM_COLOR_B) +#define CLR_DEFAULT (PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B) + +static void term_set_color(int level) +{ +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + unsigned attr = 0; + switch (level) { + case 0: + case 1: attr = CLR_FATAL; + break; + case 2: attr = CLR_WARNING; + break; + case 3: attr = CLR_INFO; + break; + default: + attr = CLR_DEFAULT; + break; + } + + pj_term_set_color(attr); +#endif +} + +static void term_restore_color(void) +{ +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 + pj_term_set_color(CLR_DEFAULT); +#endif +} + + +PJ_DEF(void) pj_log_write(int level, const char *buffer, int len) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(len); + + /* Copy to terminal/file. */ + term_set_color(level); + fputs(buffer, stdout); + term_restore_color(); + + fflush(stdout); +} + diff --git a/pjlib/src/pj/md5.c b/pjlib/src/pj/md5.c index 3c42b4ede..c796ce8c0 100644 --- a/pjlib/src/pj/md5.c +++ b/pjlib/src/pj/md5.c @@ -1,404 +1,404 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/md5.c 5 10/14/05 12:26a Bennylp $ */ -/* - Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.c is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order - either statically or dynamically; added missing #include - in library. - 2002-03-11 lpd Corrected argument list for main(), and added int return - type, in test program and T value program. - 2002-02-21 lpd Added missing #include in test program. - 2000-07-03 lpd Patched to eliminate warnings about "constant is - unsigned in ANSI C, signed in traditional"; made test program - self-checking. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). - 1999-05-03 lpd Original version. - */ - -#include -#include -#include - -#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ - -/* -#ifdef ARCH_IS_BIG_ENDIAN -# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) -#else -# define BYTE_ORDER 0 -#endif -*/ -/* pjlib: */ -#include -#if PJ_IS_LITTLE_ENDIAN -# define BYTE_ORDER -1 -#elif PJ_IS_BIG_ENDIAN -# define BYTE_ORDER 1 -#else -# error Endianess is not known! -#endif - - -#define T_MASK ((md5_word_t)~0) -#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) -#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) -#define T3 0x242070db -#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) -#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) -#define T6 0x4787c62a -#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) -#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) -#define T9 0x698098d8 -#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) -#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) -#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) -#define T13 0x6b901122 -#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) -#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) -#define T16 0x49b40821 -#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) -#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) -#define T19 0x265e5a51 -#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) -#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) -#define T22 0x02441453 -#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) -#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) -#define T25 0x21e1cde6 -#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) -#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) -#define T28 0x455a14ed -#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) -#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) -#define T31 0x676f02d9 -#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) -#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) -#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) -#define T35 0x6d9d6122 -#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) -#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) -#define T38 0x4bdecfa9 -#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) -#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) -#define T41 0x289b7ec6 -#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) -#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) -#define T44 0x04881d05 -#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) -#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) -#define T47 0x1fa27cf8 -#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) -#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) -#define T50 0x432aff97 -#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) -#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) -#define T53 0x655b59c3 -#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) -#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) -#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) -#define T57 0x6fa87e4f -#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) -#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) -#define T60 0x4e0811a1 -#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) -#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) -#define T63 0x2ad7d2bb -#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) - - -static void -md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) -{ - md5_word_t - a = pms->abcd[0], b = pms->abcd[1], - c = pms->abcd[2], d = pms->abcd[3]; - md5_word_t t; -#if BYTE_ORDER > 0 - /* Define storage only for big-endian CPUs. */ - md5_word_t X[16]; -#else - /* Define storage for little-endian or both types of CPUs. */ - md5_word_t xbuf[16]; - const md5_word_t *X; -#endif - - PJ_CHECK_STACK(); - - { -#if BYTE_ORDER == 0 - /* - * Determine dynamically whether this is a big-endian or - * little-endian machine, since we can use a more efficient - * algorithm on the latter. - */ - static const int w = 1; - - if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ -#endif -#if BYTE_ORDER <= 0 /* little-endian */ - { - /* - * On little-endian machines, we can process properly aligned - * data without copying it. - */ - if (!((data - (const md5_byte_t *)0) & 3)) { - /* data are properly aligned */ - X = (const md5_word_t *)data; - } else { - /* not aligned */ - memcpy(xbuf, data, 64); - X = xbuf; - } - } -#endif -#if BYTE_ORDER == 0 - else /* dynamic big-endian */ -#endif -#if BYTE_ORDER >= 0 /* big-endian */ - { - /* - * On big-endian machines, we must arrange the bytes in the - * right order. - */ - const md5_byte_t *xp = data; - int i; - -# if BYTE_ORDER == 0 - X = xbuf; /* (dynamic only) */ -# else -# define xbuf X /* (static only) */ -# endif - for (i = 0; i < 16; ++i, xp += 4) - xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); - } -#endif - } - -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - - /* Round 1. */ - /* Let [abcd k s i] denote the operation - a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ -#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + F(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 7, T1); - SET(d, a, b, c, 1, 12, T2); - SET(c, d, a, b, 2, 17, T3); - SET(b, c, d, a, 3, 22, T4); - SET(a, b, c, d, 4, 7, T5); - SET(d, a, b, c, 5, 12, T6); - SET(c, d, a, b, 6, 17, T7); - SET(b, c, d, a, 7, 22, T8); - SET(a, b, c, d, 8, 7, T9); - SET(d, a, b, c, 9, 12, T10); - SET(c, d, a, b, 10, 17, T11); - SET(b, c, d, a, 11, 22, T12); - SET(a, b, c, d, 12, 7, T13); - SET(d, a, b, c, 13, 12, T14); - SET(c, d, a, b, 14, 17, T15); - SET(b, c, d, a, 15, 22, T16); -#undef SET - - /* Round 2. */ - /* Let [abcd k s i] denote the operation - a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ -#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + G(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 1, 5, T17); - SET(d, a, b, c, 6, 9, T18); - SET(c, d, a, b, 11, 14, T19); - SET(b, c, d, a, 0, 20, T20); - SET(a, b, c, d, 5, 5, T21); - SET(d, a, b, c, 10, 9, T22); - SET(c, d, a, b, 15, 14, T23); - SET(b, c, d, a, 4, 20, T24); - SET(a, b, c, d, 9, 5, T25); - SET(d, a, b, c, 14, 9, T26); - SET(c, d, a, b, 3, 14, T27); - SET(b, c, d, a, 8, 20, T28); - SET(a, b, c, d, 13, 5, T29); - SET(d, a, b, c, 2, 9, T30); - SET(c, d, a, b, 7, 14, T31); - SET(b, c, d, a, 12, 20, T32); -#undef SET - - /* Round 3. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + H(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 5, 4, T33); - SET(d, a, b, c, 8, 11, T34); - SET(c, d, a, b, 11, 16, T35); - SET(b, c, d, a, 14, 23, T36); - SET(a, b, c, d, 1, 4, T37); - SET(d, a, b, c, 4, 11, T38); - SET(c, d, a, b, 7, 16, T39); - SET(b, c, d, a, 10, 23, T40); - SET(a, b, c, d, 13, 4, T41); - SET(d, a, b, c, 0, 11, T42); - SET(c, d, a, b, 3, 16, T43); - SET(b, c, d, a, 6, 23, T44); - SET(a, b, c, d, 9, 4, T45); - SET(d, a, b, c, 12, 11, T46); - SET(c, d, a, b, 15, 16, T47); - SET(b, c, d, a, 2, 23, T48); -#undef SET - - /* Round 4. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ -#define I(x, y, z) ((y) ^ ((x) | ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + I(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 6, T49); - SET(d, a, b, c, 7, 10, T50); - SET(c, d, a, b, 14, 15, T51); - SET(b, c, d, a, 5, 21, T52); - SET(a, b, c, d, 12, 6, T53); - SET(d, a, b, c, 3, 10, T54); - SET(c, d, a, b, 10, 15, T55); - SET(b, c, d, a, 1, 21, T56); - SET(a, b, c, d, 8, 6, T57); - SET(d, a, b, c, 15, 10, T58); - SET(c, d, a, b, 6, 15, T59); - SET(b, c, d, a, 13, 21, T60); - SET(a, b, c, d, 4, 6, T61); - SET(d, a, b, c, 11, 10, T62); - SET(c, d, a, b, 2, 15, T63); - SET(b, c, d, a, 9, 21, T64); -#undef SET - - /* Then perform the following additions. (That is increment each - of the four registers by the value it had before this block - was started.) */ - pms->abcd[0] += a; - pms->abcd[1] += b; - pms->abcd[2] += c; - pms->abcd[3] += d; -} - -void -md5_init(md5_state_t *pms) -{ - PJ_CHECK_STACK(); - - pms->count[0] = pms->count[1] = 0; - pms->abcd[0] = 0x67452301; - pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; - pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; - pms->abcd[3] = 0x10325476; -} - -void -md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) -{ - const md5_byte_t *p = data; - int left = nbytes; - int offset = (pms->count[0] >> 3) & 63; - md5_word_t nbits = (md5_word_t)(nbytes << 3); - - PJ_CHECK_STACK(); - - if (nbytes <= 0) - return; - - /* Update the message length. */ - pms->count[1] += nbytes >> 29; - pms->count[0] += nbits; - if (pms->count[0] < nbits) - pms->count[1]++; - - /* Process an initial partial block. */ - if (offset) { - int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); - - memcpy(pms->buf + offset, p, copy); - if (offset + copy < 64) - return; - p += copy; - left -= copy; - md5_process(pms, pms->buf); - } - - /* Process full blocks. */ - for (; left >= 64; p += 64, left -= 64) - md5_process(pms, p); - - /* Process a final partial block. */ - if (left) - memcpy(pms->buf, p, left); -} - -void -md5_finish(md5_state_t *pms, md5_byte_t digest[16]) -{ - static const md5_byte_t pad[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - md5_byte_t data[8]; - int i; - - PJ_CHECK_STACK(); - - /* Save the length before padding. */ - for (i = 0; i < 8; ++i) - data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); - /* Pad to 56 bytes mod 64. */ - md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); - /* Append the length. */ - md5_append(pms, data, 8); - for (i = 0; i < 16; ++i) - digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); -} +/* $Header: /pjproject-0.3/pjlib/src/pj/md5.c 5 10/14/05 12:26a Bennylp $ */ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include +#include +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ + +/* +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif +*/ +/* pjlib: */ +#include +#if PJ_IS_LITTLE_ENDIAN +# define BYTE_ORDER -1 +#elif PJ_IS_BIG_ENDIAN +# define BYTE_ORDER 1 +#else +# error Endianess is not known! +#endif + + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + PJ_CHECK_STACK(); + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + PJ_CHECK_STACK(); + + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + PJ_CHECK_STACK(); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + PJ_CHECK_STACK(); + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/pjlib/src/pj/os_core_linux_kernel.c b/pjlib/src/pj/os_core_linux_kernel.c index 82edccb46..f843e637a 100644 --- a/pjlib/src/pj/os_core_linux_kernel.c +++ b/pjlib/src/pj/os_core_linux_kernel.c @@ -1,685 +1,686 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_linux_kernel.c 3 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/os_core_linux_kernel.c $ - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/22/05 10:38a Bennylp - * Creaetd. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#if defined(MODVERSIONS) -#include -#endif -#include -#include -//#include -#include -#include - -#include -#include -#include - -#define THIS_FILE "oslinuxkern" - -struct pj_thread_t -{ - /** Thread's name. */ - char obj_name[PJ_MAX_OBJ_NAME]; - - /** Linux task structure for thread. */ - struct task_struct *thread; - - /** Flags (specified in pj_thread_create) */ - unsigned flags; - - /** Task queue needed to launch thread. */ - //struct tq_struct tq; - - /** Semaphore needed to control thread startup. */ - struct semaphore startstop_sem; - - /** Semaphore to suspend thread during startup. */ - struct semaphore suspend_sem; - - /** Queue thread is waiting on. Gets initialized by - thread_initialize, can be used by thread itself. - */ - wait_queue_head_t queue; - - /** Flag to tell thread whether to die or not. - When the thread receives a signal, it must check - the value of terminate and call thread_deinitialize and terminate - if set. - */ - int terminate; - - /** Thread's entry. */ - pj_thread_proc *func; - - /** Argument. */ - void *arg; -}; - -struct pj_atomic_t -{ - atomic_t atom; -}; - -struct pj_mutex_t -{ - struct semaphore sem; - pj_bool_t recursive; - pj_thread_t *owner; - int own_count; -}; - -struct pj_sem_t -{ - struct semaphore sem; -}; - -/* - * Static global variables. - */ -#define MAX_TLS_ID 32 -static void *tls_values[MAX_TLS_ID]; -static int tls_id; -static long thread_tls_id; -static spinlock_t critical_section = SPIN_LOCK_UNLOCKED; -static unsigned long spinlock_flags; -static pj_thread_t main_thread; - -/* private functions */ -//#define TRACE_(expr) PJ_LOG(3,expr) -#define TRACE_(x) - - -/* This must be called in the context of the new thread. */ -static void thread_initialize( pj_thread_t *thread ) -{ - TRACE_((THIS_FILE, "---new thread initializing...")); - - /* Set TLS */ - pj_thread_local_set(thread_tls_id, thread); - - /* fill in thread structure */ - thread->thread = current; - pj_assert(thread->thread != NULL); - - /* set signal mask to what we want to respond */ - siginitsetinv(¤t->blocked, - sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)); - - /* initialise wait queue */ - init_waitqueue_head(&thread->queue); - - /* initialise termination flag */ - thread->terminate = 0; - - /* set name of this process (max 15 chars + 0 !) */ - thread->obj_name[15] = '\0'; - sprintf(current->comm, thread->obj_name); - - /* tell the creator that we are ready and let him continue */ - up(&thread->startstop_sem); -} - -/* cleanup of thread. Called by the exiting thread. */ -static void thread_deinitialize(pj_thread_t *thread) -{ - /* we are terminating */ - - /* lock the kernel, the exit will unlock it */ - thread->thread = NULL; - mb(); - - /* notify the stop_kthread() routine that we are terminating. */ - up(&thread->startstop_sem); - - /* the kernel_thread that called clone() does a do_exit here. */ - - /* there is no race here between execution of the "killer" and - real termination of the thread (race window between up and do_exit), - since both the thread and the "killer" function are running with - the kernel lock held. - The kernel lock will be freed after the thread exited, so the code - is really not executed anymore as soon as the unload functions gets - the kernel lock back. - The init process may not have made the cleanup of the process here, - but the cleanup can be done safely with the module unloaded. - */ - -} - -static int thread_proc(void *arg) -{ - pj_thread_t *thread = arg; - - TRACE_((THIS_FILE, "---new thread starting!")); - - /* Initialize thread. */ - thread_initialize( thread ); - - /* Wait if created suspended. */ - if (thread->flags & PJ_THREAD_SUSPENDED) { - TRACE_((THIS_FILE, "---new thread suspended...")); - down(&thread->suspend_sem); - } - - TRACE_((THIS_FILE, "---new thread running...")); - - pj_assert(thread->func != NULL); - - /* Call thread's entry. */ - (*thread->func)(thread->arg); - - TRACE_((THIS_FILE, "---thread exiting...")); - - /* Cleanup thread. */ - thread_deinitialize(thread); - - return 0; -} - -/* The very task entry. */ -static void kthread_launcher(void *arg) -{ - TRACE_((THIS_FILE, "...launching thread!...")); - kernel_thread(&thread_proc, arg, 0); -} - -PJ_DEF(pj_status_t) pj_init(void) -{ - pj_status_t rc; - - PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); - - rc = pj_thread_init(); - if (rc != PJ_SUCCESS) - return rc; - - /* Initialize exception ID for the pool. - * Must do so after critical section is configured. - */ - rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); - if (rc != PJ_SUCCESS) - return rc; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_uint32_t) pj_getpid(void) -{ - return 1; -} - -PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, - pj_thread_desc desc, - pj_thread_t **ptr_thread) -{ - char stack_ptr; - pj_thread_t *thread = (pj_thread_t *)desc; - pj_str_t thread_name = pj_str((char*)cstr_thread_name); - - /* Size sanity check. */ - if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { - pj_assert(!"Not enough pj_thread_desc size!"); - return PJ_EBUG; - } - - /* If a thread descriptor has been registered before, just return it. */ - if (pj_thread_local_get (thread_tls_id) != 0) { - *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id); - return PJ_SUCCESS; - } - - /* Initialize and set the thread entry. */ - pj_memset(desc, 0, sizeof(pj_thread_desc)); - - if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) - pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); - else - pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread); - - /* Initialize. */ - thread_initialize(thread); - - /* Eat semaphore. */ - down(&thread->startstop_sem); - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - thread->stk_start = &stack_ptr; - thread->stk_size = 0xFFFFFFFFUL; - thread->stk_max_usage = 0; -#else - stack_ptr = '\0'; -#endif - - *ptr_thread = thread; - return PJ_SUCCESS; -} - - -pj_status_t pj_thread_init(void) -{ - pj_status_t rc; - pj_thread_t *dummy; - - rc = pj_thread_local_alloc(&thread_tls_id); - if (rc != PJ_SUCCESS) - return rc; - - return pj_thread_register("pjlib-main", (pj_uint8_t*)&main_thread, &dummy); -} - -PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, - pj_thread_proc *proc, void *arg, - pj_size_t stack_size, unsigned flags, - pj_thread_t **ptr_thread) -{ - pj_thread_t *thread; - - TRACE_((THIS_FILE, "pj_thread_create()")); - - PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); - - thread = pj_pool_zalloc(pool, sizeof(pj_thread_t)); - if (!thread) - return PJ_ENOMEM; - - PJ_UNUSED_ARG(stack_size); - - /* Thread name. */ - if (!thread_name) - thread_name = "thr%p"; - - if (strchr(thread_name, '%')) { - pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread); - } else { - strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME); - thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - - /* Init thread's semaphore. */ - TRACE_((THIS_FILE, "...init semaphores...")); - init_MUTEX_LOCKED(&thread->startstop_sem); - init_MUTEX_LOCKED(&thread->suspend_sem); - - thread->flags = flags; - - if ((flags & PJ_THREAD_SUSPENDED) == 0) { - up(&thread->suspend_sem); - } - - /* Store the functions and argument. */ - thread->func = proc; - thread->arg = arg; - - /* Save return value. */ - *ptr_thread = thread; - - /* Create the new thread by running a task through keventd. */ - -#if 0 - /* Initialize the task queue struct. */ - thread->tq.sync = 0; - INIT_LIST_HEAD(&thread->tq.list); - thread->tq.routine = kthread_launcher; - thread->tq.data = thread; - - /* and schedule it for execution. */ - schedule_task(&thread->tq); -#endif - kthread_launcher(thread); - - /* Wait until thread has reached the setup_thread routine. */ - TRACE_((THIS_FILE, "...wait for the new thread...")); - down(&thread->startstop_sem); - - TRACE_((THIS_FILE, "...main thread resumed...")); - return PJ_SUCCESS; -} - -PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread) -{ - return thread->obj_name; -} - -PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread) -{ - up(&thread->suspend_sem); - return PJ_SUCCESS; -} - -PJ_DEF(pj_thread_t*) pj_thread_this(void) -{ - return (pj_thread_t*)pj_thread_local_get(thread_tls_id); -} - -PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) -{ - TRACE_((THIS_FILE, "pj_thread_join()")); - down(&p->startstop_sem); - TRACE_((THIS_FILE, " joined!")); - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread) -{ - PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP); - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) -{ - pj_highprec_t ticks; - pj_thread_t *thread = pj_thread_this(); - - PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG); - - /* Use high precision calculation to make sure we don't - * crop values: - * - * ticks = HZ * msec / 1000 - */ - ticks = HZ; - pj_highprec_mul(ticks, msec); - pj_highprec_div(ticks, 1000); - - TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks)); - interruptible_sleep_on_timeout( &thread->queue, ticks); - return PJ_SUCCESS; -} - - -/////////////////////////////////////////////////////////////////////////////// -PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, - pj_atomic_value_t value, - pj_atomic_t **ptr_var) -{ - pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); - if (!t) return PJ_ENOMEM; - - atomic_set(&t->atom, value); - *ptr_var = t; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) -{ - return 0; -} - -PJ_DEF(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *var, - pj_atomic_value_t value) -{ - pj_atomic_value_t oldval = atomic_read(&var->atom); - atomic_set(&var->atom, value); - return oldval; -} - -PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var) -{ - return atomic_read(&var->atom); -} - -PJ_DEF(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *var) -{ - atomic_inc(&var->atom); - return atomic_read(&var->atom); -} - -PJ_DEF(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *var) -{ - atomic_dec(&var->atom); - return atomic_read(&var->atom); -} - - - -/////////////////////////////////////////////////////////////////////////////// -PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) -{ - if (tls_id >= MAX_TLS_ID) - return PJ_ETOOMANY; - - *index = tls_id++; - - return PJ_SUCCESS; -} - -PJ_DEF(void) pj_thread_local_free(long index) -{ - pj_assert(index >= 0 && index < MAX_TLS_ID); -} - -PJ_DEF(void) pj_thread_local_set(long index, void *value) -{ - pj_assert(index >= 0 && index < MAX_TLS_ID); - tls_values[index] = value; -} - -PJ_DEF(void*) pj_thread_local_get(long index) -{ - pj_assert(index >= 0 && index < MAX_TLS_ID); - return tls_values[index]; -} - - -/////////////////////////////////////////////////////////////////////////////// -PJ_DEF(void) pj_enter_critical_section(void) -{ - spin_lock_irqsave(&critical_section, spinlock_flags); -} - -PJ_DEF(void) pj_leave_critical_section(void) -{ - spin_unlock_irqrestore(&critical_section, spinlock_flags); -} - - -/////////////////////////////////////////////////////////////////////////////// -PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, - const char *name, - int type, - pj_mutex_t **ptr_mutex) -{ - pj_mutex_t *mutex; - - PJ_UNUSED_ARG(name); - - mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t)); - if (!mutex) - return PJ_ENOMEM; - - init_MUTEX(&mutex->sem); - - mutex->recursive = (type == PJ_MUTEX_RECURSE); - mutex->owner = NULL; - mutex->own_count = 0; - - /* Done. */ - *ptr_mutex = mutex; - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, - pj_mutex_t **mutex ) -{ - return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); -} - -PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, - const char *name, - pj_mutex_t **mutex ) -{ - return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex); -} - -PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) -{ - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - - if (mutex->recursive) { - pj_thread_t *this_thread = pj_thread_this(); - if (mutex->owner == this_thread) { - ++mutex->own_count; - } else { - down(&mutex->sem); - pj_assert(mutex->own_count == 0); - mutex->owner = this_thread; - mutex->own_count = 1; - } - } else { - down(&mutex->sem); - } - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) -{ - long rc; - - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - - if (mutex->recursive) { - pj_thread_t *this_thread = pj_thread_this(); - if (mutex->owner == this_thread) { - ++mutex->own_count; - } else { - rc = down_interruptible(&mutex->sem); - if (rc != 0) - return PJ_RETURN_OS_ERROR(-rc); - pj_assert(mutex->own_count == 0); - mutex->owner = this_thread; - mutex->own_count = 1; - } - } else { - int rc = down_trylock(&mutex->sem); - if (rc != 0) - return PJ_RETURN_OS_ERROR(-rc); - } - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) -{ - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - - if (mutex->recursive) { - pj_thread_t *this_thread = pj_thread_this(); - if (mutex->owner == this_thread) { - pj_assert(mutex->own_count > 0); - --mutex->own_count; - if (mutex->own_count == 0) { - mutex->owner = NULL; - up(&mutex->sem); - } - } else { - pj_assert(!"Not owner!"); - return PJ_EINVALIDOP; - } - } else { - up(&mutex->sem); - } - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) -{ - PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL); - - return PJ_SUCCESS; -} - -#if defined(PJ_DEBUG) && PJ_DEBUG != 0 -PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) -{ - if (mutex->recursive) - return mutex->owner == pj_thread_this(); - else - return 1; -} -#endif /* PJ_DEBUG */ - - -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 - -PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, - const char *name, - unsigned initial, - unsigned max, - pj_sem_t **sem) -{ - pj_sem_t *sem; - - PJ_UNUSED_ARG(max); - - PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); - - sem = pj_pool_alloc(pool, sizeof(pj_sem_t)); - sema_init(&sem->sem, initial); - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) -{ - PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); - - down(&sem->sem); - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) -{ - int rc; - - PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); - - rc = down_trylock(&sem->sem); - if (rc != 0) { - return PJ_RETURN_OS_ERROR(-rc); - } else { - return PJ_SUCCESS; - } -} - -PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) -{ - PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); - - up(&sem->sem); - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) -{ - PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); - - return PJ_SUCCESS; -} - -#endif /* PJ_HAS_SEMAPHORE */ - - - - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_linux_kernel.c 3 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/os_core_linux_kernel.c $ + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/22/05 10:38a Bennylp + * Creaetd. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if defined(MODVERSIONS) +#include +#endif +#include +#include +//#include +#include +#include + +#include +#include +#include + +#define THIS_FILE "oslinuxkern" + +struct pj_thread_t +{ + /** Thread's name. */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** Linux task structure for thread. */ + struct task_struct *thread; + + /** Flags (specified in pj_thread_create) */ + unsigned flags; + + /** Task queue needed to launch thread. */ + //struct tq_struct tq; + + /** Semaphore needed to control thread startup. */ + struct semaphore startstop_sem; + + /** Semaphore to suspend thread during startup. */ + struct semaphore suspend_sem; + + /** Queue thread is waiting on. Gets initialized by + thread_initialize, can be used by thread itself. + */ + wait_queue_head_t queue; + + /** Flag to tell thread whether to die or not. + When the thread receives a signal, it must check + the value of terminate and call thread_deinitialize and terminate + if set. + */ + int terminate; + + /** Thread's entry. */ + pj_thread_proc *func; + + /** Argument. */ + void *arg; +}; + +struct pj_atomic_t +{ + atomic_t atom; +}; + +struct pj_mutex_t +{ + struct semaphore sem; + pj_bool_t recursive; + pj_thread_t *owner; + int own_count; +}; + +struct pj_sem_t +{ + struct semaphore sem; +}; + +/* + * Static global variables. + */ +#define MAX_TLS_ID 32 +static void *tls_values[MAX_TLS_ID]; +static int tls_id; +static long thread_tls_id; +static spinlock_t critical_section = SPIN_LOCK_UNLOCKED; +static unsigned long spinlock_flags; +static pj_thread_t main_thread; + +/* private functions */ +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(x) + + +/* This must be called in the context of the new thread. */ +static void thread_initialize( pj_thread_t *thread ) +{ + TRACE_((THIS_FILE, "---new thread initializing...")); + + /* Set TLS */ + pj_thread_local_set(thread_tls_id, thread); + + /* fill in thread structure */ + thread->thread = current; + pj_assert(thread->thread != NULL); + + /* set signal mask to what we want to respond */ + siginitsetinv(¤t->blocked, + sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)); + + /* initialise wait queue */ + init_waitqueue_head(&thread->queue); + + /* initialise termination flag */ + thread->terminate = 0; + + /* set name of this process (max 15 chars + 0 !) */ + thread->obj_name[15] = '\0'; + sprintf(current->comm, thread->obj_name); + + /* tell the creator that we are ready and let him continue */ + up(&thread->startstop_sem); +} + +/* cleanup of thread. Called by the exiting thread. */ +static void thread_deinitialize(pj_thread_t *thread) +{ + /* we are terminating */ + + /* lock the kernel, the exit will unlock it */ + thread->thread = NULL; + mb(); + + /* notify the stop_kthread() routine that we are terminating. */ + up(&thread->startstop_sem); + + /* the kernel_thread that called clone() does a do_exit here. */ + + /* there is no race here between execution of the "killer" and + real termination of the thread (race window between up and do_exit), + since both the thread and the "killer" function are running with + the kernel lock held. + The kernel lock will be freed after the thread exited, so the code + is really not executed anymore as soon as the unload functions gets + the kernel lock back. + The init process may not have made the cleanup of the process here, + but the cleanup can be done safely with the module unloaded. + */ + +} + +static int thread_proc(void *arg) +{ + pj_thread_t *thread = arg; + + TRACE_((THIS_FILE, "---new thread starting!")); + + /* Initialize thread. */ + thread_initialize( thread ); + + /* Wait if created suspended. */ + if (thread->flags & PJ_THREAD_SUSPENDED) { + TRACE_((THIS_FILE, "---new thread suspended...")); + down(&thread->suspend_sem); + } + + TRACE_((THIS_FILE, "---new thread running...")); + + pj_assert(thread->func != NULL); + + /* Call thread's entry. */ + (*thread->func)(thread->arg); + + TRACE_((THIS_FILE, "---thread exiting...")); + + /* Cleanup thread. */ + thread_deinitialize(thread); + + return 0; +} + +/* The very task entry. */ +static void kthread_launcher(void *arg) +{ + TRACE_((THIS_FILE, "...launching thread!...")); + kernel_thread(&thread_proc, arg, 0); +} + +PJ_DEF(pj_status_t) pj_init(void) +{ + pj_status_t rc; + + PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); + + rc = pj_thread_init(); + if (rc != PJ_SUCCESS) + return rc; + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + return 1; +} + +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **ptr_thread) +{ + char stack_ptr; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_memset(desc, 0, sizeof(struct pj_thread_t)); + + if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); + else + pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread); + + /* Initialize. */ + thread_initialize(thread); + + /* Eat semaphore. */ + down(&thread->startstop_sem); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + *ptr_thread = thread; + return PJ_SUCCESS; +} + + +pj_status_t pj_thread_init(void) +{ + pj_status_t rc; + pj_thread_t *dummy; + + rc = pj_thread_local_alloc(&thread_tls_id); + if (rc != PJ_SUCCESS) + return rc; + + return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy); +} + +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name, + pj_thread_proc *proc, void *arg, + pj_size_t stack_size, unsigned flags, + pj_thread_t **ptr_thread) +{ + pj_thread_t *thread; + + TRACE_((THIS_FILE, "pj_thread_create()")); + + PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); + + thread = pj_pool_zalloc(pool, sizeof(pj_thread_t)); + if (!thread) + return PJ_ENOMEM; + + PJ_UNUSED_ARG(stack_size); + + /* Thread name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread); + } else { + strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME); + thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + /* Init thread's semaphore. */ + TRACE_((THIS_FILE, "...init semaphores...")); + init_MUTEX_LOCKED(&thread->startstop_sem); + init_MUTEX_LOCKED(&thread->suspend_sem); + + thread->flags = flags; + + if ((flags & PJ_THREAD_SUSPENDED) == 0) { + up(&thread->suspend_sem); + } + + /* Store the functions and argument. */ + thread->func = proc; + thread->arg = arg; + + /* Save return value. */ + *ptr_thread = thread; + + /* Create the new thread by running a task through keventd. */ + +#if 0 + /* Initialize the task queue struct. */ + thread->tq.sync = 0; + INIT_LIST_HEAD(&thread->tq.list); + thread->tq.routine = kthread_launcher; + thread->tq.data = thread; + + /* and schedule it for execution. */ + schedule_task(&thread->tq); +#endif + kthread_launcher(thread); + + /* Wait until thread has reached the setup_thread routine. */ + TRACE_((THIS_FILE, "...wait for the new thread...")); + down(&thread->startstop_sem); + + TRACE_((THIS_FILE, "...main thread resumed...")); + return PJ_SUCCESS; +} + +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread) +{ + return thread->obj_name; +} + +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread) +{ + up(&thread->suspend_sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ + return (pj_thread_t*)pj_thread_local_get(thread_tls_id); +} + +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ + TRACE_((THIS_FILE, "pj_thread_join()")); + down(&p->startstop_sem); + TRACE_((THIS_FILE, " joined!")); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread) +{ + PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + pj_highprec_t ticks; + pj_thread_t *thread = pj_thread_this(); + + PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG); + + /* Use high precision calculation to make sure we don't + * crop values: + * + * ticks = HZ * msec / 1000 + */ + ticks = HZ; + pj_highprec_mul(ticks, msec); + pj_highprec_div(ticks, 1000); + + TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks)); + interruptible_sleep_on_timeout( &thread->queue, ticks); + return PJ_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t value, + pj_atomic_t **ptr_var) +{ + pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); + if (!t) return PJ_ENOMEM; + + atomic_set(&t->atom, value); + *ptr_var = t; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) +{ + return 0; +} + +PJ_DEF(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *var, + pj_atomic_value_t value) +{ + pj_atomic_value_t oldval = atomic_read(&var->atom); + atomic_set(&var->atom, value); + return oldval; +} + +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var) +{ + return atomic_read(&var->atom); +} + +PJ_DEF(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *var) +{ + atomic_inc(&var->atom); + return atomic_read(&var->atom); +} + +PJ_DEF(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *var) +{ + atomic_dec(&var->atom); + return atomic_read(&var->atom); +} + + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) +{ + if (tls_id >= MAX_TLS_ID) + return PJ_ETOOMANY; + + *index = tls_id++; + + return PJ_SUCCESS; +} + +PJ_DEF(void) pj_thread_local_free(long index) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); +} + +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); + tls_values[index] = value; + return PJ_SUCCESS; +} + +PJ_DEF(void*) pj_thread_local_get(long index) +{ + pj_assert(index >= 0 && index < MAX_TLS_ID); + return tls_values[index]; +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void) pj_enter_critical_section(void) +{ + spin_lock_irqsave(&critical_section, spinlock_flags); +} + +PJ_DEF(void) pj_leave_critical_section(void) +{ + spin_unlock_irqrestore(&critical_section, spinlock_flags); +} + + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **ptr_mutex) +{ + pj_mutex_t *mutex; + + PJ_UNUSED_ARG(name); + + mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t)); + if (!mutex) + return PJ_ENOMEM; + + init_MUTEX(&mutex->sem); + + mutex->recursive = (type == PJ_MUTEX_RECURSE); + mutex->owner = NULL; + mutex->own_count = 0; + + /* Done. */ + *ptr_mutex = mutex; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex); +} + +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + ++mutex->own_count; + } else { + down(&mutex->sem); + pj_assert(mutex->own_count == 0); + mutex->owner = this_thread; + mutex->own_count = 1; + } + } else { + down(&mutex->sem); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ + long rc; + + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + ++mutex->own_count; + } else { + rc = down_interruptible(&mutex->sem); + if (rc != 0) + return PJ_RETURN_OS_ERROR(-rc); + pj_assert(mutex->own_count == 0); + mutex->owner = this_thread; + mutex->own_count = 1; + } + } else { + int rc = down_trylock(&mutex->sem); + if (rc != 0) + return PJ_RETURN_OS_ERROR(-rc); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + if (mutex->recursive) { + pj_thread_t *this_thread = pj_thread_this(); + if (mutex->owner == this_thread) { + pj_assert(mutex->own_count > 0); + --mutex->own_count; + if (mutex->own_count == 0) { + mutex->owner = NULL; + up(&mutex->sem); + } + } else { + pj_assert(!"Not owner!"); + return PJ_EINVALIDOP; + } + } else { + up(&mutex->sem); + } + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL); + + return PJ_SUCCESS; +} + +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ + if (mutex->recursive) + return mutex->owner == pj_thread_this(); + else + return 1; +} +#endif /* PJ_DEBUG */ + + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem) +{ + pj_sem_t *sem; + + PJ_UNUSED_ARG(max); + + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(pj_sem_t)); + sema_init(&sem->sem, initial); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + down(&sem->sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ + int rc; + + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + rc = down_trylock(&sem->sem); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(-rc); + } else { + return PJ_SUCCESS; + } +} + +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + up(&sem->sem); + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ + PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL); + + return PJ_SUCCESS; +} + +#endif /* PJ_HAS_SEMAPHORE */ + + + + diff --git a/pjlib/src/pj/os_core_unix.c b/pjlib/src/pj/os_core_unix.c index 0f4f3a99e..5b19668b1 100644 --- a/pjlib/src/pj/os_core_unix.c +++ b/pjlib/src/pj/os_core_unix.c @@ -1,1182 +1,1209 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_unix.c 11 10/29/05 10:27p Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/os_core_unix.c $ - * - * 11 10/29/05 10:27p Bennylp - * Fixed misc warnings. - * - * 10 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 9 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 -# include -#endif - -#include // getpid() -#include // errno - -#define __USE_GNU -#include - -#define THIS_FILE "osunix" - -struct pj_thread_t -{ - char obj_name[PJ_MAX_OBJ_NAME]; - pthread_t thread; - pj_thread_proc *proc; - void *arg; - - pj_mutex_t *suspended_mutex; - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - pj_uint32_t stk_size; - pj_uint32_t stk_max_usage; - char *stk_start; - const char *caller_file; - int caller_line; -#endif -}; - -struct pj_atomic_t -{ - pj_mutex_t *mutex; - pj_atomic_value_t value; -}; - -struct pj_mutex_t -{ - pthread_mutex_t mutex; - char obj_name[PJ_MAX_OBJ_NAME]; -#if PJ_DEBUG - int nesting_level; - pj_thread_t *owner; -#endif -}; - -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 -struct pj_sem_t -{ - sem_t sem; - char obj_name[PJ_MAX_OBJ_NAME]; -}; -#endif /* PJ_HAS_SEMAPHORE */ - -#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 -struct pj_event_t -{ - char obj_name[PJ_MAX_OBJ_NAME]; -}; -#endif /* PJ_HAS_EVENT_OBJ */ - - -#if PJ_HAS_THREADS - static pj_thread_t main_thread; - static long thread_tls_id; - static pj_mutex_t critical_section; -#else -# define MAX_THREADS 32 - static int tls_flag[MAX_THREADS]; - static void *tls[MAX_THREADS]; -#endif - -static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type); - -/* - * pj_init(void). - * Init PJLIB! - */ -PJ_DEF(pj_status_t) pj_init(void) -{ - char dummy_guid[PJ_GUID_MAX_LENGTH]; - pj_str_t guid; - pj_status_t rc; - - PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); - -#if PJ_HAS_THREADS - /* Init this thread's TLS. */ - if ((rc=pj_thread_init()) != 0) { - return rc; - } - - /* Critical section. */ - if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE)) != 0) - return rc; - -#endif - - /* Initialize exception ID for the pool. - * Must do so after critical section is configured. - */ - rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); - if (rc != PJ_SUCCESS) - return rc; - - /* Init random seed. */ - pj_srand( clock() ); - - /* Startup GUID. */ - guid.ptr = dummy_guid; - pj_generate_unique_string( &guid ); - - /* Initialize exception ID for the pool. - * Must do so after critical section is configured. - */ - rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); - if (rc != PJ_SUCCESS) - return rc; - - /* Startup timestamp */ -#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 - { - pj_timestamp dummy_ts; - if ((rc=pj_get_timestamp(&dummy_ts)) != 0) { - return rc; - } - } -#endif - - return PJ_SUCCESS; -} - -/* - * pj_getpid(void) - */ -PJ_DEF(pj_uint32_t) pj_getpid(void) -{ - PJ_CHECK_STACK(); - return getpid(); -} - -/* - * pj_thread_register(..) - */ -PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, - pj_thread_desc desc, - pj_thread_t **ptr_thread) -{ -#if PJ_HAS_THREADS - char stack_ptr; - pj_thread_t *thread = (pj_thread_t *)desc; - pj_str_t thread_name = pj_str((char*)cstr_thread_name); - - /* Size sanity check. */ - if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { - pj_assert(!"Not enough pj_thread_desc size!"); - return PJ_EBUG; - } - - /* If a thread descriptor has been registered before, just return it. */ - if (pj_thread_local_get (thread_tls_id) != 0) { - *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id); - return PJ_SUCCESS; - } - - /* Initialize and set the thread entry. */ - pj_memset(desc, 0, sizeof(pj_thread_desc)); - thread->thread = pthread_self(); - - if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) - pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); - else - pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread); - - pj_thread_local_set(thread_tls_id, thread); - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - thread->stk_start = &stack_ptr; - thread->stk_size = 0xFFFFFFFFUL; - thread->stk_max_usage = 0; -#else - stack_ptr = '\0'; -#endif - - *ptr_thread = thread; - return PJ_SUCCESS; -#else - pj_thread_t *thread = (pj_thread_t*)desc; - *ptr_thread = thread; - return SUCCESS; -#endif -} - -/* - * pj_thread_init(void) - */ -pj_status_t pj_thread_init(void) -{ -#if PJ_HAS_THREADS - pj_status_t rc; - pj_thread_t *dummy; - - rc = pj_thread_local_alloc(&thread_tls_id ); - if (rc != PJ_SUCCESS) { - return rc; - } - return pj_thread_register("thr%p", (pj_uint8_t*)&main_thread, &dummy); -#else - PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!")); - return PJ_EINVALIDOP; -#endif -} - -#if PJ_HAS_THREADS -/* - * thread_main() - * - * This is the main entry for all threads. - */ -static void *thread_main(void *param) -{ - pj_thread_t *rec = param; - void *result; - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - rec->stk_start = (char*)&rec; -#endif - - /* Set current thread id. */ - pj_thread_local_set(thread_tls_id, rec); - - /* Check if suspension is required. */ - if (rec->suspended_mutex) - pj_mutex_lock(rec->suspended_mutex); - - PJ_LOG(6,(rec->obj_name, "Thread started")); - - /* Call user's entry! */ - result = (void*) (*rec->proc)(rec->arg); - - /* Done. */ - PJ_LOG(6,(rec->obj_name, "Thread quitting")); - return result; -} -#endif - -/* - * pj_thread_create(...) - */ -PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, - const char *thread_name, - pj_thread_proc *proc, - void *arg, - pj_size_t stack_size, - unsigned flags, - pj_thread_t **ptr_thread) -{ -#if PJ_HAS_THREADS - pj_thread_t *rec; - int rc; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); - - /* Create thread record and assign name for the thread */ - rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t)); - if (!rec) - return PJ_ENOMEM; - - /* Set name. */ - if (!thread_name) - thread_name = "thr%p"; - - if (strchr(thread_name, '%')) { - pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); - } else { - strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); - rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL; - rec->stk_max_usage = 0; -#endif - - /* Emulate suspended thread with mutex. */ - if (flags & PJ_THREAD_SUSPENDED) { - rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex); - if (rc != PJ_SUCCESS) - return rc; - - pj_mutex_lock(rec->suspended_mutex); - } else { - pj_assert(rec->suspended_mutex == NULL); - } - - PJ_LOG(6, (rec->obj_name, "Thread created")); - - /* Create the thread. */ - rec->proc = proc; - rec->arg = arg; - rc = pthread_create( &rec->thread, NULL, thread_main, rec); - if (rc != 0) - return PJ_RETURN_OS_ERROR(rc); - - *ptr_thread = rec; - return PJ_SUCCESS; -#else - pj_assert(!"Threading is disabled!"); - return PJ_EINVALIDOP; -#endif -} - -/* - * pj_thread-get_name() - */ -PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) -{ -#if PJ_HAS_THREADS - pj_thread_t *rec = (pj_thread_t*)p; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(p, ""); - - return rec->obj_name; -#else - return ""; -#endif -} - -/* - * pj_thread_resume() - */ -PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) -{ - pj_status_t rc; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(p, PJ_EINVAL); - - rc = pj_mutex_unlock(p->suspended_mutex); - - return rc; -} - -/* - * pj_thread_this() - */ -PJ_DEF(pj_thread_t*) pj_thread_this(void) -{ -#if PJ_HAS_THREADS - pj_thread_t *rec = pj_thread_local_get(thread_tls_id); - pj_assert(rec != NULL); - - /* - * MUST NOT check stack because this function is called - * by PJ_CHECK_STACK() itself!!! - * - */ - - return rec; -#else - pj_assert(!"Threading is not enabled!"); - return NULL; -#endif -} - -/* - * pj_thread_join() - */ -PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) -{ -#if PJ_HAS_THREADS - pj_thread_t *rec = (pj_thread_t *)p; - void *ret; - int result; - - PJ_CHECK_STACK(); - - PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); - result = pthread_join( rec->thread, &ret); - - if (result == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(result); -#else - PJ_CHECK_STACK(); - pj_assert(!"No multithreading support!"); - return PJ_EINVALIDOP; -#endif -} - -/* - * pj_thread_destroy() - */ -PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) -{ - /* This function is used to destroy thread handle in other platforms. - * I suppose there's nothing to do here.. - */ - PJ_CHECK_STACK(); - return PJ_SUCCESS; -} - -/* - * pj_thread_sleep() - */ -PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) -{ - PJ_CHECK_STACK(); - return usleep(msec * 1000); -} - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 -/* - * pj_thread_check_stack() - * Implementation for PJ_CHECK_STACK() - */ -PJ_DEF(void) pj_thread_check_stack(const char *file, int line) -{ - char stk_ptr; - pj_uint32_t usage; - pj_thread_t *thread = pj_thread_this(); - - /* Calculate current usage. */ - usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : - thread->stk_start - &stk_ptr; - - /* Assert if stack usage is dangerously high. */ - pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); - - /* Keep statistic. */ - if (usage > thread->stk_max_usage) { - thread->stk_max_usage = usage; - thread->caller_file = file; - thread->caller_line = line; - } -} - -/* - * pj_thread_get_stack_max_usage() - */ -PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) -{ - return thread->stk_max_usage; -} - -/* - * pj_thread_get_stack_info() - */ -PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, - const char **file, - int *line ) -{ - pj_assert(thread); - - *file = thread->caller_file; - *line = thread->caller_line; - return 0; -} - -#endif /* PJ_OS_HAS_CHECK_STACK */ - -/////////////////////////////////////////////////////////////////////////////// -/* - * pj_atomic_create() - */ -PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, - pj_atomic_value_t initial, - pj_atomic_t **ptr_atomic) -{ - pj_status_t rc; - pj_atomic_t *atomic_var = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); - if (!atomic_var) - return PJ_ENOMEM; - -#if PJ_HAS_THREADS - rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex); - if (rc != PJ_SUCCESS) - return rc; -#endif - atomic_var->value = initial; - - *ptr_atomic = atomic_var; - return PJ_SUCCESS; -} - -/* - * pj_atomic_destroy() - */ -PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) -{ - PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); -#if PJ_HAS_THREADS - return pj_mutex_destroy( atomic_var->mutex ); -#else - return 0; -#endif -} - -/* - * pj_atomic_set() - */ -PJ_DEF(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *atomic_var, - pj_atomic_value_t value) -{ - pj_atomic_value_t oldval; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - -#if PJ_HAS_THREADS - pj_mutex_lock( atomic_var->mutex ); -#endif - oldval = atomic_var->value; - atomic_var->value = value; -#if PJ_HAS_THREADS - pj_mutex_unlock( atomic_var->mutex); -#endif - return oldval; -} - -/* - * pj_atomic_get() - */ -PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) -{ - pj_atomic_value_t oldval; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - -#if PJ_HAS_THREADS - pj_mutex_lock( atomic_var->mutex ); -#endif - oldval = atomic_var->value; -#if PJ_HAS_THREADS - pj_mutex_unlock( atomic_var->mutex); -#endif - return oldval; -} - -/* - * pj_atomic_inc() - */ -PJ_DEF(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *atomic_var) -{ - pj_atomic_value_t newval; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - -#if PJ_HAS_THREADS - pj_mutex_lock( atomic_var->mutex ); -#endif - newval = ++atomic_var->value; -#if PJ_HAS_THREADS - pj_mutex_unlock( atomic_var->mutex); -#endif - return newval; -} - -/* - * pj_atomic_dec() - */ -PJ_DEF(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *atomic_var) -{ - pj_atomic_value_t newval; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - -#if PJ_HAS_THREADS - pj_mutex_lock( atomic_var->mutex ); -#endif - newval = --atomic_var->value; -#if PJ_HAS_THREADS - pj_mutex_unlock( atomic_var->mutex); -#endif - return newval; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * pj_thread_local_alloc() - */ -PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index) -{ -#if PJ_HAS_THREADS - pthread_key_t key; - int rc; - - PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL); - - pj_assert( sizeof(pthread_key_t) <= sizeof(long)); - if ((rc=pthread_key_create(&key, NULL)) != 0) - return PJ_RETURN_OS_ERROR(rc); - - *p_index = key; - return PJ_SUCCESS; -#else - int i; - for (i=0; i= 0 && index < MAX_THREADS); - tls[index] = value; -#endif -} - -PJ_DEF(void*) pj_thread_local_get(long index) -{ - //Can't check stack because this function is called - //by PJ_CHECK_STACK() itself!!! - //PJ_CHECK_STACK(); -#if PJ_HAS_THREADS - return pthread_getspecific(index); -#else - pj_assert(index >= 0 && index < MAX_THREADS); - return tls[index]; -#endif -} - -/////////////////////////////////////////////////////////////////////////////// -PJ_DEF(void) pj_enter_critical_section(void) -{ -#if PJ_HAS_THREADS - pj_mutex_lock(&critical_section); -#endif -} - -PJ_DEF(void) pj_leave_critical_section(void) -{ -#if PJ_HAS_THREADS - pj_mutex_unlock(&critical_section); -#endif -} - - -/////////////////////////////////////////////////////////////////////////////// -static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type) -{ -#if PJ_HAS_THREADS - PJ_UNUSED_ARG(type); - - PJ_CHECK_STACK(); - - if (type == PJ_MUTEX_SIMPLE) { - pthread_mutex_t the_mutex = PTHREAD_MUTEX_INITIALIZER; - mutex->mutex = the_mutex; - } else { - pthread_mutex_t the_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; - mutex->mutex = the_mutex; - } - -#if PJ_DEBUG - /* Set owner. */ - mutex->nesting_level = 0; - mutex->owner = NULL; -#endif - - /* Set name. */ - if (!name) { - name = "mtx%p"; - } - if (strchr(name, '%')) { - pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); - } else { - strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); - mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - - PJ_LOG(6, (mutex->obj_name, "Mutex created")); - return PJ_SUCCESS; -#else /* PJ_HAS_THREADS */ - return PJ_SUCCESS; -#endif -} - -/* - * pj_mutex_create() - */ -PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, - const char *name, - int type, - pj_mutex_t **ptr_mutex) -{ -#if PJ_HAS_THREADS - pj_status_t rc; - pj_mutex_t *mutex; - - PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL); - - mutex = pj_pool_alloc(pool, sizeof(*mutex)); - if (!mutex) return PJ_ENOMEM; - - if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS) - return rc; - - *ptr_mutex = mutex; - return PJ_SUCCESS; -#else /* PJ_HAS_THREADS */ - return (pj_mutex_t*)1; -#endif -} - -/* - * pj_mutex_create_simple() - */ -PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, - const char *name, - pj_mutex_t **mutex ) -{ - return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); -} - -/* - * pj_mutex_create_recursive() - */ -PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, - const char *name, - pj_mutex_t **mutex ) -{ - return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); -} - -/* - * pj_mutex_lock() - */ -PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) -{ -#if PJ_HAS_THREADS - pj_status_t status; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - - PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", - pj_thread_this()->obj_name)); - - status = pthread_mutex_lock( &mutex->mutex ); - - PJ_LOG(6,(mutex->obj_name, - (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), - pj_thread_this()->obj_name)); - -#if PJ_DEBUG - if (status == PJ_SUCCESS) { - mutex->owner = pj_thread_this(); - ++mutex->nesting_level; - } -#endif - - if (status == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(status); -#else /* PJ_HAS_THREADS */ - pj_assert( mutex == (pj_mutex_t*)1 ); - return PJ_SUCCESS; -#endif -} - -/* - * pj_mutex_unlock() - */ -PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) -{ -#if PJ_HAS_THREADS - pj_status_t status; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - -#if PJ_DEBUG - pj_assert(mutex->owner == pj_thread_this()); - if (--mutex->nesting_level == 0) { - mutex->owner = NULL; - } -#endif - - PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", - pj_thread_this()->obj_name)); - - status = pthread_mutex_unlock( &mutex->mutex ); - if (status == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(status); - -#else /* PJ_HAS_THREADS */ - pj_assert( mutex == (pj_mutex_t*)1 ); - return PJ_SUCCESS; -#endif -} - -/* - * pj_mutex_trylock() - */ -PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) -{ -#if PJ_HAS_THREADS - int status; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - - status = pthread_mutex_trylock( &mutex->mutex ); - - if (status==0) { - PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", - pj_thread_this()->obj_name)); - -#if PJ_DEBUG - mutex->owner = pj_thread_this(); - ++mutex->nesting_level; -#endif - } - - if (status==0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(status); -#else /* PJ_HAS_THREADS */ - pj_assert( mutex == (pj_mutex_t*)1); - return PJ_SUCCESS; -#endif -} - -/* - * pj_mutex_destroy() - */ -PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) -{ - int status; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - -#if PJ_HAS_THREADS - PJ_LOG(6,(mutex->obj_name, "Mutex destroyed")); - status = pthread_mutex_destroy( &mutex->mutex ); - if (status == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(status); -#else - pj_assert( mutex == (pj_mutex_t*)1 ); - status = PJ_SUCCESS; - return status; -#endif -} - -#if PJ_DEBUG -PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) -{ -#if PJ_HAS_THREADS - return mutex->owner == pj_thread_this(); -#else - return 1; -#endif -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 - -/* - * pj_sem_create() - */ -PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, - const char *name, - unsigned initial, - unsigned max, - pj_sem_t **ptr_sem) -{ -#if PJ_HAS_THREADS - pj_sem_t *sem; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL); - - sem = pj_pool_alloc(pool, sizeof(*sem)); - if (!sem) return PJ_ENOMEM; - - if (sem_init( &sem->sem, 0, initial) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); - - /* Set name. */ - if (!name) { - name = "sem%p"; - } - if (strchr(name, '%')) { - pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); - } else { - strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); - sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - - PJ_LOG(6, (sem->obj_name, "Semaphore created")); - - *ptr_sem = sem; - return PJ_SUCCESS; -#else - *ptr_sem = (pj_sem_t*)1; - return PJ_SUCCESS; -#endif -} - -/* - * pj_sem_wait() - */ -PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) -{ -#if PJ_HAS_THREADS - int result; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", - pj_thread_this()->obj_name)); - - result = sem_wait( &sem->sem ); - - if (result == 0) { - PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", - pj_thread_this()->obj_name)); - } else { - PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", - pj_thread_this()->obj_name)); - } - - if (result == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); -#else - pj_assert( sem == (pj_sem_t*) 1 ); - return PJ_SUCCESS; -#endif -} - -/* - * pj_sem_trywait() - */ -PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) -{ -#if PJ_HAS_THREADS - int result; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - result = sem_trywait( &sem->sem ); - - if (result == 0) { - PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", - pj_thread_this()->obj_name)); - } - if (result == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); -#else - pj_assert( sem == (pj_sem_t*)1 ); - return PJ_SUCCESS; -#endif -} - -/* - * pj_sem_post() - */ -PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) -{ -#if PJ_HAS_THREADS - int result; - PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", - pj_thread_this()->obj_name)); - result = sem_post( &sem->sem ); - - if (result == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); -#else - pj_assert( sem == (pj_sem_t*) 1); - return PJ_SUCCESS; -#endif -} - -/* - * pj_sem_destroy() - */ -PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) -{ -#if PJ_HAS_THREADS - int result; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", - pj_thread_this()->obj_name)); - result = sem_destroy( &sem->sem ); - - if (result == 0) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); -#else - pj_assert( sem == (pj_sem_t*) 1 ); - return PJ_SUCCESS; -#endif -} - -#endif /* PJ_HAS_SEMAPHORE */ - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 - -/* - * pj_event_create() - */ -PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, - pj_bool_t manual_reset, pj_bool_t initial, - pj_event_t **ptr_event) -{ - pj_assert(!"Not supported!"); - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(name); - PJ_UNUSED_ARG(manual_reset); - PJ_UNUSED_ARG(initial); - PJ_UNUSED_ARG(ptr_event); - return PJ_EINVALIDOP; -} - -/* - * pj_event_wait() - */ -PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) -{ - PJ_UNUSED_ARG(event); - return PJ_EINVALIDOP; -} - -/* - * pj_event_trywait() - */ -PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) -{ - PJ_UNUSED_ARG(event); - return PJ_EINVALIDOP; -} - -/* - * pj_event_set() - */ -PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) -{ - PJ_UNUSED_ARG(event); - return PJ_EINVALIDOP; -} - -/* - * pj_event_pulse() - */ -PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) -{ - PJ_UNUSED_ARG(event); - return PJ_EINVALIDOP; -} - -/* - * pj_event_reset() - */ -PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) -{ - PJ_UNUSED_ARG(event); - return PJ_EINVALIDOP; -} - -/* - * pj_event_destroy() - */ -PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) -{ - PJ_UNUSED_ARG(event); - return PJ_EINVALIDOP; -} - -#endif /* PJ_HAS_EVENT_OBJ */ - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 -/* - * Terminal - */ - -/** - * Set terminal color. - */ -PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) -{ - PJ_UNUSED_ARG(color); - return PJ_EINVALIDOP; -} - -/** - * Get current terminal foreground color. - */ -PJ_DEF(pj_color_t) pj_term_get_color(void) -{ - return 0; -} - -#endif /* PJ_TERM_HAS_COLOR */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_unix.c 11 10/29/05 10:27p Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/os_core_unix.c $ + * + * 11 10/29/05 10:27p Bennylp + * Fixed misc warnings. + * + * 10 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 9 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +# include +#endif + +#include // getpid() +#include // errno + +#define __USE_GNU +#include + +#define THIS_FILE "osunix" + +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + pthread_t thread; + pj_thread_proc *proc; + void *arg; + + pj_mutex_t *suspended_mutex; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif +}; + +struct pj_atomic_t +{ + pj_mutex_t *mutex; + pj_atomic_value_t value; +}; + +struct pj_mutex_t +{ + pthread_mutex_t mutex; + char obj_name[PJ_MAX_OBJ_NAME]; +#if PJ_DEBUG + int nesting_level; + pj_thread_t *owner; +#endif +}; + +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +struct pj_sem_t +{ + sem_t sem; + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_SEMAPHORE */ + +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 +struct pj_event_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; +}; +#endif /* PJ_HAS_EVENT_OBJ */ + + +#if PJ_HAS_THREADS + static pj_thread_t main_thread; + static long thread_tls_id; + static pj_mutex_t critical_section; +#else +# define MAX_THREADS 32 + static int tls_flag[MAX_THREADS]; + static void *tls[MAX_THREADS]; +#endif + +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type); + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + char dummy_guid[PJ_GUID_MAX_LENGTH]; + pj_str_t guid; + pj_status_t rc; + + PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); + +#if PJ_HAS_THREADS + /* Init this thread's TLS. */ + if ((rc=pj_thread_init()) != 0) { + return rc; + } + + /* Critical section. */ + if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_SIMPLE)) != 0) + return rc; + +#endif + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Init random seed. */ + pj_srand( clock() ); + + /* Startup GUID. */ + guid.ptr = dummy_guid; + pj_generate_unique_string( &guid ); + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Startup timestamp */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + { + pj_timestamp dummy_ts; + if ((rc=pj_get_timestamp(&dummy_ts)) != 0) { + return rc; + } + } +#endif + + return PJ_SUCCESS; +} + +/* + * pj_getpid(void) + */ +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + PJ_CHECK_STACK(); + return getpid(); +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + char stack_ptr; + pj_status_t rc; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + *ptr_thread = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_memset(desc, 0, sizeof(struct pj_thread_t)); + thread->thread = pthread_self(); + + if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread); + else + pj_sprintf(thread->obj_name, "thr%p", (void*)thread->thread); + + rc = pj_thread_local_set(thread_tls_id, thread); + if (rc != PJ_SUCCESS) + return rc; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + *ptr_thread = thread; + return PJ_SUCCESS; +#else + pj_thread_t *thread = (pj_thread_t*)desc; + *ptr_thread = thread; + return PJ_SUCCESS; +#endif +} + +/* + * pj_thread_init(void) + */ +pj_status_t pj_thread_init(void) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_thread_t *dummy; + + rc = pj_thread_local_alloc(&thread_tls_id ); + if (rc != PJ_SUCCESS) { + return rc; + } + return pj_thread_register("thr%p", (long*)&main_thread, &dummy); +#else + PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!")); + return PJ_EINVALIDOP; +#endif +} + +#if PJ_HAS_THREADS +/* + * thread_main() + * + * This is the main entry for all threads. + */ +static void *thread_main(void *param) +{ + pj_thread_t *rec = param; + void *result; + pj_status_t rc; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_start = (char*)&rec; +#endif + + /* Set current thread id. */ + rc = pj_thread_local_set(thread_tls_id, rec); + if (rc != PJ_SUCCESS) { + pj_assert(!"Thread TLS ID is not set (pj_init() error?)"); + } + + /* Check if suspension is required. */ + if (rec->suspended_mutex) + pj_mutex_lock(rec->suspended_mutex); + + PJ_LOG(6,(rec->obj_name, "Thread started")); + + /* Call user's entry! */ + result = (void*) (*rec->proc)(rec->arg); + + /* Done. */ + PJ_LOG(6,(rec->obj_name, "Thread quitting")); + return result; +} +#endif + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **ptr_thread) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec; + int rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL); + + /* Create thread record and assign name for the thread */ + rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t)); + if (!rec) + return PJ_ENOMEM; + + /* Set name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); + } else { + strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); + rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL; + rec->stk_max_usage = 0; +#endif + + /* Emulate suspended thread with mutex. */ + if (flags & PJ_THREAD_SUSPENDED) { + rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex); + if (rc != PJ_SUCCESS) + return rc; + + pj_mutex_lock(rec->suspended_mutex); + } else { + pj_assert(rec->suspended_mutex == NULL); + } + + PJ_LOG(6, (rec->obj_name, "Thread created")); + + /* Create the thread. */ + rec->proc = proc; + rec->arg = arg; + rc = pthread_create( &rec->thread, NULL, thread_main, rec); + if (rc != 0) + return PJ_RETURN_OS_ERROR(rc); + + *ptr_thread = rec; + return PJ_SUCCESS; +#else + pj_assert(!"Threading is disabled!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, ""); + + return rec->obj_name; +#else + return ""; +#endif +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + pj_status_t rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + rc = pj_mutex_unlock(p->suspended_mutex); + + return rc; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = pj_thread_local_get(thread_tls_id); + pj_assert(rec != NULL); + + /* + * MUST NOT check stack because this function is called + * by PJ_CHECK_STACK() itself!!! + * + */ + + return rec; +#else + pj_assert(!"Threading is not enabled!"); + return NULL; +#endif +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ +#if PJ_HAS_THREADS + pj_thread_t *rec = (pj_thread_t *)p; + void *ret; + int result; + + PJ_CHECK_STACK(); + + PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); + result = pthread_join( rec->thread, &ret); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(result); +#else + PJ_CHECK_STACK(); + pj_assert(!"No multithreading support!"); + return PJ_EINVALIDOP; +#endif +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) +{ + /* This function is used to destroy thread handle in other platforms. + * I suppose there's nothing to do here.. + */ + PJ_CHECK_STACK(); + return PJ_SUCCESS; +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + PJ_CHECK_STACK(); + return usleep(msec * 1000); +} + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 +/* + * pj_thread_check_stack() + * Implementation for PJ_CHECK_STACK() + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } +} + +/* + * pj_thread_get_stack_max_usage() + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * pj_thread_get_stack_info() + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, + const char **file, + int *line ) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_atomic_create() + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **ptr_atomic) +{ + pj_status_t rc; + pj_atomic_t *atomic_var = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t)); + if (!atomic_var) + return PJ_ENOMEM; + +#if PJ_HAS_THREADS + rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex); + if (rc != PJ_SUCCESS) + return rc; +#endif + atomic_var->value = initial; + + *ptr_atomic = atomic_var; + return PJ_SUCCESS; +} + +/* + * pj_atomic_destroy() + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var ) +{ + PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL); +#if PJ_HAS_THREADS + return pj_mutex_destroy( atomic_var->mutex ); +#else + return 0; +#endif +} + +/* + * pj_atomic_set() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_set(pj_atomic_t *atomic_var, + pj_atomic_value_t value) +{ + pj_atomic_value_t oldval; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + oldval = atomic_var->value; + atomic_var->value = value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + return oldval; +} + +/* + * pj_atomic_get() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t oldval; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + oldval = atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + return oldval; +} + +/* + * pj_atomic_inc() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t newval; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + newval = ++atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + return newval; +} + +/* + * pj_atomic_dec() + */ +PJ_DEF(pj_atomic_value_t) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + pj_atomic_value_t newval; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + +#if PJ_HAS_THREADS + pj_mutex_lock( atomic_var->mutex ); +#endif + newval = --atomic_var->value; +#if PJ_HAS_THREADS + pj_mutex_unlock( atomic_var->mutex); +#endif + return newval; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index) +{ +#if PJ_HAS_THREADS + pthread_key_t key; + int rc; + + PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL); + + pj_assert( sizeof(pthread_key_t) <= sizeof(long)); + if ((rc=pthread_key_create(&key, NULL)) != 0) + return PJ_RETURN_OS_ERROR(rc); + + *p_index = key; + return PJ_SUCCESS; +#else + int i; + for (i=0; i= 0 && index < MAX_THREADS); + tls[index] = value; + return PJ_SUCCESS; +#endif +} + +PJ_DEF(void*) pj_thread_local_get(long index) +{ + //Can't check stack because this function is called + //by PJ_CHECK_STACK() itself!!! + //PJ_CHECK_STACK(); +#if PJ_HAS_THREADS + return pthread_getspecific(index); +#else + pj_assert(index >= 0 && index < MAX_THREADS); + return tls[index]; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void) pj_enter_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_lock(&critical_section); +#endif +} + +PJ_DEF(void) pj_leave_critical_section(void) +{ +#if PJ_HAS_THREADS + pj_mutex_unlock(&critical_section); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type) +{ +#if PJ_HAS_THREADS + pthread_mutexattr_t attr; + int rc; + + PJ_CHECK_STACK(); + + pthread_mutexattr_init(&attr); + + if (type == PJ_MUTEX_SIMPLE) { +#if defined(PJ_LINUX) && PJ_LINUX!=0 + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP); +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); +#endif + } else { +#if defined(PJ_LINUX) && PJ_LINUX!=0 + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); +#else + rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#endif + } + + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + + rc = pthread_mutex_init(&mutex->mutex, &attr); + if (rc != 0) { + return PJ_RETURN_OS_ERROR(rc); + } + +#if PJ_DEBUG + /* Set owner. */ + mutex->nesting_level = 0; + mutex->owner = NULL; +#endif + + /* Set name. */ + if (!name) { + name = "mtx%p"; + } + if (strchr(name, '%')) { + pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); + } else { + strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); + mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (mutex->obj_name, "Mutex created")); + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_create() + */ +PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **ptr_mutex) +{ +#if PJ_HAS_THREADS + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL); + + mutex = pj_pool_alloc(pool, sizeof(*mutex)); + if (!mutex) return PJ_ENOMEM; + + if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS) + return rc; + + *ptr_mutex = mutex; + return PJ_SUCCESS; +#else /* PJ_HAS_THREADS */ + return (pj_mutex_t*)1; +#endif +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +/* + * pj_mutex_create_recursive() + */ +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", + pj_thread_this()->obj_name)); + + status = pthread_mutex_lock( &mutex->mutex ); + + PJ_LOG(6,(mutex->obj_name, + (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"), + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + if (status == PJ_SUCCESS) { + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; + } +#endif + + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + pj_assert(mutex->owner == pj_thread_this()); + if (--mutex->nesting_level == 0) { + mutex->owner = NULL; + } +#endif + + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", + pj_thread_this()->obj_name)); + + status = pthread_mutex_unlock( &mutex->mutex ); + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); + +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + int status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + status = pthread_mutex_trylock( &mutex->mutex ); + + if (status==0) { + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; +#endif + } + + if (status==0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else /* PJ_HAS_THREADS */ + pj_assert( mutex == (pj_mutex_t*)1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + int status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_HAS_THREADS + PJ_LOG(6,(mutex->obj_name, "Mutex destroyed")); + status = pthread_mutex_destroy( &mutex->mutex ); + if (status == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(status); +#else + pj_assert( mutex == (pj_mutex_t*)1 ); + status = PJ_SUCCESS; + return status; +#endif +} + +#if PJ_DEBUG +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ +#if PJ_HAS_THREADS + return mutex->owner == pj_thread_this(); +#else + return 1; +#endif +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +/* + * pj_sem_create() + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **ptr_sem) +{ +#if PJ_HAS_THREADS + pj_sem_t *sem; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(*sem)); + if (!sem) return PJ_ENOMEM; + + if (sem_init( &sem->sem, 0, initial) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + + /* Set name. */ + if (!name) { + name = "sem%p"; + } + if (strchr(name, '%')) { + pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); + } else { + strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); + sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (sem->obj_name, "Semaphore created")); + + *ptr_sem = sem; + return PJ_SUCCESS; +#else + *ptr_sem = (pj_sem_t*)1; + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_wait() + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = sem_wait( &sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_trywait() + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + result = sem_trywait( &sem->sem ); + + if (result == 0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*)1 ); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_post() + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); + result = sem_post( &sem->sem ); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1); + return PJ_SUCCESS; +#endif +} + +/* + * pj_sem_destroy() + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ +#if PJ_HAS_THREADS + int result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); + result = sem_destroy( &sem->sem ); + + if (result == 0) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); +#else + pj_assert( sem == (pj_sem_t*) 1 ); + return PJ_SUCCESS; +#endif +} + +#endif /* PJ_HAS_SEMAPHORE */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 + +/* + * pj_event_create() + */ +PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name, + pj_bool_t manual_reset, pj_bool_t initial, + pj_event_t **ptr_event) +{ + pj_assert(!"Not supported!"); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(name); + PJ_UNUSED_ARG(manual_reset); + PJ_UNUSED_ARG(initial); + PJ_UNUSED_ARG(ptr_event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_wait() + */ +PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_trywait() + */ +PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_set() + */ +PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_pulse() + */ +PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_reset() + */ +PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +/* + * pj_event_destroy() + */ +PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) +{ + PJ_UNUSED_ARG(event); + return PJ_EINVALIDOP; +} + +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 +/* + * Terminal + */ + +/** + * Set terminal color. + */ +PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) +{ + PJ_UNUSED_ARG(color); + return PJ_EINVALIDOP; +} + +/** + * Get current terminal foreground color. + */ +PJ_DEF(pj_color_t) pj_term_get_color(void) +{ + return 0; +} + +#endif /* PJ_TERM_HAS_COLOR */ + diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c index dd892b5d9..1c37b0440 100644 --- a/pjlib/src/pj/os_core_win32.c +++ b/pjlib/src/pj/os_core_win32.c @@ -1,1182 +1,1191 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_win32.c 12 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/os_core_win32.c $ - * - * 12 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 11 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 10 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 9 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 -# include -#endif - -#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 -# include -#endif - -/* - * Implementation of pj_thread_t. - */ -struct pj_thread_t -{ - char obj_name[PJ_MAX_OBJ_NAME]; - HANDLE hthread; - DWORD idthread; - pj_thread_proc *proc; - void *arg; - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - pj_uint32_t stk_size; - pj_uint32_t stk_max_usage; - char *stk_start; - const char *caller_file; - int caller_line; -#endif -}; - - -/* - * Implementation of pj_mutex_t. - */ -struct pj_mutex_t -{ -#if PJ_WIN32_WINNT >= 0x0400 - CRITICAL_SECTION crit; -#else - HANDLE hMutex; -#endif - char obj_name[PJ_MAX_OBJ_NAME]; -#if PJ_DEBUG - int nesting_level; - pj_thread_t *owner; -#endif -}; - -/* - * Implementation of pj_sem_t. - */ -typedef struct pj_sem_t -{ - HANDLE hSemaphore; - char obj_name[PJ_MAX_OBJ_NAME]; -} pj_mem_t; - - -/* - * Implementation of pj_event_t. - */ -struct pj_event_t -{ - HANDLE hEvent; - char obj_name[PJ_MAX_OBJ_NAME]; -}; - -/* - * Implementation of pj_atomic_t. - */ -struct pj_atomic_t -{ - long value; -}; - -/* - * Static global variables. - */ -static pj_thread_desc main_thread; -static long thread_tls_id; -static pj_mutex_t critical_section_mutex; - - -/* - * Some static prototypes. - */ -static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name); - - -/* - * pj_init(void). - * Init PJLIB! - */ -PJ_DEF(pj_status_t) pj_init(void) -{ - WSADATA wsa; - char dummy_guid[32]; /* use maximum GUID length */ - pj_str_t guid; - pj_status_t rc; - - PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); - - /* Init Winsock.. */ - if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { - PJ_LOG(1, ("pj_init", "Winsock initialization has returned an error")); - return PJ_RETURN_OS_ERROR(WSAGetLastError()); - } - - /* Init this thread's TLS. */ - if ((rc=pj_thread_init()) != PJ_SUCCESS) { - PJ_LOG(1, ("pj_init", "Thread initialization has returned an error")); - return rc; - } - - /* Init random seed. */ - pj_srand( GetCurrentProcessId() ); - - /* Startup GUID. */ - guid.ptr = dummy_guid; - pj_generate_unique_string( &guid ); - - /* Initialize critical section. */ - if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS) - return rc; - - /* Initialize exception ID for the pool. - * Must do so after critical section is configured. - */ - rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); - if (rc != PJ_SUCCESS) - return rc; - - /* Startup timestamp */ -#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 - { - pj_timestamp dummy_ts; - if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) { - PJ_LOG(1, ("pj_init", "Unable to initialize timestamp")); - return rc; - } - } -#endif - - return PJ_SUCCESS; -} - -/* - * pj_getpid(void) - */ -PJ_DEF(pj_uint32_t) pj_getpid(void) -{ - PJ_CHECK_STACK(); - return GetCurrentProcessId(); -} - -/* - * pj_thread_register(..) - */ -PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, - pj_thread_desc desc, - pj_thread_t **thread_ptr) -{ - char stack_ptr; - pj_thread_t *thread = (pj_thread_t *)desc; - pj_str_t thread_name = pj_str((char*)cstr_thread_name); - - /* Size sanity check. */ - if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { - pj_assert(!"Not enough pj_thread_desc size!"); - return PJ_EBUG; - } - - /* If a thread descriptor has been registered before, just return it. */ - if (pj_thread_local_get (thread_tls_id) != 0) { - *thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); - return PJ_SUCCESS; - } - - /* Initialize and set the thread entry. */ - pj_memset(desc, 0, sizeof(pj_thread_desc)); - thread->hthread = GetCurrentThread(); - thread->idthread = GetCurrentThreadId(); - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - thread->stk_start = &stack_ptr; - thread->stk_size = 0xFFFFFFFFUL; - thread->stk_max_usage = 0; -#else - stack_ptr = '\0'; -#endif - - if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) - pj_sprintf(thread->obj_name, cstr_thread_name, thread->idthread); - else - pj_sprintf(thread->obj_name, "thr%p", (void*)thread->idthread); - - pj_thread_local_set(thread_tls_id, thread); - - *thread_ptr = thread; - return PJ_SUCCESS; -} - -/* - * pj_thread_init(void) - */ -pj_status_t pj_thread_init(void) -{ - pj_status_t rc; - pj_thread_t *thread; - - rc = pj_thread_local_alloc(&thread_tls_id); - if (rc != PJ_SUCCESS) - return rc; - - return pj_thread_register("thr%p", main_thread, &thread); -} - -static DWORD WINAPI thread_main(void *param) -{ - pj_thread_t *rec = param; - DWORD result; - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - rec->stk_start = (char*)&rec; -#endif - - PJ_LOG(6,(rec->obj_name, "Thread started")); - - pj_thread_local_set(thread_tls_id, rec); - result = (*rec->proc)(rec->arg); - - PJ_LOG(6,(rec->obj_name, "Thread quitting")); - return (DWORD)result; -} - -/* - * pj_thread_create(...) - */ -PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, - const char *thread_name, - pj_thread_proc *proc, - void *arg, - pj_size_t stack_size, - unsigned flags, - pj_thread_t **thread_ptr) -{ - DWORD dwflags = 0; - pj_thread_t *rec; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL); - - /* Set flags */ - if (flags & PJ_THREAD_SUSPENDED) - dwflags |= CREATE_SUSPENDED; - - /* Create thread record and assign name for the thread */ - rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t)); - if (!rec) - return PJ_ENOMEM; - - /* Set name. */ - if (!thread_name) - thread_name = "thr%p"; - - if (strchr(thread_name, '%')) { - pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); - } else { - strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); - rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - - PJ_LOG(6, (rec->obj_name, "Thread created")); - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 - rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL; - rec->stk_max_usage = 0; -#endif - - /* Create the thread. */ - rec->proc = proc; - rec->arg = arg; - rec->hthread = CreateThread(NULL, stack_size, - thread_main, rec, - dwflags, &rec->idthread); - if (rec->hthread == NULL) - return PJ_RETURN_OS_ERROR(GetLastError()); - - /* Success! */ - *thread_ptr = rec; - return PJ_SUCCESS; -} - -/* - * pj_thread-get_name() - */ -PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) -{ - pj_thread_t *rec = (pj_thread_t*)p; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(p, ""); - - return rec->obj_name; -} - -/* - * pj_thread_resume() - */ -PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) -{ - pj_thread_t *rec = (pj_thread_t*)p; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(p, PJ_EINVAL); - - if (ResumeThread(rec->hthread) == (DWORD)-1) - return PJ_RETURN_OS_ERROR(GetLastError()); - else - return PJ_SUCCESS; -} - -/* - * pj_thread_this() - */ -PJ_DEF(pj_thread_t*) pj_thread_this(void) -{ - pj_thread_t *rec = pj_thread_local_get(thread_tls_id); - pj_assert(rec != NULL); - - /* - * MUST NOT check stack because this function is called - * by PJ_CHECK_STACK() itself!!! - * - */ - - return rec; -} - -/* - * pj_thread_join() - */ -PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) -{ - pj_thread_t *rec = (pj_thread_t *)p; - DWORD rc; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(p, PJ_EINVAL); - - PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); - - rc = WaitForSingleObject(rec->hthread, INFINITE); - - if (rc==WAIT_OBJECT_0) - return PJ_SUCCESS; - else if (rc==WAIT_TIMEOUT) - return PJ_ETIMEDOUT; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_thread_destroy() - */ -PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) -{ - pj_thread_t *rec = (pj_thread_t *)p; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(p, PJ_EINVAL); - - if (CloseHandle(rec->hthread) == TRUE) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_thread_sleep() - */ -PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) -{ - PJ_CHECK_STACK(); - Sleep(msec); - return PJ_SUCCESS; -} - -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 -/* - * pj_thread_check_stack() - * Implementation for PJ_CHECK_STACK() - */ -PJ_DEF(void) pj_thread_check_stack(const char *file, int line) -{ - char stk_ptr; - pj_uint32_t usage; - pj_thread_t *thread = pj_thread_this(); - - pj_assert(thread); - - /* Calculate current usage. */ - usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : - thread->stk_start - &stk_ptr; - - /* Assert if stack usage is dangerously high. */ - pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); - - /* Keep statistic. */ - if (usage > thread->stk_max_usage) { - thread->stk_max_usage = usage; - thread->caller_file = file; - thread->caller_line = line; - } - -} - -/* - * pj_thread_get_stack_max_usage() - */ -PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) -{ - return thread->stk_max_usage; -} - -/* - * pj_thread_get_stack_info() - */ -PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, - const char **file, - int *line ) -{ - pj_assert(thread); - - *file = thread->caller_file; - *line = thread->caller_line; - return 0; -} - -#endif /* PJ_OS_HAS_CHECK_STACK */ - - -/////////////////////////////////////////////////////////////////////////////// - -/* - * pj_atomic_create() - */ -PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, - pj_atomic_value_t initial, - pj_atomic_t **atomic_ptr) -{ - pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t)); - if (!atomic_var) - return PJ_ENOMEM; - - atomic_var->value = initial; - *atomic_ptr = atomic_var; - - return PJ_SUCCESS; -} - -/* - * pj_atomic_destroy() - */ -PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) -{ - PJ_UNUSED_ARG(var); - PJ_ASSERT_RETURN(var, PJ_EINVAL); - - return 0; -} - -/* - * pj_atomic_set() - */ -PJ_DEF(long) pj_atomic_set(pj_atomic_t *atomic_var, long value) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - - return InterlockedExchange(&atomic_var->value, value); -} - -/* - * pj_atomic_get() - */ -PJ_DEF(long) pj_atomic_get(pj_atomic_t *atomic_var) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - - return atomic_var->value; -} - -/* - * pj_atomic_inc() - */ -PJ_DEF(long) pj_atomic_inc(pj_atomic_t *atomic_var) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - -#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 - return InterlockedIncrement(&atomic_var->value); -#else -# error Fix Me -#endif -} - -/* - * pj_atomic_dec() - */ -PJ_DEF(long) pj_atomic_dec(pj_atomic_t *atomic_var) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(atomic_var, 0); - -#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 - return InterlockedDecrement(&atomic_var->value); -#else -# error Fix me -#endif -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * pj_thread_local_alloc() - */ -PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) -{ - PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL); - - //Can't check stack because this function is called in the - //beginning before main thread is initialized. - //PJ_CHECK_STACK(); - - *index = TlsAlloc(); - - if (*index == TLS_OUT_OF_INDEXES) - return PJ_RETURN_OS_ERROR(GetLastError()); - else - return PJ_SUCCESS; -} - -/* - * pj_thread_local_free() - */ -PJ_DEF(void) pj_thread_local_free(long index) -{ - PJ_CHECK_STACK(); - TlsFree(index); -} - -/* - * pj_thread_local_set() - */ -PJ_DEF(void) pj_thread_local_set(long index, void *value) -{ - //Can't check stack because this function is called in the - //beginning before main thread is initialized. - //PJ_CHECK_STACK(); - TlsSetValue(index, value); -} - -/* - * pj_thread_local_get() - */ -PJ_DEF(void*) pj_thread_local_get(long index) -{ - //Can't check stack because this function is called - //by PJ_CHECK_STACK() itself!!! - //PJ_CHECK_STACK(); - return TlsGetValue(index); -} - -/////////////////////////////////////////////////////////////////////////////// -static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name) -{ - - PJ_CHECK_STACK(); - -#if PJ_WIN32_WINNT >= 0x0400 - InitializeCriticalSection(&mutex->crit); -#else - mutex->hMutex = CreateMutex(NULL, FALSE, NULL); - if (!mutex->hMutex) { - return PJ_RETURN_OS_ERROR(GetLastError()); - } -#endif - -#if PJ_DEBUG - /* Set owner. */ - mutex->nesting_level = 0; - mutex->owner = NULL; -#endif - - /* Set name. */ - if (!name) { - name = "mtx%p"; - } - if (strchr(name, '%')) { - pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); - } else { - strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); - mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - - PJ_LOG(6, (mutex->obj_name, "Mutex created")); - return PJ_SUCCESS; -} - -/* - * pj_mutex_create() - */ -PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, - const char *name, - int type, - pj_mutex_t **mutex_ptr) -{ - pj_status_t rc; - pj_mutex_t *mutex; - - PJ_UNUSED_ARG(type); - PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL); - - mutex = pj_pool_alloc(pool, sizeof(*mutex)); - if (!mutex) - return PJ_ENOMEM; - - rc = init_mutex(mutex, name); - if (rc != PJ_SUCCESS) - return rc; - - *mutex_ptr = mutex; - - return PJ_SUCCESS; -} - -/* - * pj_mutex_create_simple() - */ -PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, - const char *name, - pj_mutex_t **mutex ) -{ - return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); -} - -/* - * pj_mutex_create_recursive() - */ -PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, - const char *name, - pj_mutex_t **mutex ) -{ - return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); -} - -/* - * pj_mutex_lock() - */ -PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) -{ - pj_status_t status; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - - PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", - pj_thread_this()->obj_name)); - -#if PJ_WIN32_WINNT >= 0x0400 - EnterCriticalSection(&mutex->crit); - status=PJ_SUCCESS; -#else - if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0) - status = PJ_SUCCESS; - else - status = PJ_STATUS_FROM_OS(GetLastError()); - -#endif - PJ_LOG(6,(mutex->obj_name, - (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"), - pj_thread_this()->obj_name)); - -#if PJ_DEBUG - if (status == PJ_SUCCESS) { - mutex->owner = pj_thread_this(); - ++mutex->nesting_level; - } -#endif - - return status; -} - -/* - * pj_mutex_unlock() - */ -PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) -{ - pj_status_t status; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - -#if PJ_DEBUG - pj_assert(mutex->owner == pj_thread_this()); - if (--mutex->nesting_level == 0) { - mutex->owner = NULL; - } -#endif - - PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", - pj_thread_this()->obj_name)); - -#if PJ_WIN32_WINNT >= 0x0400 - LeaveCriticalSection(&mutex->crit); - status=PJ_SUCCESS; -#else - status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS : - PJ_STATUS_FROM_OS(GetLastError()); -#endif - return status; -} - -/* - * pj_mutex_trylock() - */ -PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) -{ - pj_status_t status; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - -#if PJ_WIN32_WINNT >= 0x0400 - status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN; -#else - status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? - PJ_SUCCESS : PJ_ETIMEDOUT; -#endif - if (status==PJ_SUCCESS) { - PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", - pj_thread_this()->obj_name)); - -#if PJ_DEBUG - mutex->owner = pj_thread_this(); - ++mutex->nesting_level; -#endif - } - return status; -} - -/* - * pj_mutex_destroy() - */ -PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(mutex, PJ_EINVAL); - - PJ_LOG(6,(mutex->obj_name, "Mutex destroyed")); - -#if PJ_WIN32_WINNT >= 0x0400 - DeleteCriticalSection(&mutex->crit); - return PJ_SUCCESS; -#else - return CloseHandle(mutex->hMutex) ? PJ_SUCCESS : - PJ_RETURN_OS_ERROR(GetLastError()); -#endif -} - -#if PJ_DEBUG -/* - * pj_mutex_is_locked() - */ -PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) -{ - return mutex->owner == pj_thread_this(); -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -/* - * pj_enter_critical_section() - */ -PJ_DEF(void) pj_enter_critical_section(void) -{ - pj_mutex_lock(&critical_section_mutex); -} - - -/* - * pj_leave_critical_section() - */ -PJ_DEF(void) pj_leave_critical_section(void) -{ - pj_mutex_unlock(&critical_section_mutex); -} - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 - -/* - * pj_sem_create() - */ -PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, - const char *name, - unsigned initial, - unsigned max, - pj_sem_t **sem_ptr) -{ - pj_sem_t *sem; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL); - - sem = pj_pool_alloc(pool, sizeof(*sem)); - sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL); - if (!sem->hSemaphore) - return PJ_RETURN_OS_ERROR(GetLastError()); - - /* Set name. */ - if (!name) { - name = "sem%p"; - } - if (strchr(name, '%')) { - pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); - } else { - strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); - sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - - PJ_LOG(6, (sem->obj_name, "Semaphore created")); - - *sem_ptr = sem; - return PJ_SUCCESS; -} - -static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) -{ - DWORD result; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", - pj_thread_this()->obj_name)); - - result = WaitForSingleObject(sem->hSemaphore, timeout); - if (result == WAIT_OBJECT_0) { - PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", - pj_thread_this()->obj_name)); - } else { - PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", - pj_thread_this()->obj_name)); - } - - if (result==WAIT_OBJECT_0) - return PJ_SUCCESS; - else if (result==WAIT_TIMEOUT) - return PJ_ETIMEDOUT; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_sem_wait() - */ -PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - return pj_sem_wait_for(sem, INFINITE); -} - -/* - * pj_sem_trywait() - */ -PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - return pj_sem_wait_for(sem, 0); -} - -/* - * pj_sem_post() - */ -PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", - pj_thread_this()->obj_name)); - - if (ReleaseSemaphore(sem->hSemaphore, 1, NULL)) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_sem_destroy() - */ -PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sem, PJ_EINVAL); - - PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", - pj_thread_this()->obj_name)); - - if (CloseHandle(sem->hSemaphore)) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -#endif /* PJ_HAS_SEMAPHORE */ -/////////////////////////////////////////////////////////////////////////////// - - -#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 - -/* - * pj_event_create() - */ -PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, - const char *name, - pj_bool_t manual_reset, - pj_bool_t initial, - pj_event_t **event_ptr) -{ - pj_event_t *event; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL); - - event = pj_pool_alloc(pool, sizeof(*event)); - if (!event) - return PJ_ENOMEM; - - event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, - initial?TRUE:FALSE, NULL); - - if (!event->hEvent) - return PJ_RETURN_OS_ERROR(GetLastError()); - - /* Set name. */ - if (!name) { - name = "evt%p"; - } - if (strchr(name, '%')) { - pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event); - } else { - strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME); - event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; - } - - PJ_LOG(6, (event->obj_name, "Event created")); - - *event_ptr = event; - return PJ_SUCCESS; -} - -static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout) -{ - DWORD result; - - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(event, PJ_EINVAL); - - PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", - pj_thread_this()->obj_name)); - - result = WaitForSingleObject(event->hEvent, timeout); - if (result == WAIT_OBJECT_0) { - PJ_LOG(6, (event->obj_name, "Event: thread %s is released", - pj_thread_this()->obj_name)); - } else { - PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", - pj_thread_this()->obj_name)); - } - - if (result==WAIT_OBJECT_0) - return PJ_SUCCESS; - else if (result==WAIT_TIMEOUT) - return PJ_ETIMEDOUT; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_event_wait() - */ -PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) -{ - PJ_ASSERT_RETURN(event, PJ_EINVAL); - - return pj_event_wait_for(event, INFINITE); -} - -/* - * pj_event_trywait() - */ -PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) -{ - PJ_ASSERT_RETURN(event, PJ_EINVAL); - - return pj_event_wait_for(event, 0); -} - -/* - * pj_event_set() - */ -PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(event, PJ_EINVAL); - - PJ_LOG(6, (event->obj_name, "Setting event")); - - if (SetEvent(event->hEvent)) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_event_pulse() - */ -PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(event, PJ_EINVAL); - - PJ_LOG(6, (event->obj_name, "Pulsing event")); - - if (PulseEvent(event->hEvent)) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_event_reset() - */ -PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(event, PJ_EINVAL); - - PJ_LOG(6, (event->obj_name, "Event is reset")); - - if (ResetEvent(event->hEvent)) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_event_destroy() - */ -PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(event, PJ_EINVAL); - - PJ_LOG(6, (event->obj_name, "Event is destroying")); - - if (CloseHandle(event->hEvent)) - return PJ_SUCCESS; - else - return PJ_RETURN_OS_ERROR(GetLastError()); -} - -#endif /* PJ_HAS_EVENT_OBJ */ - -/////////////////////////////////////////////////////////////////////////////// -#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 -/* - * Terminal color - */ - -static WORD pj_color_to_os_attr(pj_color_t color) -{ - WORD attr = 0; - - if (color & PJ_TERM_COLOR_R) - attr |= FOREGROUND_RED; - if (color & PJ_TERM_COLOR_G) - attr |= FOREGROUND_GREEN; - if (color & PJ_TERM_COLOR_B) - attr |= FOREGROUND_BLUE; - if (color & PJ_TERM_COLOR_BRIGHT) - attr |= FOREGROUND_INTENSITY; - - return attr; -} - -static pj_color_t os_attr_to_pj_color(WORD attr) -{ - int color = 0; - - if (attr & FOREGROUND_RED) - color |= PJ_TERM_COLOR_R; - if (attr & FOREGROUND_GREEN) - color |= PJ_TERM_COLOR_G; - if (attr & FOREGROUND_BLUE) - color |= PJ_TERM_COLOR_B; - if (attr & FOREGROUND_INTENSITY) - color |= PJ_TERM_COLOR_BRIGHT; - - return color; -} - - -/* - * pj_term_set_color() - */ -PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) -{ - BOOL rc; - WORD attr = 0; - - PJ_CHECK_STACK(); - - attr = pj_color_to_os_attr(color); - rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr); - return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); -} - -/* - * pj_term_get_color() - * Get current terminal foreground color. - */ -PJ_DEF(pj_color_t) pj_term_get_color(void) -{ - CONSOLE_SCREEN_BUFFER_INFO info; - - PJ_CHECK_STACK(); - - GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info); - return os_attr_to_pj_color(info.wAttributes); -} - -#endif /* PJ_TERM_HAS_COLOR */ +/* $Header: /pjproject-0.3/pjlib/src/pj/os_core_win32.c 12 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/os_core_win32.c $ + * + * 12 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 11 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 10 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 9 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include +#endif + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include +#endif + +/* + * Implementation of pj_thread_t. + */ +struct pj_thread_t +{ + char obj_name[PJ_MAX_OBJ_NAME]; + HANDLE hthread; + DWORD idthread; + pj_thread_proc *proc; + void *arg; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + pj_uint32_t stk_size; + pj_uint32_t stk_max_usage; + char *stk_start; + const char *caller_file; + int caller_line; +#endif +}; + + +/* + * Implementation of pj_mutex_t. + */ +struct pj_mutex_t +{ +#if PJ_WIN32_WINNT >= 0x0400 + CRITICAL_SECTION crit; +#else + HANDLE hMutex; +#endif + char obj_name[PJ_MAX_OBJ_NAME]; +#if PJ_DEBUG + int nesting_level; + pj_thread_t *owner; +#endif +}; + +/* + * Implementation of pj_sem_t. + */ +typedef struct pj_sem_t +{ + HANDLE hSemaphore; + char obj_name[PJ_MAX_OBJ_NAME]; +} pj_mem_t; + + +/* + * Implementation of pj_event_t. + */ +struct pj_event_t +{ + HANDLE hEvent; + char obj_name[PJ_MAX_OBJ_NAME]; +}; + +/* + * Implementation of pj_atomic_t. + */ +struct pj_atomic_t +{ + long value; +}; + +/* + * Static global variables. + */ +static pj_thread_desc main_thread; +static long thread_tls_id; +static pj_mutex_t critical_section_mutex; + + +/* + * Some static prototypes. + */ +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name); + + +/* + * pj_init(void). + * Init PJLIB! + */ +PJ_DEF(pj_status_t) pj_init(void) +{ + WSADATA wsa; + char dummy_guid[32]; /* use maximum GUID length */ + pj_str_t guid; + pj_status_t rc; + + PJ_LOG(5, ("pj_init", "Initializing PJ Library..")); + + /* Init Winsock.. */ + if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { + PJ_LOG(1, ("pj_init", "Winsock initialization has returned an error")); + return PJ_RETURN_OS_ERROR(WSAGetLastError()); + } + + /* Init this thread's TLS. */ + if ((rc=pj_thread_init()) != PJ_SUCCESS) { + PJ_LOG(1, ("pj_init", "Thread initialization has returned an error")); + return rc; + } + + /* Init random seed. */ + pj_srand( GetCurrentProcessId() ); + + /* Startup GUID. */ + guid.ptr = dummy_guid; + pj_generate_unique_string( &guid ); + + /* Initialize critical section. */ + if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS) + return rc; + + /* Initialize exception ID for the pool. + * Must do so after critical section is configured. + */ + rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION); + if (rc != PJ_SUCCESS) + return rc; + + /* Startup timestamp */ +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + { + pj_timestamp dummy_ts; + if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) { + PJ_LOG(1, ("pj_init", "Unable to initialize timestamp")); + return rc; + } + } +#endif + + return PJ_SUCCESS; +} + +/* + * pj_getpid(void) + */ +PJ_DEF(pj_uint32_t) pj_getpid(void) +{ + PJ_CHECK_STACK(); + return GetCurrentProcessId(); +} + +/* + * pj_thread_register(..) + */ +PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name, + pj_thread_desc desc, + pj_thread_t **thread_ptr) +{ + char stack_ptr; + pj_status_t rc; + pj_thread_t *thread = (pj_thread_t *)desc; + pj_str_t thread_name = pj_str((char*)cstr_thread_name); + + /* Size sanity check. */ + if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) { + pj_assert(!"Not enough pj_thread_desc size!"); + return PJ_EBUG; + } + + /* If a thread descriptor has been registered before, just return it. */ + if (pj_thread_local_get (thread_tls_id) != 0) { + *thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id); + return PJ_SUCCESS; + } + + /* Initialize and set the thread entry. */ + pj_memset(desc, 0, sizeof(struct pj_thread_t)); + thread->hthread = GetCurrentThread(); + thread->idthread = GetCurrentThreadId(); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + thread->stk_start = &stack_ptr; + thread->stk_size = 0xFFFFFFFFUL; + thread->stk_max_usage = 0; +#else + stack_ptr = '\0'; +#endif + + if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1) + pj_sprintf(thread->obj_name, cstr_thread_name, thread->idthread); + else + pj_sprintf(thread->obj_name, "thr%p", (void*)thread->idthread); + + rc = pj_thread_local_set(thread_tls_id, thread); + if (rc != PJ_SUCCESS) + return rc; + + *thread_ptr = thread; + return PJ_SUCCESS; +} + +/* + * pj_thread_init(void) + */ +pj_status_t pj_thread_init(void) +{ + pj_status_t rc; + pj_thread_t *thread; + + rc = pj_thread_local_alloc(&thread_tls_id); + if (rc != PJ_SUCCESS) + return rc; + + return pj_thread_register("thr%p", main_thread, &thread); +} + +static DWORD WINAPI thread_main(void *param) +{ + pj_thread_t *rec = param; + DWORD result; + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_start = (char*)&rec; +#endif + + PJ_LOG(6,(rec->obj_name, "Thread started")); + + if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) { + pj_assert(!"TLS is not set (pj_init() error?)"); + } + + result = (*rec->proc)(rec->arg); + + PJ_LOG(6,(rec->obj_name, "Thread quitting")); + return (DWORD)result; +} + +/* + * pj_thread_create(...) + */ +PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, + const char *thread_name, + pj_thread_proc *proc, + void *arg, + pj_size_t stack_size, + unsigned flags, + pj_thread_t **thread_ptr) +{ + DWORD dwflags = 0; + pj_thread_t *rec; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL); + + /* Set flags */ + if (flags & PJ_THREAD_SUSPENDED) + dwflags |= CREATE_SUSPENDED; + + /* Create thread record and assign name for the thread */ + rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t)); + if (!rec) + return PJ_ENOMEM; + + /* Set name. */ + if (!thread_name) + thread_name = "thr%p"; + + if (strchr(thread_name, '%')) { + pj_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec); + } else { + strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME); + rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (rec->obj_name, "Thread created")); + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 + rec->stk_size = stack_size ? stack_size : 0xFFFFFFFFUL; + rec->stk_max_usage = 0; +#endif + + /* Create the thread. */ + rec->proc = proc; + rec->arg = arg; + rec->hthread = CreateThread(NULL, stack_size, + thread_main, rec, + dwflags, &rec->idthread); + if (rec->hthread == NULL) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Success! */ + *thread_ptr = rec; + return PJ_SUCCESS; +} + +/* + * pj_thread-get_name() + */ +PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, ""); + + return rec->obj_name; +} + +/* + * pj_thread_resume() + */ +PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t*)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + if (ResumeThread(rec->hthread) == (DWORD)-1) + return PJ_RETURN_OS_ERROR(GetLastError()); + else + return PJ_SUCCESS; +} + +/* + * pj_thread_this() + */ +PJ_DEF(pj_thread_t*) pj_thread_this(void) +{ + pj_thread_t *rec = pj_thread_local_get(thread_tls_id); + pj_assert(rec != NULL); + + /* + * MUST NOT check stack because this function is called + * by PJ_CHECK_STACK() itself!!! + * + */ + + return rec; +} + +/* + * pj_thread_join() + */ +PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t *)p; + DWORD rc; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name)); + + rc = WaitForSingleObject(rec->hthread, INFINITE); + + if (rc==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (rc==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_destroy() + */ +PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p) +{ + pj_thread_t *rec = (pj_thread_t *)p; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(p, PJ_EINVAL); + + if (CloseHandle(rec->hthread) == TRUE) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_sleep() + */ +PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec) +{ + PJ_CHECK_STACK(); + Sleep(msec); + return PJ_SUCCESS; +} + +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 +/* + * pj_thread_check_stack() + * Implementation for PJ_CHECK_STACK() + */ +PJ_DEF(void) pj_thread_check_stack(const char *file, int line) +{ + char stk_ptr; + pj_uint32_t usage; + pj_thread_t *thread = pj_thread_this(); + + pj_assert(thread); + + /* Calculate current usage. */ + usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start : + thread->stk_start - &stk_ptr; + + /* Assert if stack usage is dangerously high. */ + pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128)); + + /* Keep statistic. */ + if (usage > thread->stk_max_usage) { + thread->stk_max_usage = usage; + thread->caller_file = file; + thread->caller_line = line; + } + +} + +/* + * pj_thread_get_stack_max_usage() + */ +PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread) +{ + return thread->stk_max_usage; +} + +/* + * pj_thread_get_stack_info() + */ +PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread, + const char **file, + int *line ) +{ + pj_assert(thread); + + *file = thread->caller_file; + *line = thread->caller_line; + return 0; +} + +#endif /* PJ_OS_HAS_CHECK_STACK */ + + +/////////////////////////////////////////////////////////////////////////////// + +/* + * pj_atomic_create() + */ +PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, + pj_atomic_value_t initial, + pj_atomic_t **atomic_ptr) +{ + pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t)); + if (!atomic_var) + return PJ_ENOMEM; + + atomic_var->value = initial; + *atomic_ptr = atomic_var; + + return PJ_SUCCESS; +} + +/* + * pj_atomic_destroy() + */ +PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var ) +{ + PJ_UNUSED_ARG(var); + PJ_ASSERT_RETURN(var, PJ_EINVAL); + + return 0; +} + +/* + * pj_atomic_set() + */ +PJ_DEF(long) pj_atomic_set(pj_atomic_t *atomic_var, long value) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + + return InterlockedExchange(&atomic_var->value, value); +} + +/* + * pj_atomic_get() + */ +PJ_DEF(long) pj_atomic_get(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + + return atomic_var->value; +} + +/* + * pj_atomic_inc() + */ +PJ_DEF(long) pj_atomic_inc(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + return InterlockedIncrement(&atomic_var->value); +#else +# error Fix Me +#endif +} + +/* + * pj_atomic_dec() + */ +PJ_DEF(long) pj_atomic_dec(pj_atomic_t *atomic_var) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(atomic_var, 0); + +#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400 + return InterlockedDecrement(&atomic_var->value); +#else +# error Fix me +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_thread_local_alloc() + */ +PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index) +{ + PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL); + + //Can't check stack because this function is called in the + //beginning before main thread is initialized. + //PJ_CHECK_STACK(); + + *index = TlsAlloc(); + + if (*index == TLS_OUT_OF_INDEXES) + return PJ_RETURN_OS_ERROR(GetLastError()); + else + return PJ_SUCCESS; +} + +/* + * pj_thread_local_free() + */ +PJ_DEF(void) pj_thread_local_free(long index) +{ + PJ_CHECK_STACK(); + TlsFree(index); +} + +/* + * pj_thread_local_set() + */ +PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value) +{ + BOOL rc; + + //Can't check stack because this function is called in the + //beginning before main thread is initialized. + //PJ_CHECK_STACK(); + rc = TlsSetValue(index, value); + return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_thread_local_get() + */ +PJ_DEF(void*) pj_thread_local_get(long index) +{ + //Can't check stack because this function is called + //by PJ_CHECK_STACK() itself!!! + //PJ_CHECK_STACK(); + return TlsGetValue(index); +} + +/////////////////////////////////////////////////////////////////////////////// +static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name) +{ + + PJ_CHECK_STACK(); + +#if PJ_WIN32_WINNT >= 0x0400 + InitializeCriticalSection(&mutex->crit); +#else + mutex->hMutex = CreateMutex(NULL, FALSE, NULL); + if (!mutex->hMutex) { + return PJ_RETURN_OS_ERROR(GetLastError()); + } +#endif + +#if PJ_DEBUG + /* Set owner. */ + mutex->nesting_level = 0; + mutex->owner = NULL; +#endif + + /* Set name. */ + if (!name) { + name = "mtx%p"; + } + if (strchr(name, '%')) { + pj_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex); + } else { + strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME); + mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (mutex->obj_name, "Mutex created")); + return PJ_SUCCESS; +} + +/* + * pj_mutex_create() + */ +PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, + const char *name, + int type, + pj_mutex_t **mutex_ptr) +{ + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_UNUSED_ARG(type); + PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL); + + mutex = pj_pool_alloc(pool, sizeof(*mutex)); + if (!mutex) + return PJ_ENOMEM; + + rc = init_mutex(mutex, name); + if (rc != PJ_SUCCESS) + return rc; + + *mutex_ptr = mutex; + + return PJ_SUCCESS; +} + +/* + * pj_mutex_create_simple() + */ +PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex); +} + +/* + * pj_mutex_create_recursive() + */ +PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool, + const char *name, + pj_mutex_t **mutex ) +{ + return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex); +} + +/* + * pj_mutex_lock() + */ +PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", + pj_thread_this()->obj_name)); + +#if PJ_WIN32_WINNT >= 0x0400 + EnterCriticalSection(&mutex->crit); + status=PJ_SUCCESS; +#else + if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0) + status = PJ_SUCCESS; + else + status = PJ_STATUS_FROM_OS(GetLastError()); + +#endif + PJ_LOG(6,(mutex->obj_name, + (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"), + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + if (status == PJ_SUCCESS) { + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; + } +#endif + + return status; +} + +/* + * pj_mutex_unlock() + */ +PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_DEBUG + pj_assert(mutex->owner == pj_thread_this()); + if (--mutex->nesting_level == 0) { + mutex->owner = NULL; + } +#endif + + PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_WIN32_WINNT >= 0x0400 + LeaveCriticalSection(&mutex->crit); + status=PJ_SUCCESS; +#else + status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS : + PJ_STATUS_FROM_OS(GetLastError()); +#endif + return status; +} + +/* + * pj_mutex_trylock() + */ +PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex) +{ + pj_status_t status; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + +#if PJ_WIN32_WINNT >= 0x0400 + status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN; +#else + status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? + PJ_SUCCESS : PJ_ETIMEDOUT; +#endif + if (status==PJ_SUCCESS) { + PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", + pj_thread_this()->obj_name)); + +#if PJ_DEBUG + mutex->owner = pj_thread_this(); + ++mutex->nesting_level; +#endif + } + return status; +} + +/* + * pj_mutex_destroy() + */ +PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(mutex, PJ_EINVAL); + + PJ_LOG(6,(mutex->obj_name, "Mutex destroyed")); + +#if PJ_WIN32_WINNT >= 0x0400 + DeleteCriticalSection(&mutex->crit); + return PJ_SUCCESS; +#else + return CloseHandle(mutex->hMutex) ? PJ_SUCCESS : + PJ_RETURN_OS_ERROR(GetLastError()); +#endif +} + +#if PJ_DEBUG +/* + * pj_mutex_is_locked() + */ +PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex) +{ + return mutex->owner == pj_thread_this(); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +/* + * pj_enter_critical_section() + */ +PJ_DEF(void) pj_enter_critical_section(void) +{ + pj_mutex_lock(&critical_section_mutex); +} + + +/* + * pj_leave_critical_section() + */ +PJ_DEF(void) pj_leave_critical_section(void) +{ + pj_mutex_unlock(&critical_section_mutex); +} + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 + +/* + * pj_sem_create() + */ +PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, + const char *name, + unsigned initial, + unsigned max, + pj_sem_t **sem_ptr) +{ + pj_sem_t *sem; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL); + + sem = pj_pool_alloc(pool, sizeof(*sem)); + sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL); + if (!sem->hSemaphore) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Set name. */ + if (!name) { + name = "sem%p"; + } + if (strchr(name, '%')) { + pj_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem); + } else { + strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME); + sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (sem->obj_name, "Semaphore created")); + + *sem_ptr = sem; + return PJ_SUCCESS; +} + +static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout) +{ + DWORD result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = WaitForSingleObject(sem->hSemaphore, timeout); + if (result == WAIT_OBJECT_0) { + PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (result==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_sem_wait() + */ +PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + return pj_sem_wait_for(sem, INFINITE); +} + +/* + * pj_sem_trywait() + */ +PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + return pj_sem_wait_for(sem, 0); +} + +/* + * pj_sem_post() + */ +PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s", + pj_thread_this()->obj_name)); + + if (ReleaseSemaphore(sem->hSemaphore, 1, NULL)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_sem_destroy() + */ +PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sem, PJ_EINVAL); + + PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s", + pj_thread_this()->obj_name)); + + if (CloseHandle(sem->hSemaphore)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +#endif /* PJ_HAS_SEMAPHORE */ +/////////////////////////////////////////////////////////////////////////////// + + +#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0 + +/* + * pj_event_create() + */ +PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, + const char *name, + pj_bool_t manual_reset, + pj_bool_t initial, + pj_event_t **event_ptr) +{ + pj_event_t *event; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL); + + event = pj_pool_alloc(pool, sizeof(*event)); + if (!event) + return PJ_ENOMEM; + + event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, + initial?TRUE:FALSE, NULL); + + if (!event->hEvent) + return PJ_RETURN_OS_ERROR(GetLastError()); + + /* Set name. */ + if (!name) { + name = "evt%p"; + } + if (strchr(name, '%')) { + pj_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event); + } else { + strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME); + event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0'; + } + + PJ_LOG(6, (event->obj_name, "Event created")); + + *event_ptr = event; + return PJ_SUCCESS; +} + +static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout) +{ + DWORD result; + + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", + pj_thread_this()->obj_name)); + + result = WaitForSingleObject(event->hEvent, timeout); + if (result == WAIT_OBJECT_0) { + PJ_LOG(6, (event->obj_name, "Event: thread %s is released", + pj_thread_this()->obj_name)); + } else { + PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", + pj_thread_this()->obj_name)); + } + + if (result==WAIT_OBJECT_0) + return PJ_SUCCESS; + else if (result==WAIT_TIMEOUT) + return PJ_ETIMEDOUT; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_wait() + */ +PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event) +{ + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + return pj_event_wait_for(event, INFINITE); +} + +/* + * pj_event_trywait() + */ +PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event) +{ + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + return pj_event_wait_for(event, 0); +} + +/* + * pj_event_set() + */ +PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Setting event")); + + if (SetEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_pulse() + */ +PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Pulsing event")); + + if (PulseEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_reset() + */ +PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event is reset")); + + if (ResetEvent(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_event_destroy() + */ +PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(event, PJ_EINVAL); + + PJ_LOG(6, (event->obj_name, "Event is destroying")); + + if (CloseHandle(event->hEvent)) + return PJ_SUCCESS; + else + return PJ_RETURN_OS_ERROR(GetLastError()); +} + +#endif /* PJ_HAS_EVENT_OBJ */ + +/////////////////////////////////////////////////////////////////////////////// +#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0 +/* + * Terminal color + */ + +static WORD pj_color_to_os_attr(pj_color_t color) +{ + WORD attr = 0; + + if (color & PJ_TERM_COLOR_R) + attr |= FOREGROUND_RED; + if (color & PJ_TERM_COLOR_G) + attr |= FOREGROUND_GREEN; + if (color & PJ_TERM_COLOR_B) + attr |= FOREGROUND_BLUE; + if (color & PJ_TERM_COLOR_BRIGHT) + attr |= FOREGROUND_INTENSITY; + + return attr; +} + +static pj_color_t os_attr_to_pj_color(WORD attr) +{ + int color = 0; + + if (attr & FOREGROUND_RED) + color |= PJ_TERM_COLOR_R; + if (attr & FOREGROUND_GREEN) + color |= PJ_TERM_COLOR_G; + if (attr & FOREGROUND_BLUE) + color |= PJ_TERM_COLOR_B; + if (attr & FOREGROUND_INTENSITY) + color |= PJ_TERM_COLOR_BRIGHT; + + return color; +} + + +/* + * pj_term_set_color() + */ +PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color) +{ + BOOL rc; + WORD attr = 0; + + PJ_CHECK_STACK(); + + attr = pj_color_to_os_attr(color); + rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr); + return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError()); +} + +/* + * pj_term_get_color() + * Get current terminal foreground color. + */ +PJ_DEF(pj_color_t) pj_term_get_color(void) +{ + CONSOLE_SCREEN_BUFFER_INFO info; + + PJ_CHECK_STACK(); + + GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info); + return os_attr_to_pj_color(info.wAttributes); +} + +#endif /* PJ_TERM_HAS_COLOR */ diff --git a/pjlib/src/pj/os_error_linux_kernel.c b/pjlib/src/pj/os_error_linux_kernel.c index 4c83b4918..7951c2dbd 100644 --- a/pjlib/src/pj/os_error_linux_kernel.c +++ b/pjlib/src/pj/os_error_linux_kernel.c @@ -1,73 +1,73 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_linux_kernel.c 2 10/29/05 11:51a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/os_error_linux_kernel.c $ - * - * 2 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 1 10/19/05 1:48p Bennylp - * Created. - * - */ -#include -#include -#include -#include -#if defined(MODVERSIONS) -#include -#endif -#include -#include - -int kernel_errno; - -PJ_DEF(pj_status_t) pj_get_os_error(void) -{ - return errno; -} - -PJ_DEF(void) pj_set_os_error(pj_status_t code) -{ - errno = code; -} - -PJ_DEF(pj_status_t) pj_get_netos_error(void) -{ - return errno; -} - -PJ_DEF(void) pj_set_netos_error(pj_status_t code) -{ - errno = code; -} - -/* - * platform_strerror() - * - * Platform specific error message. This file is called by pj_strerror() - * in errno.c - */ -int platform_strerror( pj_os_err_type os_errcode, - char *buf, pj_size_t bufsize) -{ - char errmsg[32]; - int len; - - /* Handle EINVAL as special case so that it'll pass errno test. */ - if (os_errcode==EINVAL) - strcpy(errmsg, "Invalid value"); - else - sprintf(errmsg, "errno=%d", os_errcode); - - len = strlen(errmsg); - - if (len >= bufsize) - len = bufsize-1; - - pj_memcpy(buf, errmsg, len); - buf[len] = '\0'; - - return len; -} - - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_linux_kernel.c 2 10/29/05 11:51a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/os_error_linux_kernel.c $ + * + * 2 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 1 10/19/05 1:48p Bennylp + * Created. + * + */ +#include +#include +#include +#include +#if defined(MODVERSIONS) +#include +#endif +#include +#include + +int kernel_errno; + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return errno; +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + errno = code; +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return errno; +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + errno = code; +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + char errmsg[32]; + int len; + + /* Handle EINVAL as special case so that it'll pass errno test. */ + if (os_errcode==EINVAL) + strcpy(errmsg, "Invalid value"); + else + sprintf(errmsg, "errno=%d", os_errcode); + + len = strlen(errmsg); + + if (len >= bufsize) + len = bufsize-1; + + pj_memcpy(buf, errmsg, len); + buf[len] = '\0'; + + return len; +} + + diff --git a/pjlib/src/pj/os_error_unix.c b/pjlib/src/pj/os_error_unix.c index f526c671f..599ea57ac 100644 --- a/pjlib/src/pj/os_error_unix.c +++ b/pjlib/src/pj/os_error_unix.c @@ -1,52 +1,52 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_unix.c 1 10/14/05 12:19a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/os_error_unix.c $ - * - * 1 10/14/05 12:19a Bennylp - * Created. - * - */ -#include -#include -#include - -PJ_DEF(pj_status_t) pj_get_os_error(void) -{ - return PJ_STATUS_FROM_OS(errno); -} - -PJ_DEF(void) pj_set_os_error(pj_status_t code) -{ - errno = PJ_STATUS_TO_OS(code); -} - -PJ_DEF(pj_status_t) pj_get_netos_error(void) -{ - return PJ_STATUS_FROM_OS(errno); -} - -PJ_DEF(void) pj_set_netos_error(pj_status_t code) -{ - errno = PJ_STATUS_TO_OS(code); -} - -/* - * platform_strerror() - * - * Platform specific error message. This file is called by pj_strerror() - * in errno.c - */ -int platform_strerror( pj_os_err_type os_errcode, - char *buf, pj_size_t bufsize) -{ - const char *syserr = strerror(os_errcode); - pj_size_t len = syserr ? strlen(syserr) : 0; - - if (len >= bufsize) len = bufsize - 1; - if (len > 0) - pj_memcpy(buf, syserr, len); - buf[len] = '\0'; - return len; -} - - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_unix.c 1 10/14/05 12:19a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/os_error_unix.c $ + * + * 1 10/14/05 12:19a Bennylp + * Created. + * + */ +#include +#include +#include + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return PJ_STATUS_FROM_OS(errno); +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + errno = PJ_STATUS_TO_OS(code); +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return PJ_STATUS_FROM_OS(errno); +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + errno = PJ_STATUS_TO_OS(code); +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + const char *syserr = strerror(os_errcode); + pj_size_t len = syserr ? strlen(syserr) : 0; + + if (len >= bufsize) len = bufsize - 1; + if (len > 0) + pj_memcpy(buf, syserr, len); + buf[len] = '\0'; + return len; +} + + diff --git a/pjlib/src/pj/os_error_win32.c b/pjlib/src/pj/os_error_win32.c index 19471fcf5..d0fe2c767 100644 --- a/pjlib/src/pj/os_error_win32.c +++ b/pjlib/src/pj/os_error_win32.c @@ -1,161 +1,161 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_win32.c 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/os_error_win32.c $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#include -#include -#include -#include -#include -#include - - -#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 -# include -#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 -# include -#endif - - -/* - * From Apache's APR: - */ -static const struct { - pj_os_err_type code; - const char *msg; -} gaErrorList[] = { - {WSAEINTR, "Interrupted system call"}, - {WSAEBADF, "Bad file number"}, - {WSAEACCES, "Permission denied"}, - {WSAEFAULT, "Bad address"}, - {WSAEINVAL, "Invalid argument"}, - {WSAEMFILE, "Too many open sockets"}, - {WSAEWOULDBLOCK, "Operation would block"}, - {WSAEINPROGRESS, "Operation now in progress"}, - {WSAEALREADY, "Operation already in progress"}, - {WSAENOTSOCK, "Socket operation on non-socket"}, - {WSAEDESTADDRREQ, "Destination address required"}, - {WSAEMSGSIZE, "Message too long"}, - {WSAEPROTOTYPE, "Protocol wrong type for socket"}, - {WSAENOPROTOOPT, "Bad protocol option"}, - {WSAEPROTONOSUPPORT, "Protocol not supported"}, - {WSAESOCKTNOSUPPORT, "Socket type not supported"}, - {WSAEOPNOTSUPP, "Operation not supported on socket"}, - {WSAEPFNOSUPPORT, "Protocol family not supported"}, - {WSAEAFNOSUPPORT, "Address family not supported"}, - {WSAEADDRINUSE, "Address already in use"}, - {WSAEADDRNOTAVAIL, "Can't assign requested address"}, - {WSAENETDOWN, "Network is down"}, - {WSAENETUNREACH, "Network is unreachable"}, - {WSAENETRESET, "Net connection reset"}, - {WSAECONNABORTED, "Software caused connection abort"}, - {WSAECONNRESET, "Connection reset by peer"}, - {WSAENOBUFS, "No buffer space available"}, - {WSAEISCONN, "Socket is already connected"}, - {WSAENOTCONN, "Socket is not connected"}, - {WSAESHUTDOWN, "Can't send after socket shutdown"}, - {WSAETOOMANYREFS, "Too many references, can't splice"}, - {WSAETIMEDOUT, "Connection timed out"}, - {WSAECONNREFUSED, "Connection refused"}, - {WSAELOOP, "Too many levels of symbolic links"}, - {WSAENAMETOOLONG, "File name too long"}, - {WSAEHOSTDOWN, "Host is down"}, - {WSAEHOSTUNREACH, "No route to host"}, - {WSAENOTEMPTY, "Directory not empty"}, - {WSAEPROCLIM, "Too many processes"}, - {WSAEUSERS, "Too many users"}, - {WSAEDQUOT, "Disc quota exceeded"}, - {WSAESTALE, "Stale NFS file handle"}, - {WSAEREMOTE, "Too many levels of remote in path"}, - {WSASYSNOTREADY, "Network system is unavailable"}, - {WSAVERNOTSUPPORTED, "Winsock version out of range"}, - {WSANOTINITIALISED, "WSAStartup not yet called"}, - {WSAEDISCON, "Graceful shutdown in progress"}, - {WSAHOST_NOT_FOUND, "Host not found"}, - {WSANO_DATA, "No host data of that type was found"}, - {0, NULL} -}; - - -PJ_DEF(pj_status_t) pj_get_os_error(void) -{ - return PJ_STATUS_FROM_OS(GetLastError()); -} - -PJ_DEF(void) pj_set_os_error(pj_status_t code) -{ - SetLastError(PJ_STATUS_TO_OS(code)); -} - -PJ_DEF(pj_status_t) pj_get_netos_error(void) -{ - return PJ_STATUS_FROM_OS(WSAGetLastError()); -} - -PJ_DEF(void) pj_set_netos_error(pj_status_t code) -{ - WSASetLastError(PJ_STATUS_TO_OS(code)); -} - -/* - * platform_strerror() - * - * Platform specific error message. This file is called by pj_strerror() - * in errno.c - */ -int platform_strerror( pj_os_err_type os_errcode, - char *buf, pj_size_t bufsize) -{ - int len; - - pj_assert(buf != NULL); - pj_assert(bufsize >= 0); - - /* - * MUST NOT check stack here. - * This function might be called from PJ_CHECK_STACK() itself! - //PJ_CHECK_STACK(); - */ - - len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - os_errcode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)buf, - (DWORD)bufsize, - NULL); - - if (!len) { - int i; - for (i = 0; gaErrorList[i].msg; ++i) { - if (gaErrorList[i].code == os_errcode) { - len = strlen(gaErrorList[i].msg); - if ((pj_size_t)len >= bufsize) { - len = bufsize-1; - } - pj_memcpy(buf, gaErrorList[i].msg, len); - buf[len] = '\0'; - break; - } - } - } - - if (!len) { - len = snprintf( buf, bufsize, "Unknown native error %u", (unsigned)os_errcode); - buf[len] = '\0'; - } - - return len; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_error_win32.c 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/os_error_win32.c $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#include +#include +#include +#include +#include +#include + + +#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0 +# include +#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0 +# include +#endif + + +/* + * From Apache's APR: + */ +static const struct { + pj_os_err_type code; + const char *msg; +} gaErrorList[] = { + {WSAEINTR, "Interrupted system call"}, + {WSAEBADF, "Bad file number"}, + {WSAEACCES, "Permission denied"}, + {WSAEFAULT, "Bad address"}, + {WSAEINVAL, "Invalid argument"}, + {WSAEMFILE, "Too many open sockets"}, + {WSAEWOULDBLOCK, "Operation would block"}, + {WSAEINPROGRESS, "Operation now in progress"}, + {WSAEALREADY, "Operation already in progress"}, + {WSAENOTSOCK, "Socket operation on non-socket"}, + {WSAEDESTADDRREQ, "Destination address required"}, + {WSAEMSGSIZE, "Message too long"}, + {WSAEPROTOTYPE, "Protocol wrong type for socket"}, + {WSAENOPROTOOPT, "Bad protocol option"}, + {WSAEPROTONOSUPPORT, "Protocol not supported"}, + {WSAESOCKTNOSUPPORT, "Socket type not supported"}, + {WSAEOPNOTSUPP, "Operation not supported on socket"}, + {WSAEPFNOSUPPORT, "Protocol family not supported"}, + {WSAEAFNOSUPPORT, "Address family not supported"}, + {WSAEADDRINUSE, "Address already in use"}, + {WSAEADDRNOTAVAIL, "Can't assign requested address"}, + {WSAENETDOWN, "Network is down"}, + {WSAENETUNREACH, "Network is unreachable"}, + {WSAENETRESET, "Net connection reset"}, + {WSAECONNABORTED, "Software caused connection abort"}, + {WSAECONNRESET, "Connection reset by peer"}, + {WSAENOBUFS, "No buffer space available"}, + {WSAEISCONN, "Socket is already connected"}, + {WSAENOTCONN, "Socket is not connected"}, + {WSAESHUTDOWN, "Can't send after socket shutdown"}, + {WSAETOOMANYREFS, "Too many references, can't splice"}, + {WSAETIMEDOUT, "Connection timed out"}, + {WSAECONNREFUSED, "Connection refused"}, + {WSAELOOP, "Too many levels of symbolic links"}, + {WSAENAMETOOLONG, "File name too long"}, + {WSAEHOSTDOWN, "Host is down"}, + {WSAEHOSTUNREACH, "No route to host"}, + {WSAENOTEMPTY, "Directory not empty"}, + {WSAEPROCLIM, "Too many processes"}, + {WSAEUSERS, "Too many users"}, + {WSAEDQUOT, "Disc quota exceeded"}, + {WSAESTALE, "Stale NFS file handle"}, + {WSAEREMOTE, "Too many levels of remote in path"}, + {WSASYSNOTREADY, "Network system is unavailable"}, + {WSAVERNOTSUPPORTED, "Winsock version out of range"}, + {WSANOTINITIALISED, "WSAStartup not yet called"}, + {WSAEDISCON, "Graceful shutdown in progress"}, + {WSAHOST_NOT_FOUND, "Host not found"}, + {WSANO_DATA, "No host data of that type was found"}, + {0, NULL} +}; + + +PJ_DEF(pj_status_t) pj_get_os_error(void) +{ + return PJ_STATUS_FROM_OS(GetLastError()); +} + +PJ_DEF(void) pj_set_os_error(pj_status_t code) +{ + SetLastError(PJ_STATUS_TO_OS(code)); +} + +PJ_DEF(pj_status_t) pj_get_netos_error(void) +{ + return PJ_STATUS_FROM_OS(WSAGetLastError()); +} + +PJ_DEF(void) pj_set_netos_error(pj_status_t code) +{ + WSASetLastError(PJ_STATUS_TO_OS(code)); +} + +/* + * platform_strerror() + * + * Platform specific error message. This file is called by pj_strerror() + * in errno.c + */ +int platform_strerror( pj_os_err_type os_errcode, + char *buf, pj_size_t bufsize) +{ + int len; + + pj_assert(buf != NULL); + pj_assert(bufsize >= 0); + + /* + * MUST NOT check stack here. + * This function might be called from PJ_CHECK_STACK() itself! + //PJ_CHECK_STACK(); + */ + + len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + os_errcode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)buf, + (DWORD)bufsize, + NULL); + + if (!len) { + int i; + for (i = 0; gaErrorList[i].msg; ++i) { + if (gaErrorList[i].code == os_errcode) { + len = strlen(gaErrorList[i].msg); + if ((pj_size_t)len >= bufsize) { + len = bufsize-1; + } + pj_memcpy(buf, gaErrorList[i].msg, len); + buf[len] = '\0'; + break; + } + } + } + + if (!len) { + len = snprintf( buf, bufsize, "Unknown native error %u", (unsigned)os_errcode); + buf[len] = '\0'; + } + + return len; +} + diff --git a/pjlib/src/pj/os_time_ansi.c b/pjlib/src/pj/os_time_ansi.c index 906b21d65..0d6d9d7e8 100644 --- a/pjlib/src/pj/os_time_ansi.c +++ b/pjlib/src/pj/os_time_ansi.c @@ -1,65 +1,65 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_time_ansi.c 2 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_ansi.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/17/05 10:36a Bennylp - * Created. - * - */ -#include -#include - -/////////////////////////////////////////////////////////////////////////////// - -PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) -{ - struct timeb tb; - - PJ_CHECK_STACK(); - - ftime(&tb); - tv->sec = tb.time; - tv->msec = tb.millitm; - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) -{ - struct tm *local_time; - - PJ_CHECK_STACK(); - - local_time = localtime((time_t*)&tv->sec); - - pt->year = local_time->tm_year+1900; - pt->mon = local_time->tm_mon; - pt->day = local_time->tm_mday; - pt->hour = local_time->tm_hour; - pt->min = local_time->tm_min; - pt->sec = local_time->tm_sec; - pt->wday = local_time->tm_wday; - pt->yday = local_time->tm_yday; - pt->msec = tv->msec; - - return PJ_SUCCESS; -} - -/** - * Encode parsed time to time value. - */ -PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); - -/** - * Convert local time to GMT. - */ -PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); - -/** - * Convert GMT to local time. - */ -PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); - - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_time_ansi.c 2 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_ansi.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/17/05 10:36a Bennylp + * Created. + * + */ +#include +#include + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) +{ + struct timeb tb; + + PJ_CHECK_STACK(); + + ftime(&tb); + tv->sec = tb.time; + tv->msec = tb.millitm; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) +{ + struct tm *local_time; + + PJ_CHECK_STACK(); + + local_time = localtime((time_t*)&tv->sec); + + pt->year = local_time->tm_year+1900; + pt->mon = local_time->tm_mon; + pt->day = local_time->tm_mday; + pt->hour = local_time->tm_hour; + pt->min = local_time->tm_min; + pt->sec = local_time->tm_sec; + pt->wday = local_time->tm_wday; + pt->yday = local_time->tm_yday; + pt->msec = tv->msec; + + return PJ_SUCCESS; +} + +/** + * Encode parsed time to time value. + */ +PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); + +/** + * Convert local time to GMT. + */ +PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + */ +PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + + diff --git a/pjlib/src/pj/os_time_linux_kernel.c b/pjlib/src/pj/os_time_linux_kernel.c index 4d5f4cb4e..5ca1f4ed1 100644 --- a/pjlib/src/pj/os_time_linux_kernel.c +++ b/pjlib/src/pj/os_time_linux_kernel.c @@ -1,58 +1,58 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c 2 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/22/05 10:39a Bennylp - * Created. - * - */ -#include -#include - -/////////////////////////////////////////////////////////////////////////////// - -PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) -{ - struct timeval tval; - - do_gettimeofday(&tval); - tv->sec = tval.tv_sec; - tv->msec = tval.tv_usec / 1000; - - return 0; -} - -PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) -{ - pt->year = 2005; - pt->mon = 8; - pt->day = 20; - pt->hour = 16; - pt->min = 30; - pt->sec = 30; - pt->wday = 3; - pt->yday = 200; - pt->msec = 777; - - return -1; -} - -/** - * Encode parsed time to time value. - */ -PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); - -/** - * Convert local time to GMT. - */ -PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); - -/** - * Convert GMT to local time. - */ -PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); - - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c 2 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/os_time_linux_kernel.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/22/05 10:39a Bennylp + * Created. + * + */ +#include +#include + +/////////////////////////////////////////////////////////////////////////////// + +PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv) +{ + struct timeval tval; + + do_gettimeofday(&tval); + tv->sec = tval.tv_sec; + tv->msec = tval.tv_usec / 1000; + + return 0; +} + +PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt) +{ + pt->year = 2005; + pt->mon = 8; + pt->day = 20; + pt->hour = 16; + pt->min = 30; + pt->sec = 30; + pt->wday = 3; + pt->yday = 200; + pt->msec = 777; + + return -1; +} + +/** + * Encode parsed time to time value. + */ +PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv); + +/** + * Convert local time to GMT. + */ +PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv); + +/** + * Convert GMT to local time. + */ +PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv); + + diff --git a/pjlib/src/pj/os_timestamp_common.c b/pjlib/src/pj/os_timestamp_common.c index 630ffb277..3a170a33e 100644 --- a/pjlib/src/pj/os_timestamp_common.c +++ b/pjlib/src/pj/os_timestamp_common.c @@ -1,129 +1,129 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_common.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_common.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/09/05 2:56p Bennylp - * Created. - * - */ -#include -#include - -#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 - -#define U32MAX (0xFFFFFFFFUL) -#define NANOSEC (1000000000UL) -#define USEC (1000000UL) -#define MSEC (1000) - -static pj_highprec_t get_elapsed( const pj_timestamp *start, - const pj_timestamp *stop ) -{ - pj_highprec_t elapsed_hi, elapsed_lo; - - elapsed_hi = stop->u32.hi - start->u32.hi; - elapsed_lo = stop->u32.lo - start->u32.lo; - - /* elapsed_hi = elapsed_hi * U32MAX */ - pj_highprec_mul(elapsed_hi, U32MAX); - - return elapsed_hi + elapsed_lo; -} - -static pj_highprec_t elapsed_usec( const pj_timestamp *start, - const pj_timestamp *stop ) -{ - pj_timestamp ts_freq; - pj_highprec_t freq, elapsed; - - if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) - return 0; - - /* Convert frequency timestamp */ - freq = ts_freq.u32.hi; - pj_highprec_mul(freq, U32MAX); - freq += ts_freq.u32.lo; - - /* Avoid division by zero. */ - if (freq == 0) freq = 1; - - /* Get elapsed time in cycles. */ - elapsed = get_elapsed(start, stop); - - /* usec = elapsed * USEC / freq */ - pj_highprec_mul(elapsed, USEC); - pj_highprec_div(elapsed, freq); - - return elapsed; -} - -PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, - const pj_timestamp *stop ) -{ - pj_timestamp ts_freq; - pj_highprec_t freq, elapsed; - - if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) - return 0; - - /* Convert frequency timestamp */ - freq = ts_freq.u32.hi; - pj_highprec_mul(freq, U32MAX); - freq += ts_freq.u32.lo; - - /* Avoid division by zero. */ - if (freq == 0) freq = 1; - - /* Get elapsed time in cycles. */ - elapsed = get_elapsed(start, stop); - - /* usec = elapsed * USEC / freq */ - pj_highprec_mul(elapsed, NANOSEC); - pj_highprec_div(elapsed, freq); - - return (pj_uint32_t)elapsed; -} - -PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, - const pj_timestamp *stop ) -{ - return (pj_uint32_t)elapsed_usec(start, stop); -} - -PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start, - const pj_timestamp *stop ) -{ - pj_highprec_t elapsed = elapsed_usec(start, stop); - pj_time_val tv_elapsed; - - if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) { - tv_elapsed.sec = tv_elapsed.msec = 0; - return tv_elapsed; - } else { - pj_highprec_t sec, msec; - - sec = elapsed; - pj_highprec_div(sec, USEC); - tv_elapsed.sec = (long)sec; - - msec = elapsed; - pj_highprec_mod(msec, USEC); - pj_highprec_div(msec, 1000); - tv_elapsed.msec = (long)msec; - - return tv_elapsed; - } -} - -PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, - const pj_timestamp *stop ) -{ - return stop->u32.lo - start->u32.lo; -} - -#endif /* PJ_HAS_HIGH_RES_TIMER */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_common.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_common.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/09/05 2:56p Bennylp + * Created. + * + */ +#include +#include + +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 + +#define U32MAX (0xFFFFFFFFUL) +#define NANOSEC (1000000000UL) +#define USEC (1000000UL) +#define MSEC (1000) + +static pj_highprec_t get_elapsed( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_highprec_t elapsed_hi, elapsed_lo; + + elapsed_hi = stop->u32.hi - start->u32.hi; + elapsed_lo = stop->u32.lo - start->u32.lo; + + /* elapsed_hi = elapsed_hi * U32MAX */ + pj_highprec_mul(elapsed_hi, U32MAX); + + return elapsed_hi + elapsed_lo; +} + +static pj_highprec_t elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_timestamp ts_freq; + pj_highprec_t freq, elapsed; + + if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) + return 0; + + /* Convert frequency timestamp */ + freq = ts_freq.u32.hi; + pj_highprec_mul(freq, U32MAX); + freq += ts_freq.u32.lo; + + /* Avoid division by zero. */ + if (freq == 0) freq = 1; + + /* Get elapsed time in cycles. */ + elapsed = get_elapsed(start, stop); + + /* usec = elapsed * USEC / freq */ + pj_highprec_mul(elapsed, USEC); + pj_highprec_div(elapsed, freq); + + return elapsed; +} + +PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_timestamp ts_freq; + pj_highprec_t freq, elapsed; + + if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS) + return 0; + + /* Convert frequency timestamp */ + freq = ts_freq.u32.hi; + pj_highprec_mul(freq, U32MAX); + freq += ts_freq.u32.lo; + + /* Avoid division by zero. */ + if (freq == 0) freq = 1; + + /* Get elapsed time in cycles. */ + elapsed = get_elapsed(start, stop); + + /* usec = elapsed * USEC / freq */ + pj_highprec_mul(elapsed, NANOSEC); + pj_highprec_div(elapsed, freq); + + return (pj_uint32_t)elapsed; +} + +PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return (pj_uint32_t)elapsed_usec(start, stop); +} + +PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + pj_highprec_t elapsed = elapsed_usec(start, stop); + pj_time_val tv_elapsed; + + if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) { + tv_elapsed.sec = tv_elapsed.msec = 0; + return tv_elapsed; + } else { + pj_highprec_t sec, msec; + + sec = elapsed; + pj_highprec_div(sec, USEC); + tv_elapsed.sec = (long)sec; + + msec = elapsed; + pj_highprec_mod(msec, USEC); + pj_highprec_div(msec, 1000); + tv_elapsed.msec = (long)msec; + + return tv_elapsed; + } +} + +PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start, + const pj_timestamp *stop ) +{ + return stop->u32.lo - start->u32.lo; +} + +#endif /* PJ_HAS_HIGH_RES_TIMER */ + diff --git a/pjlib/src/pj/os_timestamp_linux.c b/pjlib/src/pj/os_timestamp_linux.c index 52639dcd2..ee94e6e80 100644 --- a/pjlib/src/pj/os_timestamp_linux.c +++ b/pjlib/src/pj/os_timestamp_linux.c @@ -1,137 +1,137 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux.c 4 10/29/05 10:27p Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux.c $ - * - * 4 10/29/05 10:27p Bennylp - * Fixed misc warnings. - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 1 9/18/05 9:25p Bennylp - * Created. - * - */ -#include -#include -#include -#include -#include -#include - -#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 -static int machine_speed_mhz; -static pj_timestamp machine_speed; - -static __inline__ unsigned long long int rdtsc() -{ - unsigned long long int x; - __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); - return x; -} - -/* Determine machine's CPU MHz to get the counter's frequency. - */ -static int get_machine_speed_mhz() -{ - FILE *strm; - char buf[512]; - int len; - char *pos, *end; - - PJ_CHECK_STACK(); - - /* Open /proc/cpuinfo and read the file */ - strm = fopen("/proc/cpuinfo", "r"); - if (!strm) - return -1; - len = fread(buf, 1, sizeof(buf), strm); - fclose(strm); - if (len < 1) { - return -1; - } - buf[len] = '\0'; - - /* Locate the MHz digit. */ - pos = strstr(buf, "cpu MHz"); - if (!pos) - return -1; - pos = strchr(pos, ':'); - if (!pos) - return -1; - end = (pos += 2); - while (isdigit(*end)) ++end; - *end = '\0'; - - /* Return the Mhz part, and give it a +1. */ - return atoi(pos)+1; -} - -PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) -{ - if (machine_speed_mhz == 0) { - machine_speed_mhz = get_machine_speed_mhz(); - if (machine_speed_mhz > 0) { - machine_speed.u64 = machine_speed_mhz * 1000000.0; - } - } - - if (machine_speed_mhz == -1) { - ts->u64 = 0; - return -1; - } - ts->u64 = rdtsc(); - return 0; -} - -PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) -{ - if (machine_speed_mhz == 0) { - machine_speed_mhz = get_machine_speed_mhz(); - if (machine_speed_mhz > 0) { - machine_speed.u64 = machine_speed_mhz * 1000000.0; - } - } - - if (machine_speed_mhz == -1) { - freq->u64 = 1; /* return 1 to prevent division by zero in apps. */ - return -1; - } - - freq->u64 = machine_speed.u64; - return 0; -} - -#else -#include -#include - -#define USEC_PER_SEC 1000000 - -PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) -{ - struct timeval tv; - - if (gettimeofday(&tv, NULL) != 0) { - return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); - } - - ts->u64 = tv.tv_sec; - ts->u64 *= USEC_PER_SEC; - ts->u64 += tv.tv_usec; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) -{ - freq->u32.hi = 0; - freq->u32.lo = USEC_PER_SEC; - - return PJ_SUCCESS; -} - -#endif - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux.c 4 10/29/05 10:27p Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux.c $ + * + * 4 10/29/05 10:27p Bennylp + * Fixed misc warnings. + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 1 9/18/05 9:25p Bennylp + * Created. + * + */ +#include +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 +static int machine_speed_mhz; +static pj_timestamp machine_speed; + +static __inline__ unsigned long long int rdtsc() +{ + unsigned long long int x; + __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); + return x; +} + +/* Determine machine's CPU MHz to get the counter's frequency. + */ +static int get_machine_speed_mhz() +{ + FILE *strm; + char buf[512]; + int len; + char *pos, *end; + + PJ_CHECK_STACK(); + + /* Open /proc/cpuinfo and read the file */ + strm = fopen("/proc/cpuinfo", "r"); + if (!strm) + return -1; + len = fread(buf, 1, sizeof(buf), strm); + fclose(strm); + if (len < 1) { + return -1; + } + buf[len] = '\0'; + + /* Locate the MHz digit. */ + pos = strstr(buf, "cpu MHz"); + if (!pos) + return -1; + pos = strchr(pos, ':'); + if (!pos) + return -1; + end = (pos += 2); + while (isdigit(*end)) ++end; + *end = '\0'; + + /* Return the Mhz part, and give it a +1. */ + return atoi(pos)+1; +} + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + if (machine_speed_mhz == 0) { + machine_speed_mhz = get_machine_speed_mhz(); + if (machine_speed_mhz > 0) { + machine_speed.u64 = machine_speed_mhz * 1000000.0; + } + } + + if (machine_speed_mhz == -1) { + ts->u64 = 0; + return -1; + } + ts->u64 = rdtsc(); + return 0; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + if (machine_speed_mhz == 0) { + machine_speed_mhz = get_machine_speed_mhz(); + if (machine_speed_mhz > 0) { + machine_speed.u64 = machine_speed_mhz * 1000000.0; + } + } + + if (machine_speed_mhz == -1) { + freq->u64 = 1; /* return 1 to prevent division by zero in apps. */ + return -1; + } + + freq->u64 = machine_speed.u64; + return 0; +} + +#else +#include +#include + +#define USEC_PER_SEC 1000000 + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) { + return PJ_RETURN_OS_ERROR(pj_get_native_os_error()); + } + + ts->u64 = tv.tv_sec; + ts->u64 *= USEC_PER_SEC; + ts->u64 += tv.tv_usec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = USEC_PER_SEC; + + return PJ_SUCCESS; +} + +#endif + diff --git a/pjlib/src/pj/os_timestamp_linux_kernel.c b/pjlib/src/pj/os_timestamp_linux_kernel.c index 8895cf9d4..a5a538ef7 100644 --- a/pjlib/src/pj/os_timestamp_linux_kernel.c +++ b/pjlib/src/pj/os_timestamp_linux_kernel.c @@ -1,70 +1,70 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux_kernel.c 2 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux_kernel.c $ - * - * 2 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 1 9/22/05 10:39a Bennylp - * Created. - * - */ -#include -#include - -#if 0 -PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) -{ - ts->u32.hi = 0; - ts->u32.lo = jiffies; - return 0; -} - -PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) -{ - freq->u32.hi = 0; - freq->u32.lo = HZ; - return 0; -} -#elif 0 -PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) -{ - struct timespec tv; - - tv = CURRENT_TIME; - - ts->u64 = tv.tv_sec; - ts->u64 *= NSEC_PER_SEC; - ts->u64 += tv.tv_nsec; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) -{ - freq->u32.hi = 0; - freq->u32.lo = NSEC_PER_SEC; - return 0; -} -#else -PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) -{ - struct timeval tv; - - do_gettimeofday(&tv); - - ts->u64 = tv.tv_sec; - ts->u64 *= USEC_PER_SEC; - ts->u64 += tv.tv_usec; - - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) -{ - freq->u32.hi = 0; - freq->u32.lo = USEC_PER_SEC; - return 0; -} - -#endif - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux_kernel.c 2 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_linux_kernel.c $ + * + * 2 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 1 9/22/05 10:39a Bennylp + * Created. + * + */ +#include +#include + +#if 0 +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + ts->u32.hi = 0; + ts->u32.lo = jiffies; + return 0; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = HZ; + return 0; +} +#elif 0 +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timespec tv; + + tv = CURRENT_TIME; + + ts->u64 = tv.tv_sec; + ts->u64 *= NSEC_PER_SEC; + ts->u64 += tv.tv_nsec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = NSEC_PER_SEC; + return 0; +} +#else +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + struct timeval tv; + + do_gettimeofday(&tv); + + ts->u64 = tv.tv_sec; + ts->u64 *= USEC_PER_SEC; + ts->u64 += tv.tv_usec; + + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + freq->u32.hi = 0; + freq->u32.lo = USEC_PER_SEC; + return 0; +} + +#endif + diff --git a/pjlib/src/pj/os_timestamp_win32.c b/pjlib/src/pj/os_timestamp_win32.c index 787c4bfc8..924bfc6f9 100644 --- a/pjlib/src/pj/os_timestamp_win32.c +++ b/pjlib/src/pj/os_timestamp_win32.c @@ -1,38 +1,38 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_win32.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_win32.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/18/05 8:15p Bennylp - * Created. - * - */ -#include -#include -#include - -PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) -{ - LARGE_INTEGER val; - - if (!QueryPerformanceCounter(&val)) - return PJ_RETURN_OS_ERROR(GetLastError()); - - ts->u64 = val.QuadPart; - return PJ_SUCCESS; -} - -PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) -{ - LARGE_INTEGER val; - - if (!QueryPerformanceFrequency(&val)) - return PJ_RETURN_OS_ERROR(GetLastError()); - - freq->u64 = val.QuadPart; - return PJ_SUCCESS; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/os_timestamp_win32.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/os_timestamp_win32.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/18/05 8:15p Bennylp + * Created. + * + */ +#include +#include +#include + +PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) +{ + LARGE_INTEGER val; + + if (!QueryPerformanceCounter(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + ts->u64 = val.QuadPart; + return PJ_SUCCESS; +} + +PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) +{ + LARGE_INTEGER val; + + if (!QueryPerformanceFrequency(&val)) + return PJ_RETURN_OS_ERROR(GetLastError()); + + freq->u64 = val.QuadPart; + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/pool.c b/pjlib/src/pj/pool.c index 3bf195b08..4ad6280c6 100644 --- a/pjlib/src/pj/pool.c +++ b/pjlib/src/pj/pool.c @@ -1,265 +1,265 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/pool.c 8 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/pool.c $ - * - * 8 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 7 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include - -/* Include inline definitions when inlining is disabled. */ -#if !PJ_FUNCTIONS_ARE_INLINED -# include -#endif - -#define LOG(expr) PJ_LOG(5,expr) - -int PJ_NO_MEMORY_EXCEPTION; - -/* - * Create new block. - * Create a new big chunk of memory block, from which user allocation will be - * taken from. - */ -static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) -{ - pj_pool_block *block; - - PJ_CHECK_STACK(); - pj_assert(size >= sizeof(pj_pool_block)); - - LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u", - size, pool->capacity, pool->used_size)); - - /* Request memory from allocator. */ - block = (pj_pool_block*) - (*pool->factory->policy.block_alloc)(pool->factory, size); - if (block == NULL) { - (*pool->callback)(pool, size); - return NULL; - } - - /* Add capacity. */ - pool->capacity += size; - pool->used_size += sizeof(pj_pool_block); - - /* Set block attribytes. */ - block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); - block->end = ((unsigned char*)block) + size; - - /* Insert in the front of the list. */ - pj_list_insert_after(&pool->block_list, block); - - LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end)); - - return block; -} - -/* - * Allocate memory chunk for user from available blocks. - * This will iterate through block list to find space to allocate the chunk. - * If no space is available in all the blocks, a new block might be created - * (depending on whether the pool is allowed to resize). - */ -PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size) -{ - pj_pool_block *block = pool->block_list.next; - void *p; - unsigned block_size; - - PJ_CHECK_STACK(); - - while (block != &pool->block_list) { - p = pj_pool_alloc_from_block(pool, block, size); - if (p != NULL) - return p; - block = block->next; - } - /* No available space in all blocks. */ - - /* If pool is configured NOT to expand, return error. */ - if (pool->increment_size == 0) { - LOG((pool->obj_name, "Can't expand pool to allocate %u bytes " - "(used=%u, cap=%u)", - size, pool->used_size, pool->capacity)); - (*pool->callback)(pool, size); - return NULL; - } - - /* If pool is configured to expand, but the increment size - * is less than the required size, expand the pool by multiple - * increment size - */ - if (pool->increment_size < size + sizeof(pj_pool_block)) { - unsigned count; - count = (size + pool->increment_size + sizeof(pj_pool_block)) / - pool->increment_size; - block_size = count * pool->increment_size; - - } else { - block_size = pool->increment_size; - } - - LOG((pool->obj_name, - "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)", - size, block_size, pool->used_size, pool->capacity)); - - block = pj_pool_create_block(pool, block_size); - if (!block) - return NULL; - - p = pj_pool_alloc_from_block(pool, block, size); - pj_assert(p != NULL); -#if PJ_DEBUG - if (p == NULL) { - p = p; - } -#endif - return p; -} - -/* - * Internal function to initialize pool. - */ -PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, - const char *name, - pj_size_t increment_size, - pj_pool_callback *callback) -{ - pj_pool_block *block; - - PJ_CHECK_STACK(); - - pool->increment_size = increment_size; - pool->callback = callback; - pool->used_size = sizeof(*pool); - block = pool->block_list.next; - while (block != &pool->block_list) { - pool->used_size += sizeof(pj_pool_block); - block = block->next; - } - - if (name) { - if (strchr(name, '%') != NULL) { - sprintf(pool->obj_name, name, pool); - } else { - strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); - } - } else { - pool->obj_name[0] = '\0'; - } -} - -/* - * Create new memory pool. - */ -PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, - pj_size_t initial_size, - pj_size_t increment_size, - pj_pool_callback *callback) -{ - pj_pool_t *pool; - pj_pool_block *block; - unsigned char *buffer; - - PJ_CHECK_STACK(); - - buffer = (*f->policy.block_alloc)(f, initial_size); - if (!buffer) - return NULL; - - /* Set pool administrative data. */ - pool = (pj_pool_t*)buffer; - pj_memset(pool, 0, sizeof(*pool)); - - pj_list_init(&pool->block_list); - pool->factory = f; - - /* Create the first block from the memory. */ - block = (pj_pool_block*) (buffer + sizeof(*pool)); - block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); - block->end = buffer + initial_size; - pj_list_insert_after(&pool->block_list, block); - - pj_pool_init_int(pool, name, increment_size, callback); - - /* Pool initial capacity and used size */ - pool->capacity = initial_size; - - LOG((pool->obj_name, "pool created, size=%u", pool->capacity)); - return pool; -} - -/* - * Reset the pool to the state when it was created. - * All blocks will be deallocated except the first block. All memory areas - * are marked as free. - */ -static void reset_pool(pj_pool_t *pool) -{ - pj_pool_block *block; - - PJ_CHECK_STACK(); - - block = pool->block_list.prev; - if (block == &pool->block_list) - return; - - /* Skip the first block because it is occupying the same memory - as the pool itself. - */ - block = block->prev; - - while (block != &pool->block_list) { - pj_pool_block *prev = block->prev; - pj_list_erase(block); - (*pool->factory->policy.block_free)(pool->factory, block, - block->end - (unsigned char*)block); - block = prev; - } - - block = pool->block_list.next; - block->cur = block->buf; - pool->capacity = block->end - (unsigned char*)pool; - pool->used_size = 0; -} - -/* - * The public function to reset pool. - */ -PJ_DEF(void) pj_pool_reset(pj_pool_t *pool) -{ - LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)", - pool->capacity, pool->used_size, pool->used_size*100/pool->capacity)); - - reset_pool(pool); -} - -/* - * Destroy the pool. - */ -PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool) -{ - pj_size_t initial_size; - - LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p", - pool->capacity, pool->used_size, pool->used_size*100/pool->capacity, - ((pj_pool_block*)pool->block_list.next)->buf, - ((pj_pool_block*)pool->block_list.next)->end)); - - reset_pool(pool); - initial_size = ((pj_pool_block*)pool->block_list.next)->end - - (unsigned char*)pool; - (*pool->factory->policy.block_free)(pool->factory, pool, initial_size); -} - - +/* $Header: /pjproject-0.3/pjlib/src/pj/pool.c 8 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/pool.c $ + * + * 8 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 7 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include + +/* Include inline definitions when inlining is disabled. */ +#if !PJ_FUNCTIONS_ARE_INLINED +# include +#endif + +#define LOG(expr) PJ_LOG(5,expr) + +int PJ_NO_MEMORY_EXCEPTION; + +/* + * Create new block. + * Create a new big chunk of memory block, from which user allocation will be + * taken from. + */ +static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + pj_assert(size >= sizeof(pj_pool_block)); + + LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u", + size, pool->capacity, pool->used_size)); + + /* Request memory from allocator. */ + block = (pj_pool_block*) + (*pool->factory->policy.block_alloc)(pool->factory, size); + if (block == NULL) { + (*pool->callback)(pool, size); + return NULL; + } + + /* Add capacity. */ + pool->capacity += size; + pool->used_size += sizeof(pj_pool_block); + + /* Set block attribytes. */ + block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); + block->end = ((unsigned char*)block) + size; + + /* Insert in the front of the list. */ + pj_list_insert_after(&pool->block_list, block); + + LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end)); + + return block; +} + +/* + * Allocate memory chunk for user from available blocks. + * This will iterate through block list to find space to allocate the chunk. + * If no space is available in all the blocks, a new block might be created + * (depending on whether the pool is allowed to resize). + */ +PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, unsigned size) +{ + pj_pool_block *block = pool->block_list.next; + void *p; + unsigned block_size; + + PJ_CHECK_STACK(); + + while (block != &pool->block_list) { + p = pj_pool_alloc_from_block(pool, block, size); + if (p != NULL) + return p; + block = block->next; + } + /* No available space in all blocks. */ + + /* If pool is configured NOT to expand, return error. */ + if (pool->increment_size == 0) { + LOG((pool->obj_name, "Can't expand pool to allocate %u bytes " + "(used=%u, cap=%u)", + size, pool->used_size, pool->capacity)); + (*pool->callback)(pool, size); + return NULL; + } + + /* If pool is configured to expand, but the increment size + * is less than the required size, expand the pool by multiple + * increment size + */ + if (pool->increment_size < size + sizeof(pj_pool_block)) { + unsigned count; + count = (size + pool->increment_size + sizeof(pj_pool_block)) / + pool->increment_size; + block_size = count * pool->increment_size; + + } else { + block_size = pool->increment_size; + } + + LOG((pool->obj_name, + "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)", + size, block_size, pool->used_size, pool->capacity)); + + block = pj_pool_create_block(pool, block_size); + if (!block) + return NULL; + + p = pj_pool_alloc_from_block(pool, block, size); + pj_assert(p != NULL); +#if PJ_DEBUG + if (p == NULL) { + p = p; + } +#endif + return p; +} + +/* + * Internal function to initialize pool. + */ +PJ_DEF(void) pj_pool_init_int( pj_pool_t *pool, + const char *name, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + + pool->increment_size = increment_size; + pool->callback = callback; + pool->used_size = sizeof(*pool); + block = pool->block_list.next; + while (block != &pool->block_list) { + pool->used_size += sizeof(pj_pool_block); + block = block->next; + } + + if (name) { + if (strchr(name, '%') != NULL) { + sprintf(pool->obj_name, name, pool); + } else { + strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); + } + } else { + pool->obj_name[0] = '\0'; + } +} + +/* + * Create new memory pool. + */ +PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + pj_pool_t *pool; + pj_pool_block *block; + unsigned char *buffer; + + PJ_CHECK_STACK(); + + buffer = (*f->policy.block_alloc)(f, initial_size); + if (!buffer) + return NULL; + + /* Set pool administrative data. */ + pool = (pj_pool_t*)buffer; + pj_memset(pool, 0, sizeof(*pool)); + + pj_list_init(&pool->block_list); + pool->factory = f; + + /* Create the first block from the memory. */ + block = (pj_pool_block*) (buffer + sizeof(*pool)); + block->cur = block->buf = ((unsigned char*)block) + sizeof(pj_pool_block); + block->end = buffer + initial_size; + pj_list_insert_after(&pool->block_list, block); + + pj_pool_init_int(pool, name, increment_size, callback); + + /* Pool initial capacity and used size */ + pool->capacity = initial_size; + + LOG((pool->obj_name, "pool created, size=%u", pool->capacity)); + return pool; +} + +/* + * Reset the pool to the state when it was created. + * All blocks will be deallocated except the first block. All memory areas + * are marked as free. + */ +static void reset_pool(pj_pool_t *pool) +{ + pj_pool_block *block; + + PJ_CHECK_STACK(); + + block = pool->block_list.prev; + if (block == &pool->block_list) + return; + + /* Skip the first block because it is occupying the same memory + as the pool itself. + */ + block = block->prev; + + while (block != &pool->block_list) { + pj_pool_block *prev = block->prev; + pj_list_erase(block); + (*pool->factory->policy.block_free)(pool->factory, block, + block->end - (unsigned char*)block); + block = prev; + } + + block = pool->block_list.next; + block->cur = block->buf; + pool->capacity = block->end - (unsigned char*)pool; + pool->used_size = 0; +} + +/* + * The public function to reset pool. + */ +PJ_DEF(void) pj_pool_reset(pj_pool_t *pool) +{ + LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)", + pool->capacity, pool->used_size, pool->used_size*100/pool->capacity)); + + reset_pool(pool); +} + +/* + * Destroy the pool. + */ +PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool) +{ + pj_size_t initial_size; + + LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p", + pool->capacity, pool->used_size, pool->used_size*100/pool->capacity, + ((pj_pool_block*)pool->block_list.next)->buf, + ((pj_pool_block*)pool->block_list.next)->end)); + + reset_pool(pool); + initial_size = ((pj_pool_block*)pool->block_list.next)->end - + (unsigned char*)pool; + (*pool->factory->policy.block_free)(pool->factory, pool, initial_size); +} + + diff --git a/pjlib/src/pj/pool_caching.c b/pjlib/src/pj/pool_caching.c index b72b0d458..2bdd61793 100644 --- a/pjlib/src/pj/pool_caching.c +++ b/pjlib/src/pj/pool_caching.c @@ -1,210 +1,210 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/pool_caching.c 5 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/pool_caching.c $ - * - * 5 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include - -static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, - const char *name, - pj_size_t initial_size, - pj_size_t increment_sz, - pj_pool_callback *callback); -static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool); -static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ); - -static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = -{ - 256, 512, 1024, 2048, 4096, 8192, 12288, 16384, - 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536 -}; - - -PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, - const pj_pool_factory_policy *policy, - pj_size_t max_capacity) -{ - int i; - - PJ_CHECK_STACK(); - - pj_memset(cp, 0, sizeof(*cp)); - - cp->max_capacity = max_capacity; - pj_list_init(&cp->used_list); - for (i=0; ifree_list[i]); - - pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy)); - cp->factory.create_pool = &cpool_create_pool; - cp->factory.release_pool = &cpool_release_pool; - cp->factory.dump_status = &cpool_dump_status; -} - -PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp ) -{ - int i; - pj_pool_t *pool; - - PJ_CHECK_STACK(); - - /* Delete all pool in free list */ - for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) { - pj_pool_t *pool = cp->free_list[i].next; - pj_pool_t *next; - for (; pool != (void*)&cp->free_list[i]; pool = next) { - next = pool->next; - pj_list_erase(pool); - pj_pool_destroy_int(pool); - } - } - - /* Delete all pools in used list */ - pool = cp->used_list.next; - while (pool != (pj_pool_t*) &cp->used_list) { - pj_pool_t *next = pool->next; - pj_list_erase(pool); - pj_pool_destroy_int(pool); - pool = next; - } -} - -static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, - const char *name, - pj_size_t initial_size, - pj_size_t increment_sz, - pj_pool_callback *callback) -{ - pj_caching_pool *cp = (pj_caching_pool*)pf; - pj_pool_t *pool; - int idx; - - PJ_CHECK_STACK(); - - /* Use pool factory's policy when callback is NULL */ - if (callback == NULL) { - callback = pf->policy.callback; - } - - /* Search the suitable size for the pool. - * We'll just do linear search to the size array, as the array size itself - * is only a few elements. Binary search I suspect will be less efficient - * for this purpose. - */ - for (idx=0; - idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size; - ++idx) - ; - - /* Check whether there's a pool in the list. */ - if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) { - /* No pool is available. */ - /* Set minimum size. */ - if (idx < PJ_CACHING_POOL_ARRAY_SIZE) - initial_size = pool_sizes[idx]; - - /* Create new pool */ - pool = pj_pool_create_int(&cp->factory, name, initial_size, - increment_sz, callback); - if (!pool) - return NULL; - - } else { - /* Get one pool from the list. */ - pool = cp->free_list[idx].next; - pj_list_erase(pool); - - /* Initialize the pool. */ - pj_pool_init_int(pool, name, increment_sz, callback); - - /* Update pool manager's free capacity. */ - cp->capacity -= pj_pool_get_capacity(pool); - - PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity)); - } - - /* Put in used list. */ - pj_list_insert_before( &cp->used_list, pool ); - - /* Increment used count. */ - ++cp->used_count; - return pool; -} - -static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool) -{ - pj_caching_pool *cp = (pj_caching_pool*)pf; - int i; - - PJ_CHECK_STACK(); - - /* Erase from the used list. */ - pj_list_erase(pool); - - /* Decrement used count. */ - --cp->used_count; - - /* Destroy the pool if the size is greater than our size or if the total - * capacity in our recycle list (plus the size of the pool) exceeds - * maximum capacity. - . */ - if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] || - cp->capacity + pool->capacity > cp->max_capacity) - { - pj_pool_destroy_int(pool); - return; - } - - /* Reset pool. */ - PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", - pool->capacity, pool->used_size, pool->used_size*100/pool->capacity)); - pj_pool_reset(pool); - - /* - * Otherwise put the pool in our recycle list. - */ - for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i) - ; - - pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE ); - if (i == PJ_CACHING_POOL_ARRAY_SIZE) { - /* Something has gone wrong with the pool. */ - pj_pool_destroy_int(pool); - return; - } - - pj_list_insert_after(&cp->free_list[i], pool); - cp->capacity += pool->capacity; -} - -static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ) -{ -#if PJ_LOG_MAX_LEVEL >= 3 - pj_caching_pool *cp = (pj_caching_pool*)factory; - PJ_LOG(3,("cachpool", " Dumping caching pool:")); - PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \ - cp->capacity, cp->max_capacity, cp->used_count)); - if (detail) { - pj_pool_t *pool = cp->used_list.next; - pj_uint32_t total_used = 0, total_capacity = 0; - PJ_LOG(3,("cachpool", " Dumping all active pools:")); - while (pool != (void*)&cp->used_list) { - PJ_LOG(3,("cachpool", " %12s: %8d of %8d (%d%%) used", pool->obj_name, - pool->used_size, pool->capacity, - pool->used_size*100/pool->capacity)); - total_used += pool->used_size; - total_capacity += pool->capacity; - pool = pool->next; - } - PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!", - total_used, total_capacity, - total_used * 100 / total_capacity)); - } -#endif -} +/* $Header: /pjproject-0.3/pjlib/src/pj/pool_caching.c 5 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/pool_caching.c $ + * + * 5 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include + +static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, + const char *name, + pj_size_t initial_size, + pj_size_t increment_sz, + pj_pool_callback *callback); +static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool); +static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ); + +static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = +{ + 256, 512, 1024, 2048, 4096, 8192, 12288, 16384, + 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536 +}; + + +PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, + const pj_pool_factory_policy *policy, + pj_size_t max_capacity) +{ + int i; + + PJ_CHECK_STACK(); + + pj_memset(cp, 0, sizeof(*cp)); + + cp->max_capacity = max_capacity; + pj_list_init(&cp->used_list); + for (i=0; ifree_list[i]); + + pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy)); + cp->factory.create_pool = &cpool_create_pool; + cp->factory.release_pool = &cpool_release_pool; + cp->factory.dump_status = &cpool_dump_status; +} + +PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp ) +{ + int i; + pj_pool_t *pool; + + PJ_CHECK_STACK(); + + /* Delete all pool in free list */ + for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) { + pj_pool_t *pool = cp->free_list[i].next; + pj_pool_t *next; + for (; pool != (void*)&cp->free_list[i]; pool = next) { + next = pool->next; + pj_list_erase(pool); + pj_pool_destroy_int(pool); + } + } + + /* Delete all pools in used list */ + pool = cp->used_list.next; + while (pool != (pj_pool_t*) &cp->used_list) { + pj_pool_t *next = pool->next; + pj_list_erase(pool); + pj_pool_destroy_int(pool); + pool = next; + } +} + +static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, + const char *name, + pj_size_t initial_size, + pj_size_t increment_sz, + pj_pool_callback *callback) +{ + pj_caching_pool *cp = (pj_caching_pool*)pf; + pj_pool_t *pool; + int idx; + + PJ_CHECK_STACK(); + + /* Use pool factory's policy when callback is NULL */ + if (callback == NULL) { + callback = pf->policy.callback; + } + + /* Search the suitable size for the pool. + * We'll just do linear search to the size array, as the array size itself + * is only a few elements. Binary search I suspect will be less efficient + * for this purpose. + */ + for (idx=0; + idx < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[idx] < initial_size; + ++idx) + ; + + /* Check whether there's a pool in the list. */ + if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) { + /* No pool is available. */ + /* Set minimum size. */ + if (idx < PJ_CACHING_POOL_ARRAY_SIZE) + initial_size = pool_sizes[idx]; + + /* Create new pool */ + pool = pj_pool_create_int(&cp->factory, name, initial_size, + increment_sz, callback); + if (!pool) + return NULL; + + } else { + /* Get one pool from the list. */ + pool = cp->free_list[idx].next; + pj_list_erase(pool); + + /* Initialize the pool. */ + pj_pool_init_int(pool, name, increment_sz, callback); + + /* Update pool manager's free capacity. */ + cp->capacity -= pj_pool_get_capacity(pool); + + PJ_LOG(5, (pool->obj_name, "pool reused, size=%u", pool->capacity)); + } + + /* Put in used list. */ + pj_list_insert_before( &cp->used_list, pool ); + + /* Increment used count. */ + ++cp->used_count; + return pool; +} + +static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool) +{ + pj_caching_pool *cp = (pj_caching_pool*)pf; + int i; + + PJ_CHECK_STACK(); + + /* Erase from the used list. */ + pj_list_erase(pool); + + /* Decrement used count. */ + --cp->used_count; + + /* Destroy the pool if the size is greater than our size or if the total + * capacity in our recycle list (plus the size of the pool) exceeds + * maximum capacity. + . */ + if (pool->capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] || + cp->capacity + pool->capacity > cp->max_capacity) + { + pj_pool_destroy_int(pool); + return; + } + + /* Reset pool. */ + PJ_LOG(4, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", + pool->capacity, pool->used_size, pool->used_size*100/pool->capacity)); + pj_pool_reset(pool); + + /* + * Otherwise put the pool in our recycle list. + */ + for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE && pool_sizes[i] != pool->capacity; ++i) + ; + + pj_assert( i != PJ_CACHING_POOL_ARRAY_SIZE ); + if (i == PJ_CACHING_POOL_ARRAY_SIZE) { + /* Something has gone wrong with the pool. */ + pj_pool_destroy_int(pool); + return; + } + + pj_list_insert_after(&cp->free_list[i], pool); + cp->capacity += pool->capacity; +} + +static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail ) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + pj_caching_pool *cp = (pj_caching_pool*)factory; + PJ_LOG(3,("cachpool", " Dumping caching pool:")); + PJ_LOG(3,("cachpool", " Capacity=%u, max_capacity=%u, used_cnt=%u", \ + cp->capacity, cp->max_capacity, cp->used_count)); + if (detail) { + pj_pool_t *pool = cp->used_list.next; + pj_uint32_t total_used = 0, total_capacity = 0; + PJ_LOG(3,("cachpool", " Dumping all active pools:")); + while (pool != (void*)&cp->used_list) { + PJ_LOG(3,("cachpool", " %12s: %8d of %8d (%d%%) used", pool->obj_name, + pool->used_size, pool->capacity, + pool->used_size*100/pool->capacity)); + total_used += pool->used_size; + total_capacity += pool->capacity; + pool = pool->next; + } + PJ_LOG(3,("cachpool", " Total %9d of %9d (%d %%) used!", + total_used, total_capacity, + total_used * 100 / total_capacity)); + } +#endif +} diff --git a/pjlib/src/pj/pool_dbg_win32.c b/pjlib/src/pj/pool_dbg_win32.c index 4419d048c..a4d404db3 100644 --- a/pjlib/src/pj/pool_dbg_win32.c +++ b/pjlib/src/pj/pool_dbg_win32.c @@ -1,226 +1,226 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/pool_dbg_win32.c 4 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/pool_dbg_win32.c $ - * - * 4 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include - -/* Only if we ARE debugging memory allocations. */ -#if PJ_POOL_DEBUG - -#include -#include - -#include -#include -#define WIN32_LEAN_AND_MEAN -#include - -typedef struct memory_entry -{ - PJ_DECL_LIST_MEMBER(struct memory_entry) - void *ptr; - char *file; - int line; -} memory_entry; - -struct pj_pool_t -{ - char obj_name[32]; - HANDLE hHeap; - memory_entry first; - pj_size_t initial_size; - pj_size_t increment; - pj_size_t used_size; - char *file; - int line; -}; - -PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t), - void (*free_func)(void *ptr, pj_size_t)) -{ - /* Ignored. */ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(malloc_func) - PJ_UNUSED_ARG(free_func) -} - -PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name, - pj_size_t initial_size, - pj_size_t increment_size, - pj_pool_callback *callback, - char *file, int line) -{ - pj_pool_t *pool; - HANDLE hHeap; - - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(callback) - - /* Create Win32 heap for the pool. */ - hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, - initial_size, 0); - if (!hHeap) { - return NULL; - } - - - /* Create and initialize the pool structure. */ - pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, - sizeof(*pool)); - memset(pool, 0, sizeof(*pool)); - pool->file = file; - pool->line = line; - pool->hHeap = hHeap; - pool->initial_size = initial_size; - pool->increment = increment_size; - pool->used_size = 0; - - /* Set name. */ - if (name) { - if (strchr(name, '%') != NULL) { - sprintf(pool->obj_name, name, pool); - } else { - strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); - } - } else { - pool->obj_name[0] = '\0'; - } - - /* List pool's entry. */ - pj_list_init(&pool->first); - - PJ_LOG(3,(pool->obj_name, "Pool created")); - return pool; -} - -PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool ) -{ - memory_entry *entry; - - PJ_CHECK_STACK(); - - PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u", - pool->initial_size, pool->used_size)); - - if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) { - PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d", - pool->file, pool->line)); - } - - /* Validate all memory entries in the pool. */ - for (entry=pool->first.next; entry != &pool->first; entry = entry->next) { - if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) { - PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d", - entry->file, entry->line)); - } - - if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) { - PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d", - entry->file, entry->line)); - } - } - - /* Destroy heap. */ - HeapDestroy(pool->hHeap); -} - -PJ_DEF(void) pj_pool_reset( pj_pool_t *pool ) -{ - /* Do nothing. */ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool) -} - -PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool) - return 0; -} - -PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool) - return 0; -} - -PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool ) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool) - return 0; -} - -PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size, - char *file, int line) -{ - memory_entry *entry; - int entry_size; - - PJ_CHECK_STACK(); - - entry_size = sizeof(*entry); - entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, - entry_size); - entry->file = file; - entry->line = line; - entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, - size); - pj_list_insert_before( &pool->first, entry); - - pool->used_size += size; - return entry->ptr; -} - -PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem, - char *file, int line) -{ - void *ptr; - - PJ_CHECK_STACK(); - - ptr = pj_pool_alloc_dbg(pool, count*elem, file, line); - memset(ptr, 0, count*elem); - return ptr; -} - - -PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool, - pj_size_t max_capacity) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool_pool) - PJ_UNUSED_ARG(max_capacity) -} - -PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool ) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool_pool) -} - -PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool, - const char *name, - pj_size_t initial_size, - pj_size_t increment_size, - pj_pool_callback *callback) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool_pool) - return pj_pool_create(name, initial_size, increment_size, callback); -} - -PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool, - pj_pool_t *pool ) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool_pool) - pj_pool_destroy(pool); -} - - -#endif /* PJ_POOL_DEBUG */ +/* $Header: /pjproject-0.3/pjlib/src/pj/pool_dbg_win32.c 4 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/pool_dbg_win32.c $ + * + * 4 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include + +/* Only if we ARE debugging memory allocations. */ +#if PJ_POOL_DEBUG + +#include +#include + +#include +#include +#define WIN32_LEAN_AND_MEAN +#include + +typedef struct memory_entry +{ + PJ_DECL_LIST_MEMBER(struct memory_entry) + void *ptr; + char *file; + int line; +} memory_entry; + +struct pj_pool_t +{ + char obj_name[32]; + HANDLE hHeap; + memory_entry first; + pj_size_t initial_size; + pj_size_t increment; + pj_size_t used_size; + char *file; + int line; +}; + +PJ_DEF(void) pj_pool_set_functions( void *(*malloc_func)(pj_size_t), + void (*free_func)(void *ptr, pj_size_t)) +{ + /* Ignored. */ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(malloc_func) + PJ_UNUSED_ARG(free_func) +} + +PJ_DEF(pj_pool_t*) pj_pool_create_dbg( const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback, + char *file, int line) +{ + pj_pool_t *pool; + HANDLE hHeap; + + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(callback) + + /* Create Win32 heap for the pool. */ + hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + initial_size, 0); + if (!hHeap) { + return NULL; + } + + + /* Create and initialize the pool structure. */ + pool = HeapAlloc(hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + sizeof(*pool)); + memset(pool, 0, sizeof(*pool)); + pool->file = file; + pool->line = line; + pool->hHeap = hHeap; + pool->initial_size = initial_size; + pool->increment = increment_size; + pool->used_size = 0; + + /* Set name. */ + if (name) { + if (strchr(name, '%') != NULL) { + sprintf(pool->obj_name, name, pool); + } else { + strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME); + } + } else { + pool->obj_name[0] = '\0'; + } + + /* List pool's entry. */ + pj_list_init(&pool->first); + + PJ_LOG(3,(pool->obj_name, "Pool created")); + return pool; +} + +PJ_DEF(void) pj_pool_destroy( pj_pool_t *pool ) +{ + memory_entry *entry; + + PJ_CHECK_STACK(); + + PJ_LOG(3,(pool->obj_name, "Destoying pool, init_size=%u, used=%u", + pool->initial_size, pool->used_size)); + + if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, pool)) { + PJ_LOG(2,(pool->obj_name, "Corrupted pool structure, allocated in %s:%d", + pool->file, pool->line)); + } + + /* Validate all memory entries in the pool. */ + for (entry=pool->first.next; entry != &pool->first; entry = entry->next) { + if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry)) { + PJ_LOG(2,(pool->obj_name, "Corrupted pool entry, allocated in %s:%d", + entry->file, entry->line)); + } + + if (!HeapValidate( pool->hHeap, HEAP_NO_SERIALIZE, entry->ptr)) { + PJ_LOG(2,(pool->obj_name, "Corrupted pool memory, allocated in %s:%d", + entry->file, entry->line)); + } + } + + /* Destroy heap. */ + HeapDestroy(pool->hHeap); +} + +PJ_DEF(void) pj_pool_reset( pj_pool_t *pool ) +{ + /* Do nothing. */ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) +} + +PJ_DEF(pj_size_t) pj_pool_get_capacity( pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) + return 0; +} + +PJ_DEF(pj_size_t) pj_pool_get_used_size( pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) + return 0; +} + +PJ_DEF(pj_size_t) pj_pool_get_request_count( pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool) + return 0; +} + +PJ_DEF(void*) pj_pool_alloc_dbg( pj_pool_t *pool, pj_size_t size, + char *file, int line) +{ + memory_entry *entry; + int entry_size; + + PJ_CHECK_STACK(); + + entry_size = sizeof(*entry); + entry = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + entry_size); + entry->file = file; + entry->line = line; + entry->ptr = HeapAlloc(pool->hHeap, HEAP_GENERATE_EXCEPTIONS|HEAP_NO_SERIALIZE, + size); + pj_list_insert_before( &pool->first, entry); + + pool->used_size += size; + return entry->ptr; +} + +PJ_DEF(void*) pj_pool_calloc_dbg( pj_pool_t *pool, pj_size_t count, pj_size_t elem, + char *file, int line) +{ + void *ptr; + + PJ_CHECK_STACK(); + + ptr = pj_pool_alloc_dbg(pool, count*elem, file, line); + memset(ptr, 0, count*elem); + return ptr; +} + + +PJ_DEF(void) pj_pool_pool_init( pj_pool_pool_t *pool_pool, + pj_size_t max_capacity) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) + PJ_UNUSED_ARG(max_capacity) +} + +PJ_DEF(void) pj_pool_pool_destroy( pj_pool_pool_t *pool_pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) +} + +PJ_DEF(pj_pool_t*) pj_pool_pool_create_pool( pj_pool_pool_t *pool_pool, + const char *name, + pj_size_t initial_size, + pj_size_t increment_size, + pj_pool_callback *callback) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) + return pj_pool_create(name, initial_size, increment_size, callback); +} + +PJ_DEF(void) pj_pool_pool_release_pool( pj_pool_pool_t *pool_pool, + pj_pool_t *pool ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool_pool) + pj_pool_destroy(pool); +} + + +#endif /* PJ_POOL_DEBUG */ diff --git a/pjlib/src/pj/pool_policy_kmalloc.c b/pjlib/src/pj/pool_policy_kmalloc.c index 4accff7d4..0c7f81015 100644 --- a/pjlib/src/pj/pool_policy_kmalloc.c +++ b/pjlib/src/pj/pool_policy_kmalloc.c @@ -1,54 +1,54 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/pool_policy_kmalloc.c 3 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/pool_policy_kmalloc.c $ - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/22/05 10:40a Bennylp - * Created. - * - */ -#include -#include -#include - - -static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(factory); - - return kmalloc(size, GFP_ATOMIC); -} - -static void default_block_free(pj_pool_factory *factory, - void *mem, pj_size_t size) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(factory); - PJ_UNUSED_ARG(size); - - kfree(mem); -} - -static void default_pool_callback(pj_pool_t *pool, pj_size_t size) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(size); - - PJ_THROW(PJ_NO_MEMORY_EXCEPTION); -} - -pj_pool_factory_policy pj_pool_factory_default_policy = -{ - &default_block_alloc, - &default_block_free, - &default_pool_callback, - 0 -}; - +/* $Header: /pjproject-0.3/pjlib/src/pj/pool_policy_kmalloc.c 3 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/pool_policy_kmalloc.c $ + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/22/05 10:40a Bennylp + * Created. + * + */ +#include +#include +#include + + +static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + + return kmalloc(size, GFP_ATOMIC); +} + +static void default_block_free(pj_pool_factory *factory, + void *mem, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(size); + + kfree(mem); +} + +static void default_pool_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJ_NO_MEMORY_EXCEPTION); +} + +pj_pool_factory_policy pj_pool_factory_default_policy = +{ + &default_block_alloc, + &default_block_free, + &default_pool_callback, + 0 +}; + diff --git a/pjlib/src/pj/pool_policy_malloc.c b/pjlib/src/pj/pool_policy_malloc.c index 12eb7c343..eb31deeab 100644 --- a/pjlib/src/pj/pool_policy_malloc.c +++ b/pjlib/src/pj/pool_policy_malloc.c @@ -1,58 +1,58 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/pool_policy_malloc.c 2 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/pool_policy_malloc.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/21/05 1:37p Bennylp - * Renamed from pool_policy.c - * - * 3 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include - -/* - * This file contains pool default policy definition and implementation. - */ - - -static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(factory); - PJ_UNUSED_ARG(size); - - return malloc(size); -} - -static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(factory); - PJ_UNUSED_ARG(size); - - free(mem); -} - -static void default_pool_callback(pj_pool_t *pool, pj_size_t size) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(size); - - PJ_THROW(PJ_NO_MEMORY_EXCEPTION); -} - -pj_pool_factory_policy pj_pool_factory_default_policy = -{ - &default_block_alloc, - &default_block_free, - &default_pool_callback, - 0 -}; +/* $Header: /pjproject-0.3/pjlib/src/pj/pool_policy_malloc.c 2 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/pool_policy_malloc.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/21/05 1:37p Bennylp + * Renamed from pool_policy.c + * + * 3 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include + +/* + * This file contains pool default policy definition and implementation. + */ + + +static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(size); + + return malloc(size); +} + +static void default_block_free(pj_pool_factory *factory, void *mem, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(factory); + PJ_UNUSED_ARG(size); + + free(mem); +} + +static void default_pool_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); + + PJ_THROW(PJ_NO_MEMORY_EXCEPTION); +} + +pj_pool_factory_policy pj_pool_factory_default_policy = +{ + &default_block_alloc, + &default_block_free, + &default_pool_callback, + 0 +}; diff --git a/pjlib/src/pj/rand.c b/pjlib/src/pj/rand.c index 6d25670f5..2e0065db5 100644 --- a/pjlib/src/pj/rand.c +++ b/pjlib/src/pj/rand.c @@ -1,29 +1,29 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/rand.c 3 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/rand.c $ - * - * 3 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - * 1 9/15/05 8:40p Bennylp - * Created. - */ -#include -#include -#include - -PJ_DEF(void) pj_srand(unsigned int seed) -{ - PJ_CHECK_STACK(); - platform_srand(seed); -} - -PJ_DEF(int) pj_rand(void) -{ - PJ_CHECK_STACK(); - return platform_rand(); -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/rand.c 3 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/rand.c $ + * + * 3 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + * 1 9/15/05 8:40p Bennylp + * Created. + */ +#include +#include +#include + +PJ_DEF(void) pj_srand(unsigned int seed) +{ + PJ_CHECK_STACK(); + platform_srand(seed); +} + +PJ_DEF(int) pj_rand(void) +{ + PJ_CHECK_STACK(); + return platform_rand(); +} + diff --git a/pjlib/src/pj/rbtree.c b/pjlib/src/pj/rbtree.c index 582a6f75e..027084e72 100644 --- a/pjlib/src/pj/rbtree.c +++ b/pjlib/src/pj/rbtree.c @@ -1,416 +1,416 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/rbtree.c 5 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/rbtree.c $ - * - * 5 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include - -static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node ) -{ - pj_rbtree_node *rnode, *parent; - - PJ_CHECK_STACK(); - - rnode = node->right; - if (rnode == tree->null) - return; - - node->right = rnode->left; - if (rnode->left != tree->null) - rnode->left->parent = node; - parent = node->parent; - rnode->parent = parent; - if (parent != tree->null) { - if (parent->left == node) - parent->left = rnode; - else - parent->right = rnode; - } else { - tree->root = rnode; - } - rnode->left = node; - node->parent = rnode; -} - -static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node ) -{ - pj_rbtree_node *lnode, *parent; - - PJ_CHECK_STACK(); - - lnode = node->left; - if (lnode == tree->null) - return; - - node->left = lnode->right; - if (lnode->right != tree->null) - lnode->right->parent = node; - parent = node->parent; - lnode->parent = parent; - - if (parent != tree->null) { - if (parent->left == node) - parent->left = lnode; - else - parent->right = lnode; - } else { - tree->root = lnode; - } - lnode->right = node; - node->parent = lnode; -} - -static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node ) -{ - pj_rbtree_node *temp, *parent; - - PJ_CHECK_STACK(); - - while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) { - parent = node->parent; - if (parent == parent->parent->left) { - temp = parent->parent->right; - if (temp->color == PJ_RBCOLOR_RED) { - temp->color = PJ_RBCOLOR_BLACK; - node = parent; - node->color = PJ_RBCOLOR_BLACK; - node = node->parent; - node->color = PJ_RBCOLOR_RED; - } else { - if (node == parent->right) { - node = parent; - left_rotate(tree, node); - } - temp = node->parent; - temp->color = PJ_RBCOLOR_BLACK; - temp = temp->parent; - temp->color = PJ_RBCOLOR_RED; - right_rotate( tree, temp); - } - } else { - temp = parent->parent->left; - if (temp->color == PJ_RBCOLOR_RED) { - temp->color = PJ_RBCOLOR_BLACK; - node = parent; - node->color = PJ_RBCOLOR_BLACK; - node = node->parent; - node->color = PJ_RBCOLOR_RED; - } else { - if (node == parent->left) { - node = parent; - right_rotate(tree, node); - } - temp = node->parent; - temp->color = PJ_RBCOLOR_BLACK; - temp = temp->parent; - temp->color = PJ_RBCOLOR_RED; - left_rotate(tree, temp); - } - } - } - - tree->root->color = PJ_RBCOLOR_BLACK; -} - - -static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node ) -{ - pj_rbtree_node *temp; - - PJ_CHECK_STACK(); - - while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) { - if (node->parent->left == node) { - temp = node->parent->right; - if (temp->color == PJ_RBCOLOR_RED) { - temp->color = PJ_RBCOLOR_BLACK; - node->parent->color = PJ_RBCOLOR_RED; - left_rotate(tree, node->parent); - temp = node->parent->right; - } - if (temp->left->color == PJ_RBCOLOR_BLACK && - temp->right->color == PJ_RBCOLOR_BLACK) - { - temp->color = PJ_RBCOLOR_RED; - node = node->parent; - } else { - if (temp->right->color == PJ_RBCOLOR_BLACK) { - temp->left->color = PJ_RBCOLOR_BLACK; - temp->color = PJ_RBCOLOR_RED; - right_rotate( tree, temp); - temp = node->parent->right; - } - temp->color = node->parent->color; - temp->right->color = PJ_RBCOLOR_BLACK; - node->parent->color = PJ_RBCOLOR_BLACK; - left_rotate(tree, node->parent); - node = tree->root; - } - } else { - temp = node->parent->left; - if (temp->color == PJ_RBCOLOR_RED) { - temp->color = PJ_RBCOLOR_BLACK; - node->parent->color = PJ_RBCOLOR_RED; - right_rotate( tree, node->parent); - temp = node->parent->left; - } - if (temp->right->color == PJ_RBCOLOR_BLACK && - temp->left->color == PJ_RBCOLOR_BLACK) - { - temp->color = PJ_RBCOLOR_RED; - node = node->parent; - } else { - if (temp->left->color == PJ_RBCOLOR_BLACK) { - temp->right->color = PJ_RBCOLOR_BLACK; - temp->color = PJ_RBCOLOR_RED; - left_rotate( tree, temp); - temp = node->parent->left; - } - temp->color = node->parent->color; - node->parent->color = PJ_RBCOLOR_BLACK; - temp->left->color = PJ_RBCOLOR_BLACK; - right_rotate(tree, node->parent); - node = tree->root; - } - } - } - - node->color = PJ_RBCOLOR_BLACK; -} - - -PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp ) -{ - PJ_CHECK_STACK(); - - tree->null = tree->root = &tree->null_node; - tree->null->key = NULL; - tree->null->user_data = NULL; - tree->size = 0; - tree->null->left = tree->null->right = tree->null->parent = tree->null; - tree->null->color = PJ_RBCOLOR_BLACK; - tree->comp = comp; -} - -PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ) -{ - register pj_rbtree_node *node = tree->root; - register pj_rbtree_node *null = tree->null; - - PJ_CHECK_STACK(); - - while (node->left != null) - node = node->left; - return node != null ? node : NULL; -} - -PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ) -{ - register pj_rbtree_node *node = tree->root; - register pj_rbtree_node *null = tree->null; - - PJ_CHECK_STACK(); - - while (node->right != null) - node = node->right; - return node != null ? node : NULL; -} - -PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, - register pj_rbtree_node *node ) -{ - register pj_rbtree_node *null = tree->null; - - PJ_CHECK_STACK(); - - if (node->right != null) { - for (node=node->right; node->left!=null; node = node->left) - /* void */; - } else { - register pj_rbtree_node *temp = node->parent; - while (temp!=null && temp->right==node) { - node = temp; - temp = temp->parent; - } - node = temp; - } - return node != null ? node : NULL; -} - -PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, - register pj_rbtree_node *node ) -{ - register pj_rbtree_node *null = tree->null; - - PJ_CHECK_STACK(); - - if (node->left != null) { - for (node=node->left; node->right!=null; node=node->right) - /* void */; - } else { - register pj_rbtree_node *temp = node->parent; - while (temp!=null && temp->left==node) { - node = temp; - temp = temp->parent; - } - node = temp; - } - return node != null ? node : NULL; -} - -PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree, - pj_rbtree_node *element ) -{ - int rv = 0; - pj_rbtree_node *node, *parent = tree->null, - *null = tree->null; - pj_rbtree_comp *comp = tree->comp; - - PJ_CHECK_STACK(); - - node = tree->root; - while (node != null) { - rv = (*comp)(element->key, node->key); - if (rv == 0) { - /* found match, i.e. entry with equal key already exist */ - return -1; - } - parent = node; - node = rv < 0 ? node->left : node->right; - } - - element->color = PJ_RBCOLOR_RED; - element->left = element->right = null; - - node = element; - if (parent != null) { - node->parent = parent; - if (rv < 0) - parent->left = node; - else - parent->right = node; - insert_fixup( tree, node); - } else { - tree->root = node; - node->parent = null; - node->color = PJ_RBCOLOR_BLACK; - } - - ++tree->size; - return 0; -} - - -PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, - const void *key ) -{ - int rv; - pj_rbtree_node *node = tree->root; - pj_rbtree_node *null = tree->null; - pj_rbtree_comp *comp = tree->comp; - - while (node != null) { - rv = (*comp)(key, node->key); - if (rv == 0) - return node; - node = rv < 0 ? node->left : node->right; - } - return node != null ? node : NULL; -} - -PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, - pj_rbtree_node *node ) -{ - pj_rbtree_node *succ; - pj_rbtree_node *null = tree->null; - pj_rbtree_node *child; - pj_rbtree_node *parent; - - PJ_CHECK_STACK(); - - if (node->left == null || node->right == null) { - succ = node; - } else { - for (succ=node->right; succ->left!=null; succ=succ->left) - /* void */; - } - - child = succ->left != null ? succ->left : succ->right; - parent = succ->parent; - child->parent = parent; - - if (parent != null) { - if (parent->left == succ) - parent->left = child; - else - parent->right = child; - } else - tree->root = child; - - if (succ != node) { - succ->parent = node->parent; - succ->left = node->left; - succ->right = node->right; - succ->color = node->color; - - parent = node->parent; - if (parent != null) { - if (parent->left==node) - parent->left=succ; - else - parent->right=succ; - } - if (node->left != null) - node->left->parent = succ;; - if (node->right != null) - node->right->parent = succ; - - if (tree->root == node) - tree->root = succ; - } - - if (succ->color == PJ_RBCOLOR_BLACK) { - if (child != null) - delete_fixup(tree, child); - tree->null->color = PJ_RBCOLOR_BLACK; - } - - --tree->size; - return node; -} - - -PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree, - pj_rbtree_node *node ) -{ - unsigned l, r; - - PJ_CHECK_STACK(); - - if (node==NULL) - node = tree->root; - - l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0; - r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0; - return l > r ? l : r; -} - -PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree, - pj_rbtree_node *node ) -{ - unsigned l, r; - - PJ_CHECK_STACK(); - - if (node==NULL) - node=tree->root; - - l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0; - r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0; - return l > r ? r : l; -} - - +/* $Header: /pjproject-0.3/pjlib/src/pj/rbtree.c 5 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/rbtree.c $ + * + * 5 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include + +static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *rnode, *parent; + + PJ_CHECK_STACK(); + + rnode = node->right; + if (rnode == tree->null) + return; + + node->right = rnode->left; + if (rnode->left != tree->null) + rnode->left->parent = node; + parent = node->parent; + rnode->parent = parent; + if (parent != tree->null) { + if (parent->left == node) + parent->left = rnode; + else + parent->right = rnode; + } else { + tree->root = rnode; + } + rnode->left = node; + node->parent = rnode; +} + +static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *lnode, *parent; + + PJ_CHECK_STACK(); + + lnode = node->left; + if (lnode == tree->null) + return; + + node->left = lnode->right; + if (lnode->right != tree->null) + lnode->right->parent = node; + parent = node->parent; + lnode->parent = parent; + + if (parent != tree->null) { + if (parent->left == node) + parent->left = lnode; + else + parent->right = lnode; + } else { + tree->root = lnode; + } + lnode->right = node; + node->parent = lnode; +} + +static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *temp, *parent; + + PJ_CHECK_STACK(); + + while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) { + parent = node->parent; + if (parent == parent->parent->left) { + temp = parent->parent->right; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node = parent; + node->color = PJ_RBCOLOR_BLACK; + node = node->parent; + node->color = PJ_RBCOLOR_RED; + } else { + if (node == parent->right) { + node = parent; + left_rotate(tree, node); + } + temp = node->parent; + temp->color = PJ_RBCOLOR_BLACK; + temp = temp->parent; + temp->color = PJ_RBCOLOR_RED; + right_rotate( tree, temp); + } + } else { + temp = parent->parent->left; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node = parent; + node->color = PJ_RBCOLOR_BLACK; + node = node->parent; + node->color = PJ_RBCOLOR_RED; + } else { + if (node == parent->left) { + node = parent; + right_rotate(tree, node); + } + temp = node->parent; + temp->color = PJ_RBCOLOR_BLACK; + temp = temp->parent; + temp->color = PJ_RBCOLOR_RED; + left_rotate(tree, temp); + } + } + } + + tree->root->color = PJ_RBCOLOR_BLACK; +} + + +static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node ) +{ + pj_rbtree_node *temp; + + PJ_CHECK_STACK(); + + while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) { + if (node->parent->left == node) { + temp = node->parent->right; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_RED; + left_rotate(tree, node->parent); + temp = node->parent->right; + } + if (temp->left->color == PJ_RBCOLOR_BLACK && + temp->right->color == PJ_RBCOLOR_BLACK) + { + temp->color = PJ_RBCOLOR_RED; + node = node->parent; + } else { + if (temp->right->color == PJ_RBCOLOR_BLACK) { + temp->left->color = PJ_RBCOLOR_BLACK; + temp->color = PJ_RBCOLOR_RED; + right_rotate( tree, temp); + temp = node->parent->right; + } + temp->color = node->parent->color; + temp->right->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_BLACK; + left_rotate(tree, node->parent); + node = tree->root; + } + } else { + temp = node->parent->left; + if (temp->color == PJ_RBCOLOR_RED) { + temp->color = PJ_RBCOLOR_BLACK; + node->parent->color = PJ_RBCOLOR_RED; + right_rotate( tree, node->parent); + temp = node->parent->left; + } + if (temp->right->color == PJ_RBCOLOR_BLACK && + temp->left->color == PJ_RBCOLOR_BLACK) + { + temp->color = PJ_RBCOLOR_RED; + node = node->parent; + } else { + if (temp->left->color == PJ_RBCOLOR_BLACK) { + temp->right->color = PJ_RBCOLOR_BLACK; + temp->color = PJ_RBCOLOR_RED; + left_rotate( tree, temp); + temp = node->parent->left; + } + temp->color = node->parent->color; + node->parent->color = PJ_RBCOLOR_BLACK; + temp->left->color = PJ_RBCOLOR_BLACK; + right_rotate(tree, node->parent); + node = tree->root; + } + } + } + + node->color = PJ_RBCOLOR_BLACK; +} + + +PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp ) +{ + PJ_CHECK_STACK(); + + tree->null = tree->root = &tree->null_node; + tree->null->key = NULL; + tree->null->user_data = NULL; + tree->size = 0; + tree->null->left = tree->null->right = tree->null->parent = tree->null; + tree->null->color = PJ_RBCOLOR_BLACK; + tree->comp = comp; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree ) +{ + register pj_rbtree_node *node = tree->root; + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + while (node->left != null) + node = node->left; + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree ) +{ + register pj_rbtree_node *node = tree->root; + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + while (node->right != null) + node = node->right; + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, + register pj_rbtree_node *node ) +{ + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + if (node->right != null) { + for (node=node->right; node->left!=null; node = node->left) + /* void */; + } else { + register pj_rbtree_node *temp = node->parent; + while (temp!=null && temp->right==node) { + node = temp; + temp = temp->parent; + } + node = temp; + } + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, + register pj_rbtree_node *node ) +{ + register pj_rbtree_node *null = tree->null; + + PJ_CHECK_STACK(); + + if (node->left != null) { + for (node=node->left; node->right!=null; node=node->right) + /* void */; + } else { + register pj_rbtree_node *temp = node->parent; + while (temp!=null && temp->left==node) { + node = temp; + temp = temp->parent; + } + node = temp; + } + return node != null ? node : NULL; +} + +PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree, + pj_rbtree_node *element ) +{ + int rv = 0; + pj_rbtree_node *node, *parent = tree->null, + *null = tree->null; + pj_rbtree_comp *comp = tree->comp; + + PJ_CHECK_STACK(); + + node = tree->root; + while (node != null) { + rv = (*comp)(element->key, node->key); + if (rv == 0) { + /* found match, i.e. entry with equal key already exist */ + return -1; + } + parent = node; + node = rv < 0 ? node->left : node->right; + } + + element->color = PJ_RBCOLOR_RED; + element->left = element->right = null; + + node = element; + if (parent != null) { + node->parent = parent; + if (rv < 0) + parent->left = node; + else + parent->right = node; + insert_fixup( tree, node); + } else { + tree->root = node; + node->parent = null; + node->color = PJ_RBCOLOR_BLACK; + } + + ++tree->size; + return 0; +} + + +PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree, + const void *key ) +{ + int rv; + pj_rbtree_node *node = tree->root; + pj_rbtree_node *null = tree->null; + pj_rbtree_comp *comp = tree->comp; + + while (node != null) { + rv = (*comp)(key, node->key); + if (rv == 0) + return node; + node = rv < 0 ? node->left : node->right; + } + return node != null ? node : NULL; +} + +PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + pj_rbtree_node *succ; + pj_rbtree_node *null = tree->null; + pj_rbtree_node *child; + pj_rbtree_node *parent; + + PJ_CHECK_STACK(); + + if (node->left == null || node->right == null) { + succ = node; + } else { + for (succ=node->right; succ->left!=null; succ=succ->left) + /* void */; + } + + child = succ->left != null ? succ->left : succ->right; + parent = succ->parent; + child->parent = parent; + + if (parent != null) { + if (parent->left == succ) + parent->left = child; + else + parent->right = child; + } else + tree->root = child; + + if (succ != node) { + succ->parent = node->parent; + succ->left = node->left; + succ->right = node->right; + succ->color = node->color; + + parent = node->parent; + if (parent != null) { + if (parent->left==node) + parent->left=succ; + else + parent->right=succ; + } + if (node->left != null) + node->left->parent = succ;; + if (node->right != null) + node->right->parent = succ; + + if (tree->root == node) + tree->root = succ; + } + + if (succ->color == PJ_RBCOLOR_BLACK) { + if (child != null) + delete_fixup(tree, child); + tree->null->color = PJ_RBCOLOR_BLACK; + } + + --tree->size; + return node; +} + + +PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + unsigned l, r; + + PJ_CHECK_STACK(); + + if (node==NULL) + node = tree->root; + + l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0; + r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0; + return l > r ? l : r; +} + +PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree, + pj_rbtree_node *node ) +{ + unsigned l, r; + + PJ_CHECK_STACK(); + + if (node==NULL) + node=tree->root; + + l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0; + r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0; + return l > r ? r : l; +} + + diff --git a/pjlib/src/pj/scanner.c b/pjlib/src/pj/scanner.c index 08c7c7574..b8296cba1 100644 --- a/pjlib/src/pj/scanner.c +++ b/pjlib/src/pj/scanner.c @@ -1,556 +1,556 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/scanner.c 9 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/scanner.c $ - * - * 9 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 8 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 7 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include - -#define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t') -#define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n') -#define PJ_SCAN_CHECK_EOF(s) (s != end) - - -static void pj_scan_syntax_err(pj_scanner *scanner) -{ - (*scanner->callback)(scanner); -} - -PJ_DEF(void) pj_cs_init( pj_char_spec cs) -{ - PJ_CHECK_STACK(); - memset(cs, 0, sizeof(cs)); -} - -PJ_DEF(void) pj_cs_set( pj_char_spec cs, int c) -{ - PJ_CHECK_STACK(); - cs[c] = 1; -} - -PJ_DEF(void) pj_cs_add_range( pj_char_spec cs, int cstart, int cend) -{ - PJ_CHECK_STACK(); - while (cstart != cend) - cs[cstart++] = 1; -} - -PJ_DEF(void) pj_cs_add_alpha( pj_char_spec cs) -{ - pj_cs_add_range( cs, 'a', 'z'+1); - pj_cs_add_range( cs, 'A', 'Z'+1); -} - -PJ_DEF(void) pj_cs_add_num( pj_char_spec cs) -{ - pj_cs_add_range( cs, '0', '9'+1); -} - -PJ_DEF(void) pj_cs_add_str( pj_char_spec cs, const char *str) -{ - PJ_CHECK_STACK(); - while (*str) { - cs[(int)*str] = 1; - ++str; - } -} - -PJ_DEF(void) pj_cs_del_range( pj_char_spec cs, int cstart, int cend) -{ - PJ_CHECK_STACK(); - while (cstart != cend) - cs[cstart++] = 0; -} - -PJ_DEF(void) pj_cs_del_str( pj_char_spec cs, const char *str) -{ - PJ_CHECK_STACK(); - while (*str) { - cs[(int)*str] = 0; - ++str; - } -} - -PJ_DEF(void) pj_cs_invert( pj_char_spec cs ) -{ - unsigned i; - PJ_CHECK_STACK(); - for (i=0; ibegin = scanner->curptr = bufstart; - scanner->end = bufstart + buflen; - scanner->line = 1; - scanner->col = 1; - scanner->callback = callback; - scanner->skip_ws = options; - - if (scanner->skip_ws) - pj_scan_skip_whitespace(scanner); - - scanner->col = scanner->curptr - scanner->begin + 1; -} - - -PJ_DEF(void) pj_scan_fini( pj_scanner *scanner ) -{ - PJ_CHECK_STACK(); - PJ_UNUSED_ARG(scanner); -} - -PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner ) -{ - register char *s = scanner->curptr; - - PJ_CHECK_STACK(); - - while (PJ_SCAN_IS_SPACE(*s)) { - ++s; - } - - if ((scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE) && PJ_SCAN_IS_NEWLINE(*s)) { - for (;;) { - if (*s == '\r') { - ++s; - if (*s == '\n') ++s; - ++scanner->line; - scanner->col = 1; - scanner->curptr = s; - } else if (*s == '\n') { - ++s; - ++scanner->line; - scanner->col = 1; - scanner->curptr = s; - } else if (PJ_SCAN_IS_SPACE(*s)) { - do { - ++s; - } while (PJ_SCAN_IS_SPACE(*s)); - } else { - break; - } - } - } - - if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) { - /* Check for header continuation. */ - scanner->col += s - scanner->curptr; - scanner->curptr = s; - - if (*s == '\r') { - ++s; - } - if (*s == '\n') { - ++s; - } - if (PJ_SCAN_IS_SPACE(*s)) { - register char *t = s; - do { - ++t; - } while (PJ_SCAN_IS_SPACE(*t)); - - ++scanner->line; - scanner->col = t-s; - scanner->curptr = t; - } - } else { - scanner->col += s - scanner->curptr; - scanner->curptr = s; - } -} - -PJ_DEF(int) pj_scan_peek( pj_scanner *scanner, - const pj_char_spec spec, pj_str_t *out) -{ - register char *s = scanner->curptr; - register char *end = scanner->end; - - PJ_CHECK_STACK(); - - if (pj_scan_is_eof(scanner)) { - pj_scan_syntax_err(scanner); - return -1; - } - - while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s)) - ++s; - - pj_strset3(out, scanner->curptr, s); - return s < scanner->end ? *s : 0; -} - - -PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner, - pj_size_t len, pj_str_t *out) -{ - char *endpos = scanner->curptr + len; - - PJ_CHECK_STACK(); - - if (endpos > scanner->end) { - pj_scan_syntax_err(scanner); - return -1; - } - - pj_strset(out, scanner->curptr, len); - return *endpos; -} - - -PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner, - const pj_char_spec spec, - pj_str_t *out) -{ - register char *s = scanner->curptr; - register char *end = scanner->end; - - PJ_CHECK_STACK(); - - if (pj_scan_is_eof(scanner)) { - pj_scan_syntax_err(scanner); - return -1; - } - - while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match( spec, *s)) - ++s; - - pj_strset3(out, scanner->curptr, s); - return s!=scanner->end ? *s : 0; -} - - -PJ_DEF(void) pj_scan_get( pj_scanner *scanner, - const pj_char_spec spec, pj_str_t *out) -{ - register char *s = scanner->curptr; - register char *end = scanner->end; - char *start = s; - - PJ_CHECK_STACK(); - - if (pj_scan_is_eof(scanner) || !pj_cs_match(spec, *s)) { - pj_scan_syntax_err(scanner); - return; - } - - do { - ++s; - } while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s)); - - pj_strset3(out, scanner->curptr, s); - - scanner->col += (s - start); - scanner->curptr = s; - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - - -PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner, - int begin_quote, int end_quote, - pj_str_t *out) -{ - register char *s = scanner->curptr; - register char *end = scanner->end; - char *start = s; - - PJ_CHECK_STACK(); - - /* Check and eat the begin_quote. */ - if (*s != begin_quote) { - pj_scan_syntax_err(scanner); - return; - } - ++s; - - /* Loop until end_quote is found. - */ - do { - /* loop until end_quote is found. */ - do { - ++s; - } while (s != end && *s != '\n' && *s != end_quote); - - /* check that no backslash character precedes the end_quote. */ - if (*s == end_quote) { - if (*(s-1) == '\\') { - if (s-2 == scanner->begin) { - break; - } else { - char *q = s-2; - char *r = s-2; - - while (r != scanner->begin && *r == '\\') { - --r; - } - /* break from main loop if we have odd number of backslashes */ - if (((unsigned)(q-r) & 0x01) == 1) { - break; - } - } - } else { - /* end_quote is not preceeded by backslash. break now. */ - break; - } - } else { - /* loop ended by non-end_quote character. break now. */ - break; - } - } while (1); - - /* Check and eat the end quote. */ - if (*s != end_quote) { - pj_scan_syntax_err(scanner); - return; - } - ++s; - - pj_strset3(out, scanner->curptr, s); - - scanner->col += (s - start); - scanner->curptr = s; - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - -PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner, - unsigned N, pj_str_t *out) -{ - register char *s = scanner->curptr; - char *start = scanner->curptr; - - PJ_CHECK_STACK(); - - if (scanner->curptr + N > scanner->end) { - pj_scan_syntax_err(scanner); - return; - } - - pj_strset(out, s, N); - - s += N; - scanner->col += (s - start); - scanner->curptr = s; - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - - -PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner ) -{ - char *start = scanner->curptr; - int chr = *start; - - PJ_CHECK_STACK(); - - if (pj_scan_is_eof(scanner)) { - pj_scan_syntax_err(scanner); - return 0; - } - - ++scanner->curptr; - scanner->col += (scanner->curptr - start); - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } - return chr; -} - - -PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner ) -{ - PJ_CHECK_STACK(); - - if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) { - pj_scan_syntax_err(scanner); - return; - } - - if (*scanner->curptr == '\r') { - ++scanner->curptr; - } - if (*scanner->curptr == '\n') { - ++scanner->curptr; - } - - ++scanner->line; - scanner->col = 1; - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - - -PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner, - const pj_char_spec spec, pj_str_t *out) -{ - register char *s = scanner->curptr; - register char *end = scanner->end; - char *start = s; - - PJ_CHECK_STACK(); - - if (pj_scan_is_eof(scanner)) { - pj_scan_syntax_err(scanner); - return; - } - - while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match(spec, *s)) { - ++s; - } - - pj_strset3(out, scanner->curptr, s); - - scanner->col += (s - start); - scanner->curptr = s; - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - - -PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner, - int until_char, pj_str_t *out) -{ - register char *s = scanner->curptr; - register char *end = scanner->end; - char *start = s; - - PJ_CHECK_STACK(); - - if (pj_scan_is_eof(scanner)) { - pj_scan_syntax_err(scanner); - return; - } - - while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) { - ++s; - } - - pj_strset3(out, scanner->curptr, s); - - scanner->col += (s - start); - scanner->curptr = s; - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - - -PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner, - const char *until_spec, pj_str_t *out) -{ - register char *s = scanner->curptr; - register char *end = scanner->end; - char *start = scanner->curptr; - - PJ_CHECK_STACK(); - - if (pj_scan_is_eof(scanner)) { - pj_scan_syntax_err(scanner); - return; - } - - while (PJ_SCAN_CHECK_EOF(s) && !strchr(until_spec, *s)) { - ++s; - } - - pj_strset3(out, scanner->curptr, s); - - scanner->col += (s - start); - scanner->curptr = s; - - if (scanner->skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - -PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner, - unsigned N, pj_bool_t skip_ws) -{ - char *start = scanner->curptr; - - PJ_CHECK_STACK(); - - if (scanner->curptr + N > scanner->end) { - pj_scan_syntax_err(scanner); - return; - } - - scanner->curptr += N; - scanner->col += (scanner->curptr - start); - - if (skip_ws) { - pj_scan_skip_whitespace(scanner); - } -} - - -PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len) -{ - if (scanner->curptr + len > scanner->end) { - pj_scan_syntax_err(scanner); - return -1; - } - return strncmp(scanner->curptr, s, len); -} - - -PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len) -{ - if (scanner->curptr + len > scanner->end) { - pj_scan_syntax_err(scanner); - return -1; - } - return strnicmp(scanner->curptr, s, len); -} - - -PJ_DEF(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state) -{ - PJ_CHECK_STACK(); - - state->curptr = scanner->curptr; - state->line = scanner->line; - state->col = scanner->col; -} - - -PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner, - pj_scan_state *state) -{ - PJ_CHECK_STACK(); - - scanner->curptr = state->curptr; - scanner->line = state->line; - scanner->col = state->col; -} - - +/* $Header: /pjproject-0.3/pjlib/src/pj/scanner.c 9 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/scanner.c $ + * + * 9 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 8 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 7 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include + +#define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t') +#define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n') +#define PJ_SCAN_CHECK_EOF(s) (s != end) + + +static void pj_scan_syntax_err(pj_scanner *scanner) +{ + (*scanner->callback)(scanner); +} + +PJ_DEF(void) pj_cs_init( pj_char_spec cs) +{ + PJ_CHECK_STACK(); + memset(cs, 0, sizeof(cs)); +} + +PJ_DEF(void) pj_cs_set( pj_char_spec cs, int c) +{ + PJ_CHECK_STACK(); + cs[c] = 1; +} + +PJ_DEF(void) pj_cs_add_range( pj_char_spec cs, int cstart, int cend) +{ + PJ_CHECK_STACK(); + while (cstart != cend) + cs[cstart++] = 1; +} + +PJ_DEF(void) pj_cs_add_alpha( pj_char_spec cs) +{ + pj_cs_add_range( cs, 'a', 'z'+1); + pj_cs_add_range( cs, 'A', 'Z'+1); +} + +PJ_DEF(void) pj_cs_add_num( pj_char_spec cs) +{ + pj_cs_add_range( cs, '0', '9'+1); +} + +PJ_DEF(void) pj_cs_add_str( pj_char_spec cs, const char *str) +{ + PJ_CHECK_STACK(); + while (*str) { + cs[(int)*str] = 1; + ++str; + } +} + +PJ_DEF(void) pj_cs_del_range( pj_char_spec cs, int cstart, int cend) +{ + PJ_CHECK_STACK(); + while (cstart != cend) + cs[cstart++] = 0; +} + +PJ_DEF(void) pj_cs_del_str( pj_char_spec cs, const char *str) +{ + PJ_CHECK_STACK(); + while (*str) { + cs[(int)*str] = 0; + ++str; + } +} + +PJ_DEF(void) pj_cs_invert( pj_char_spec cs ) +{ + unsigned i; + PJ_CHECK_STACK(); + for (i=0; ibegin = scanner->curptr = bufstart; + scanner->end = bufstart + buflen; + scanner->line = 1; + scanner->col = 1; + scanner->callback = callback; + scanner->skip_ws = options; + + if (scanner->skip_ws) + pj_scan_skip_whitespace(scanner); + + scanner->col = scanner->curptr - scanner->begin + 1; +} + + +PJ_DEF(void) pj_scan_fini( pj_scanner *scanner ) +{ + PJ_CHECK_STACK(); + PJ_UNUSED_ARG(scanner); +} + +PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner ) +{ + register char *s = scanner->curptr; + + PJ_CHECK_STACK(); + + while (PJ_SCAN_IS_SPACE(*s)) { + ++s; + } + + if ((scanner->skip_ws & PJ_SCAN_AUTOSKIP_NEWLINE) && PJ_SCAN_IS_NEWLINE(*s)) { + for (;;) { + if (*s == '\r') { + ++s; + if (*s == '\n') ++s; + ++scanner->line; + scanner->col = 1; + scanner->curptr = s; + } else if (*s == '\n') { + ++s; + ++scanner->line; + scanner->col = 1; + scanner->curptr = s; + } else if (PJ_SCAN_IS_SPACE(*s)) { + do { + ++s; + } while (PJ_SCAN_IS_SPACE(*s)); + } else { + break; + } + } + } + + if (PJ_SCAN_IS_NEWLINE(*s) && (scanner->skip_ws & PJ_SCAN_AUTOSKIP_WS_HEADER)==PJ_SCAN_AUTOSKIP_WS_HEADER) { + /* Check for header continuation. */ + scanner->col += s - scanner->curptr; + scanner->curptr = s; + + if (*s == '\r') { + ++s; + } + if (*s == '\n') { + ++s; + } + if (PJ_SCAN_IS_SPACE(*s)) { + register char *t = s; + do { + ++t; + } while (PJ_SCAN_IS_SPACE(*t)); + + ++scanner->line; + scanner->col = t-s; + scanner->curptr = t; + } + } else { + scanner->col += s - scanner->curptr; + scanner->curptr = s; + } +} + +PJ_DEF(int) pj_scan_peek( pj_scanner *scanner, + const pj_char_spec spec, pj_str_t *out) +{ + register char *s = scanner->curptr; + register char *end = scanner->end; + + PJ_CHECK_STACK(); + + if (pj_scan_is_eof(scanner)) { + pj_scan_syntax_err(scanner); + return -1; + } + + while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s)) + ++s; + + pj_strset3(out, scanner->curptr, s); + return s < scanner->end ? *s : 0; +} + + +PJ_DEF(int) pj_scan_peek_n( pj_scanner *scanner, + pj_size_t len, pj_str_t *out) +{ + char *endpos = scanner->curptr + len; + + PJ_CHECK_STACK(); + + if (endpos > scanner->end) { + pj_scan_syntax_err(scanner); + return -1; + } + + pj_strset(out, scanner->curptr, len); + return *endpos; +} + + +PJ_DEF(int) pj_scan_peek_until( pj_scanner *scanner, + const pj_char_spec spec, + pj_str_t *out) +{ + register char *s = scanner->curptr; + register char *end = scanner->end; + + PJ_CHECK_STACK(); + + if (pj_scan_is_eof(scanner)) { + pj_scan_syntax_err(scanner); + return -1; + } + + while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match( spec, *s)) + ++s; + + pj_strset3(out, scanner->curptr, s); + return s!=scanner->end ? *s : 0; +} + + +PJ_DEF(void) pj_scan_get( pj_scanner *scanner, + const pj_char_spec spec, pj_str_t *out) +{ + register char *s = scanner->curptr; + register char *end = scanner->end; + char *start = s; + + PJ_CHECK_STACK(); + + if (pj_scan_is_eof(scanner) || !pj_cs_match(spec, *s)) { + pj_scan_syntax_err(scanner); + return; + } + + do { + ++s; + } while (PJ_SCAN_CHECK_EOF(s) && pj_cs_match(spec, *s)); + + pj_strset3(out, scanner->curptr, s); + + scanner->col += (s - start); + scanner->curptr = s; + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + + +PJ_DEF(void) pj_scan_get_quote( pj_scanner *scanner, + int begin_quote, int end_quote, + pj_str_t *out) +{ + register char *s = scanner->curptr; + register char *end = scanner->end; + char *start = s; + + PJ_CHECK_STACK(); + + /* Check and eat the begin_quote. */ + if (*s != begin_quote) { + pj_scan_syntax_err(scanner); + return; + } + ++s; + + /* Loop until end_quote is found. + */ + do { + /* loop until end_quote is found. */ + do { + ++s; + } while (s != end && *s != '\n' && *s != end_quote); + + /* check that no backslash character precedes the end_quote. */ + if (*s == end_quote) { + if (*(s-1) == '\\') { + if (s-2 == scanner->begin) { + break; + } else { + char *q = s-2; + char *r = s-2; + + while (r != scanner->begin && *r == '\\') { + --r; + } + /* break from main loop if we have odd number of backslashes */ + if (((unsigned)(q-r) & 0x01) == 1) { + break; + } + } + } else { + /* end_quote is not preceeded by backslash. break now. */ + break; + } + } else { + /* loop ended by non-end_quote character. break now. */ + break; + } + } while (1); + + /* Check and eat the end quote. */ + if (*s != end_quote) { + pj_scan_syntax_err(scanner); + return; + } + ++s; + + pj_strset3(out, scanner->curptr, s); + + scanner->col += (s - start); + scanner->curptr = s; + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + +PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner, + unsigned N, pj_str_t *out) +{ + register char *s = scanner->curptr; + char *start = scanner->curptr; + + PJ_CHECK_STACK(); + + if (scanner->curptr + N > scanner->end) { + pj_scan_syntax_err(scanner); + return; + } + + pj_strset(out, s, N); + + s += N; + scanner->col += (s - start); + scanner->curptr = s; + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + + +PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner ) +{ + char *start = scanner->curptr; + int chr = *start; + + PJ_CHECK_STACK(); + + if (pj_scan_is_eof(scanner)) { + pj_scan_syntax_err(scanner); + return 0; + } + + ++scanner->curptr; + scanner->col += (scanner->curptr - start); + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } + return chr; +} + + +PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner ) +{ + PJ_CHECK_STACK(); + + if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) { + pj_scan_syntax_err(scanner); + return; + } + + if (*scanner->curptr == '\r') { + ++scanner->curptr; + } + if (*scanner->curptr == '\n') { + ++scanner->curptr; + } + + ++scanner->line; + scanner->col = 1; + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + + +PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner, + const pj_char_spec spec, pj_str_t *out) +{ + register char *s = scanner->curptr; + register char *end = scanner->end; + char *start = s; + + PJ_CHECK_STACK(); + + if (pj_scan_is_eof(scanner)) { + pj_scan_syntax_err(scanner); + return; + } + + while (PJ_SCAN_CHECK_EOF(s) && !pj_cs_match(spec, *s)) { + ++s; + } + + pj_strset3(out, scanner->curptr, s); + + scanner->col += (s - start); + scanner->curptr = s; + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + + +PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner, + int until_char, pj_str_t *out) +{ + register char *s = scanner->curptr; + register char *end = scanner->end; + char *start = s; + + PJ_CHECK_STACK(); + + if (pj_scan_is_eof(scanner)) { + pj_scan_syntax_err(scanner); + return; + } + + while (PJ_SCAN_CHECK_EOF(s) && *s != until_char) { + ++s; + } + + pj_strset3(out, scanner->curptr, s); + + scanner->col += (s - start); + scanner->curptr = s; + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + + +PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner, + const char *until_spec, pj_str_t *out) +{ + register char *s = scanner->curptr; + register char *end = scanner->end; + char *start = scanner->curptr; + + PJ_CHECK_STACK(); + + if (pj_scan_is_eof(scanner)) { + pj_scan_syntax_err(scanner); + return; + } + + while (PJ_SCAN_CHECK_EOF(s) && !strchr(until_spec, *s)) { + ++s; + } + + pj_strset3(out, scanner->curptr, s); + + scanner->col += (s - start); + scanner->curptr = s; + + if (scanner->skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + +PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner, + unsigned N, pj_bool_t skip_ws) +{ + char *start = scanner->curptr; + + PJ_CHECK_STACK(); + + if (scanner->curptr + N > scanner->end) { + pj_scan_syntax_err(scanner); + return; + } + + scanner->curptr += N; + scanner->col += (scanner->curptr - start); + + if (skip_ws) { + pj_scan_skip_whitespace(scanner); + } +} + + +PJ_DEF(int) pj_scan_strcmp( pj_scanner *scanner, const char *s, int len) +{ + if (scanner->curptr + len > scanner->end) { + pj_scan_syntax_err(scanner); + return -1; + } + return strncmp(scanner->curptr, s, len); +} + + +PJ_DEF(int) pj_scan_stricmp( pj_scanner *scanner, const char *s, int len) +{ + if (scanner->curptr + len > scanner->end) { + pj_scan_syntax_err(scanner); + return -1; + } + return strnicmp(scanner->curptr, s, len); +} + + +PJ_DEF(void) pj_scan_save_state( pj_scanner *scanner, pj_scan_state *state) +{ + PJ_CHECK_STACK(); + + state->curptr = scanner->curptr; + state->line = scanner->line; + state->col = scanner->col; +} + + +PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner, + pj_scan_state *state) +{ + PJ_CHECK_STACK(); + + scanner->curptr = state->curptr; + scanner->line = state->line; + scanner->col = state->col; +} + + diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c index c69b7e25c..1465d4872 100644 --- a/pjlib/src/pj/sock_bsd.c +++ b/pjlib/src/pj/sock_bsd.c @@ -1,572 +1,572 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/sock_bsd.c 10 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/sock_bsd.c $ - * - * 10 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 9 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 8 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 7 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include -#include - -/* - * Address families conversion. - * The values here are indexed based on pj_addr_family-0xFF00. - */ -const pj_uint16_t PJ_AF_UNIX = AF_UNIX; -const pj_uint16_t PJ_AF_INET = AF_INET; -const pj_uint16_t PJ_AF_INET6 = AF_INET6; -#ifdef AF_PACKET -const pj_uint16_t PJ_AF_PACKET = AF_PACKET; -#else -const pj_uint16_t PJ_AF_PACKET = 0xFFFF; -#endif -#ifdef AF_IRDA -const pj_uint16_t PJ_AF_IRDA = AF_IRDA; -#else -const pj_uint16_t PJ_AF_IRDA = 0xFFFF; -#endif - -/* - * Socket types conversion. - * The values here are indexed based on pj_sock_type-0xFF00 - */ -const pj_uint16_t PJ_SOCK_STREAM = SOCK_STREAM; -const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; -const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; -const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; - -/* - * Socket level values. - */ -const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; -#ifdef SOL_IP -const pj_uint16_t PJ_SOL_IP = SOL_IP; -#else -const pj_uint16_t PJ_SOL_IP = 0xFFFF; -#endif /* SOL_IP */ -#if defined(SOL_TCP) -const pj_uint16_t PJ_SOL_TCP = SOL_TCP; -#elif defined(IPPROTO_TCP) -const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; -#endif /* SOL_TCP */ -#ifdef SOL_UDP -const pj_uint16_t PJ_SOL_UDP = SOL_UDP; -#else -const pj_uint16_t PJ_SOL_UDP = 0xFFFF; -#endif -#ifdef SOL_IPV6 -const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; -#else -const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF; -#endif - - -/* - * Convert 16-bit value from network byte order to host byte order. - */ -PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) -{ - return ntohs(netshort); -} - -/* - * Convert 16-bit value from host byte order to network byte order. - */ -PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) -{ - return htons(hostshort); -} - -/* - * Convert 32-bit value from network byte order to host byte order. - */ -PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) -{ - return ntohl(netlong); -} - -/* - * Convert 32-bit value from host byte order to network byte order. - */ -PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) -{ - return htonl(hostlong); -} - -/* - * Convert an Internet host address given in network byte order - * to string in standard numbers and dots notation. - */ -PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) -{ - return inet_ntoa(*(struct in_addr*)&inaddr); -} - -/* - * This function converts the Internet host address cp from the standard - * numbers-and-dots notation into binary data and stores it in the structure - * that inp points to. - */ -PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp) -{ - char tempaddr[16]; - - /* Initialize output with PJ_INADDR_NONE. - * Some apps relies on this instead of the return value - * (and anyway the return value is quite confusing!) - */ - inp->s_addr = PJ_INADDR_NONE; - - /* Caution: - * this function might be called with cp->slen >= 16 - * (i.e. when called with hostname to check if it's an IP addr). - */ - PJ_ASSERT_RETURN(cp && cp->slen && inp, 0); - if (cp->slen >= 16) { - return 0; - } - - pj_memcpy(tempaddr, cp->ptr, cp->slen); - tempaddr[cp->slen] = '\0'; - -#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0 - return inet_aton(tempaddr, (struct in_addr*)inp); -#else - inp->s_addr = inet_addr(tempaddr); - return inp->s_addr == PJ_INADDR_NONE ? 0 : 1; -#endif -} - -/* - * Convert address string with numbers and dots to binary IP address. - */ -PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) -{ - pj_in_addr addr; - - pj_inet_aton(cp, &addr); - return addr; -} - -/* - * Set the IP address of an IP socket address from string address, - * with resolving the host if necessary. The string address may be in a - * standard numbers and dots notation or may be a hostname. If hostname - * is specified, then the function will resolve the host into the IP - * address. - */ -PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, - const pj_str_t *str_addr) -{ - PJ_CHECK_STACK(); - - PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME, - (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); - - addr->sin_family = AF_INET; - - if (str_addr && str_addr->slen) { - addr->sin_addr = pj_inet_addr(str_addr); - if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { - pj_hostent he; - pj_status_t rc; - - rc = pj_gethostbyname(str_addr, &he); - if (rc == 0) { - addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; - } else { - addr->sin_addr.s_addr = PJ_INADDR_NONE; - return rc; - } - } - - } else { - addr->sin_addr.s_addr = 0; - } - - return PJ_SUCCESS; -} - -/* - * Set the IP address and port of an IP socket address. - * The string address may be in a standard numbers and dots notation or - * may be a hostname. If hostname is specified, then the function will - * resolve the host into the IP address. - */ -PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, - const pj_str_t *str_addr, - pj_uint16_t port) -{ - PJ_ASSERT_RETURN(addr && str_addr, - (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); - - addr->sin_family = PJ_AF_INET; - pj_sockaddr_in_set_port(addr, port); - return pj_sockaddr_in_set_str_addr(addr, str_addr); -} - - -/* - * Get hostname. - */ -PJ_DEF(const pj_str_t*) pj_gethostname(void) -{ - static char buf[PJ_MAX_HOSTNAME]; - static pj_str_t hostname; - - PJ_CHECK_STACK(); - - if (hostname.ptr == NULL) { - hostname.ptr = buf; - if (gethostname(buf, sizeof(buf)) != 0) { - hostname.ptr[0] = '\0'; - hostname.slen = 0; - } else { - hostname.slen = strlen(buf); - } - } - return &hostname; -} - -/* - * Get first IP address associated with the hostname. - */ -PJ_DEF(pj_in_addr) pj_gethostaddr(void) -{ - pj_sockaddr_in addr; - const pj_str_t *hostname = pj_gethostname(); - - pj_sockaddr_in_set_str_addr(&addr, hostname); - return addr.sin_addr; -} - - -#if defined(PJ_WIN32) -/* - * Create new socket/endpoint for communication and returns a descriptor. - */ -PJ_DEF(pj_status_t) pj_sock_socket(int af, - int type, - int proto, - pj_sock_t *sock) -{ - PJ_CHECK_STACK(); - - /* Sanity checks. */ - PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); - PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET, - (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); - - *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED); - - if (*sock == PJ_INVALID_SOCKET) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -#else -/* - * Create new socket/endpoint for communication and returns a descriptor. - */ -PJ_DEF(pj_status_t) pj_sock_socket(int af, - int type, - int proto, - pj_sock_t *sock) -{ - - PJ_CHECK_STACK(); - - /* Sanity checks. */ - PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); - PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, - (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); - - *sock = socket(af, type, proto); - if (*sock == PJ_INVALID_SOCKET) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} -#endif - - -/* - * Bind socket. - */ -PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, - const pj_sockaddr_t *addr, - int len) -{ - PJ_CHECK_STACK(); - - PJ_ASSERT_RETURN(addr && len > 0, PJ_EINVAL); - - if (bind(sock, (struct sockaddr*)addr, len) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - - -/* - * Bind socket. - */ -PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, - pj_uint32_t addr32, - pj_uint16_t port) -{ - pj_sockaddr_in addr; - - PJ_CHECK_STACK(); - - addr.sin_family = PJ_AF_INET; - addr.sin_addr.s_addr = pj_htonl(addr32); - addr.sin_port = pj_htons(port); - - return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in)); -} - - -/* - * Close socket. - */ -PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock) -{ - int rc; - - PJ_CHECK_STACK(); -#if defined(PJ_WIN32) && PJ_WIN32==1 - rc = closesocket(sock); -#else - rc = close(sock); -#endif - - if (rc != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Get remote's name. - */ -PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock, - pj_sockaddr_t *addr, - int *namelen) -{ - PJ_CHECK_STACK(); - if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Get socket name. - */ -PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, - pj_sockaddr_t *addr, - int *namelen) -{ - PJ_CHECK_STACK(); - if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Send data - */ -PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock, - const void *buf, - pj_ssize_t *len, - unsigned flags) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(len, PJ_EINVAL); - - *len = send(sock, (const char*)buf, *len, flags); - - if (*len < 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - - -/* - * Send data. - */ -PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, - const void *buf, - pj_ssize_t *len, - unsigned flags, - const pj_sockaddr_t *to, - int tolen) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(len, PJ_EINVAL); - - *len = sendto(sock, (const char*)buf, *len, flags, - (const struct sockaddr*)to, tolen); - - if (*len < 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Receive data. - */ -PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, - void *buf, - pj_ssize_t *len, - unsigned flags) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); - - *len = recv(sock, (char*)buf, *len, flags); - - if (*len < 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Receive data. - */ -PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, - void *buf, - pj_ssize_t *len, - unsigned flags, - pj_sockaddr_t *from, - int *fromlen) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); - PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL)); - - *len = recvfrom(sock, (char*)buf, *len, flags, - (struct sockaddr*)from, (socklen_t*)fromlen); - - if (*len < 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Get socket option. - */ -PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock, - int level, - int optname, - void *optval, - int *optlen) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL); - - if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Set socket option. - */ -PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, - int level, - int optname, - const void *optval, - int optlen) -{ - PJ_CHECK_STACK(); - if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Shutdown socket. - */ -#if PJ_HAS_TCP -PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock, - int how) -{ - PJ_CHECK_STACK(); - if (shutdown(sock, how) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Start listening to incoming connections. - */ -PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock, - int backlog) -{ - PJ_CHECK_STACK(); - if (listen(sock, backlog) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Connect socket. - */ -PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock, - const pj_sockaddr_t *addr, - int namelen) -{ - PJ_CHECK_STACK(); - if (connect(sock, (struct sockaddr*)addr, namelen) != 0) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} - -/* - * Accept incoming connections - */ -PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, - pj_sock_t *newsock, - pj_sockaddr_t *addr, - int *addrlen) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL); - - *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); - if (*newsock==PJ_INVALID_SOCKET) - return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); - else - return PJ_SUCCESS; -} -#endif /* PJ_HAS_TCP */ - - +/* $Header: /pjproject-0.3/pjlib/src/pj/sock_bsd.c 10 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/sock_bsd.c $ + * + * 10 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 9 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 8 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 7 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * Address families conversion. + * The values here are indexed based on pj_addr_family-0xFF00. + */ +const pj_uint16_t PJ_AF_UNIX = AF_UNIX; +const pj_uint16_t PJ_AF_INET = AF_INET; +const pj_uint16_t PJ_AF_INET6 = AF_INET6; +#ifdef AF_PACKET +const pj_uint16_t PJ_AF_PACKET = AF_PACKET; +#else +const pj_uint16_t PJ_AF_PACKET = 0xFFFF; +#endif +#ifdef AF_IRDA +const pj_uint16_t PJ_AF_IRDA = AF_IRDA; +#else +const pj_uint16_t PJ_AF_IRDA = 0xFFFF; +#endif + +/* + * Socket types conversion. + * The values here are indexed based on pj_sock_type-0xFF00 + */ +const pj_uint16_t PJ_SOCK_STREAM = SOCK_STREAM; +const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; +const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; +const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; + +/* + * Socket level values. + */ +const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; +#ifdef SOL_IP +const pj_uint16_t PJ_SOL_IP = SOL_IP; +#else +const pj_uint16_t PJ_SOL_IP = 0xFFFF; +#endif /* SOL_IP */ +#if defined(SOL_TCP) +const pj_uint16_t PJ_SOL_TCP = SOL_TCP; +#elif defined(IPPROTO_TCP) +const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; +#endif /* SOL_TCP */ +#ifdef SOL_UDP +const pj_uint16_t PJ_SOL_UDP = SOL_UDP; +#else +const pj_uint16_t PJ_SOL_UDP = 0xFFFF; +#endif +#ifdef SOL_IPV6 +const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; +#else +const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF; +#endif + + +/* + * Convert 16-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) +{ + return ntohs(netshort); +} + +/* + * Convert 16-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) +{ + return htons(hostshort); +} + +/* + * Convert 32-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) +{ + return ntohl(netlong); +} + +/* + * Convert 32-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) +{ + return htonl(hostlong); +} + +/* + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + */ +PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr) +{ + return inet_ntoa(*(struct in_addr*)&inaddr); +} + +/* + * This function converts the Internet host address cp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + */ +PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp) +{ + char tempaddr[16]; + + /* Initialize output with PJ_INADDR_NONE. + * Some apps relies on this instead of the return value + * (and anyway the return value is quite confusing!) + */ + inp->s_addr = PJ_INADDR_NONE; + + /* Caution: + * this function might be called with cp->slen >= 16 + * (i.e. when called with hostname to check if it's an IP addr). + */ + PJ_ASSERT_RETURN(cp && cp->slen && inp, 0); + if (cp->slen >= 16) { + return 0; + } + + pj_memcpy(tempaddr, cp->ptr, cp->slen); + tempaddr[cp->slen] = '\0'; + +#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0 + return inet_aton(tempaddr, (struct in_addr*)inp); +#else + inp->s_addr = inet_addr(tempaddr); + return inp->s_addr == PJ_INADDR_NONE ? 0 : 1; +#endif +} + +/* + * Convert address string with numbers and dots to binary IP address. + */ +PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) +{ + pj_in_addr addr; + + pj_inet_aton(cp, &addr); + return addr; +} + +/* + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *str_addr) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(str_addr && str_addr->slen < PJ_MAX_HOSTNAME, + (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); + + addr->sin_family = AF_INET; + + if (str_addr && str_addr->slen) { + addr->sin_addr = pj_inet_addr(str_addr); + if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { + pj_hostent he; + pj_status_t rc; + + rc = pj_gethostbyname(str_addr, &he); + if (rc == 0) { + addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; + } else { + addr->sin_addr.s_addr = PJ_INADDR_NONE; + return rc; + } + } + + } else { + addr->sin_addr.s_addr = 0; + } + + return PJ_SUCCESS; +} + +/* + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *str_addr, + pj_uint16_t port) +{ + PJ_ASSERT_RETURN(addr && str_addr, + (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL)); + + addr->sin_family = PJ_AF_INET; + pj_sockaddr_in_set_port(addr, port); + return pj_sockaddr_in_set_str_addr(addr, str_addr); +} + + +/* + * Get hostname. + */ +PJ_DEF(const pj_str_t*) pj_gethostname(void) +{ + static char buf[PJ_MAX_HOSTNAME]; + static pj_str_t hostname; + + PJ_CHECK_STACK(); + + if (hostname.ptr == NULL) { + hostname.ptr = buf; + if (gethostname(buf, sizeof(buf)) != 0) { + hostname.ptr[0] = '\0'; + hostname.slen = 0; + } else { + hostname.slen = strlen(buf); + } + } + return &hostname; +} + +/* + * Get first IP address associated with the hostname. + */ +PJ_DEF(pj_in_addr) pj_gethostaddr(void) +{ + pj_sockaddr_in addr; + const pj_str_t *hostname = pj_gethostname(); + + pj_sockaddr_in_set_str_addr(&addr, hostname); + return addr.sin_addr; +} + + +#if defined(PJ_WIN32) +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, + pj_sock_t *sock) +{ + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); + PJ_ASSERT_RETURN((unsigned)PJ_INVALID_SOCKET==INVALID_SOCKET, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED); + + if (*sock == PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +#else +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, + int type, + int proto, + pj_sock_t *sock) +{ + + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL); + PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, + (*sock=PJ_INVALID_SOCKET, PJ_EINVAL)); + + *sock = socket(af, type, proto); + if (*sock == PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} +#endif + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, + const pj_sockaddr_t *addr, + int len) +{ + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(addr && len > 0, PJ_EINVAL); + + if (bind(sock, (struct sockaddr*)addr, len) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, + pj_uint32_t addr32, + pj_uint16_t port) +{ + pj_sockaddr_in addr; + + PJ_CHECK_STACK(); + + addr.sin_family = PJ_AF_INET; + addr.sin_addr.s_addr = pj_htonl(addr32); + addr.sin_port = pj_htons(port); + + return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in)); +} + + +/* + * Close socket. + */ +PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock) +{ + int rc; + + PJ_CHECK_STACK(); +#if defined(PJ_WIN32) && PJ_WIN32==1 + rc = closesocket(sock); +#else + rc = close(sock); +#endif + + if (rc != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Get remote's name. + */ +PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Get socket name. + */ +PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock, + pj_sockaddr_t *addr, + int *namelen) +{ + PJ_CHECK_STACK(); + if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Send data + */ +PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(len, PJ_EINVAL); + + *len = send(sock, (const char*)buf, *len, flags); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + + +/* + * Send data. + */ +PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock, + const void *buf, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *to, + int tolen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(len, PJ_EINVAL); + + *len = sendto(sock, (const char*)buf, *len, flags, + (const struct sockaddr*)to, tolen); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); + + *len = recv(sock, (char*)buf, *len, flags); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock, + void *buf, + pj_ssize_t *len, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(buf && len, PJ_EINVAL); + PJ_ASSERT_RETURN(from && fromlen, (*len=-1, PJ_EINVAL)); + + *len = recvfrom(sock, (char*)buf, *len, flags, + (struct sockaddr*)from, (socklen_t*)fromlen); + + if (*len < 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Get socket option. + */ +PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock, + int level, + int optname, + void *optval, + int *optlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL); + + if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Set socket option. + */ +PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock, + int level, + int optname, + const void *optval, + int optlen) +{ + PJ_CHECK_STACK(); + if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Shutdown socket. + */ +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock, + int how) +{ + PJ_CHECK_STACK(); + if (shutdown(sock, how) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Start listening to incoming connections. + */ +PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock, + int backlog) +{ + PJ_CHECK_STACK(); + if (listen(sock, backlog) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Connect socket. + */ +PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock, + const pj_sockaddr_t *addr, + int namelen) +{ + PJ_CHECK_STACK(); + if (connect(sock, (struct sockaddr*)addr, namelen) != 0) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} + +/* + * Accept incoming connections + */ +PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd, + pj_sock_t *newsock, + pj_sockaddr_t *addr, + int *addrlen) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL); + + *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen); + if (*newsock==PJ_INVALID_SOCKET) + return PJ_RETURN_OS_ERROR(pj_get_native_netos_error()); + else + return PJ_SUCCESS; +} +#endif /* PJ_HAS_TCP */ + + diff --git a/pjlib/src/pj/sock_linux_kernel.c b/pjlib/src/pj/sock_linux_kernel.c index 76bc7bd84..28ed07af9 100644 --- a/pjlib/src/pj/sock_linux_kernel.c +++ b/pjlib/src/pj/sock_linux_kernel.c @@ -1,749 +1,749 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/sock_linux_kernel.c 4 10/29/05 11:51a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pj/sock_linux_kernel.c $ - * - * 4 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 3 10/20/05 9:19a Bennylp - * Updated with new API convention (error code) - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/05/05 4:43p Bennylp - * Created. - * - */ -#include -#include -#include /* pj_memcpy() */ -#include /* PJ_CHECK_STACK() */ -#include /* pj_gethostbyname() */ -#include -#include -#include -#include - -/* Linux kernel specific. */ -#include -#include -//#include -#include -#include /* sys_xxx() */ -#include /* FIONBIO */ -#include /* for pj_gethostname() */ - -/* - * Address families conversion. - * The values here are indexed based on pj_addr_family-0xFF00. - */ -const pj_uint16_t PJ_AF_UNIX = AF_UNIX; -const pj_uint16_t PJ_AF_INET = AF_INET; -const pj_uint16_t PJ_AF_INET6 = AF_INET6; -#ifdef AF_PACKET -const pj_uint16_t PJ_AF_PACKET = AF_PACKET; -#else -# error "AF_PACKET undeclared!" -#endif -#ifdef AF_IRDA -const pj_uint16_t PJ_AF_IRDA = AF_IRDA; -#else -# error "AF_IRDA undeclared!" -#endif - -/* - * Socket types conversion. - * The values here are indexed based on pj_sock_type-0xFF00 - */ -const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM; -const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; -const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; -const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; - -/* - * Socket level values. - */ -const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; -#ifdef SOL_IP -const pj_uint16_t PJ_SOL_IP = SOL_IP; -#else -# error "SOL_IP undeclared!" -#endif /* SOL_IP */ -#if defined(SOL_TCP) -const pj_uint16_t PJ_SOL_TCP = SOL_TCP; -#else -# error "SOL_TCP undeclared!" -#endif /* SOL_TCP */ -#ifdef SOL_UDP -const pj_uint16_t PJ_SOL_UDP = SOL_UDP; -#else -# error "SOL_UDP undeclared!" -#endif -#ifdef SOL_IPV6 -const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; -#else -# error "SOL_IPV6 undeclared!" -#endif - -/* - * Convert 16-bit value from network byte order to host byte order. - */ -PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) -{ - return ntohs(netshort); -} - -/* - * Convert 16-bit value from host byte order to network byte order. - */ -PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) -{ - return htons(hostshort); -} - -/* - * Convert 32-bit value from network byte order to host byte order. - */ -PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) -{ - return ntohl(netlong); -} - -/* - * Convert 32-bit value from host byte order to network byte order. - */ -PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) -{ - return htonl(hostlong); -} - -/* - * Convert an Internet host address given in network byte order - * to string in standard numbers and dots notation. - */ -PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in) -{ -#define UC(b) (((int)b)&0xff) - static char b[18]; - char *p; - - p = (char *)∈ - pj_snprintf(b, sizeof(b), "%d.%d.%d.%d", - UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); - - return b; -} - -/* - * This function converts the Internet host address ccp from the standard - * numbers-and-dots notation into binary data and stores it in the structure - * that inp points to. - */ -PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr) -{ - pj_uint32_t val; - int base, n; - char c; - unsigned parts[4]; - unsigned *pp = parts; - char cp_copy[18]; - char *cp = cp_copy; - - addr->s_addr = PJ_INADDR_NONE; - - if (ccp->slen > 15) return 0; - - pj_memcpy(cp, ccp->ptr, ccp->slen); - cp[ccp->slen] = '\0'; - - c = *cp; - for (;;) { - /* - * Collect number up to ``.''. - * Values are specified as for C: - * 0x=hex, 0=octal, isdigit=decimal. - */ - if (!pj_isdigit((int)c)) - return (0); - val = 0; base = 10; - if (c == '0') { - c = *++cp; - if (c == 'x' || c == 'X') - base = 16, c = *++cp; - else - base = 8; - } - - for (;;) { - if (pj_isascii((int)c) && pj_isdigit((int)c)) { - val = (val * base) + (c - '0'); - c = *++cp; - } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) { - val = (val << 4) | - (c + 10 - (pj_islower((int)c) ? 'a' : 'A')); - c = *++cp; - } else - break; - } - - if (c == '.') { - /* - * Internet format: - * a.b.c.d - * a.b.c (with c treated as 16 bits) - * a.b (with b treated as 24 bits) - */ - if (pp >= parts + 3) - return (0); - *pp++ = val; - c = *++cp; - } else - break; - } - - /* - * Check for trailing characters. - */ - if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c))) - return (0); - /* - * Concoct the address according to - * the number of parts specified. - */ - n = pp - parts + 1; - switch (n) { - case 0: - return (0); /* initial nondigit */ - case 1: /* a -- 32 bits */ - break; - case 2: /* a.b -- 8.24 bits */ - if (val > 0xffffff) - return (0); - val |= parts[0] << 24; - break; - case 3: /* a.b.c -- 8.8.16 bits */ - if (val > 0xffff) - return (0); - val |= (parts[0] << 24) | (parts[1] << 16); - break; - case 4: /* a.b.c.d -- 8.8.8.8 bits */ - if (val > 0xff) - return (0); - val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); - break; - } - - if (addr) - addr->s_addr = pj_htonl(val); - return (1); -} - -/* - * Convert address string with numbers and dots to binary IP address. - */ -PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) -{ - pj_in_addr addr; - pj_inet_aton(cp, &addr); - return addr; -} - -/* - * Set the IP address of an IP socket address from string address, - * with resolving the host if necessary. The string address may be in a - * standard numbers and dots notation or may be a hostname. If hostname - * is specified, then the function will resolve the host into the IP - * address. - */ -PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, - const pj_str_t *str_addr) -{ - PJ_CHECK_STACK(); - - pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME); - - addr->sin_family = AF_INET; - - if (str_addr && str_addr->slen) { - addr->sin_addr = pj_inet_addr(str_addr); - if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { - pj_hostent he; - if (pj_gethostbyname(str_addr, &he) == 0) { - addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; - } else { - addr->sin_addr.s_addr = PJ_INADDR_NONE; - return -1; - } - } - - } else { - addr->sin_addr.s_addr = 0; - } - - return PJ_SUCCESS; -} - -/* - * Set the IP address and port of an IP socket address. - * The string address may be in a standard numbers and dots notation or - * may be a hostname. If hostname is specified, then the function will - * resolve the host into the IP address. - */ -PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, - const pj_str_t *str_addr, - pj_uint16_t port) -{ - pj_assert(addr && str_addr); - - addr->sin_family = PJ_AF_INET; - pj_sockaddr_in_set_port(addr, port); - return pj_sockaddr_in_set_str_addr(addr, str_addr); -} - - -/* - * Get hostname. - */ -PJ_DEF(const pj_str_t*) pj_gethostname(void) -{ - static char buf[PJ_MAX_HOSTNAME]; - static pj_str_t hostname; - - PJ_CHECK_STACK(); - - if (hostname.ptr == NULL) { - hostname.ptr = buf; - down_read(&uts_sem); - hostname.slen = strlen(system_utsname.nodename); - if (hostname.slen > PJ_MAX_HOSTNAME) { - hostname.ptr[0] = '\0'; - hostname.slen = 0; - } else { - pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen); - } - up_read(&uts_sem); - } - return &hostname; -} - -/* - * Get first IP address associated with the hostname. - */ -PJ_DEF(pj_in_addr) pj_gethostaddr(void) -{ - pj_sockaddr_in addr; - const pj_str_t *hostname = pj_gethostname(); - - pj_sockaddr_in_set_str_addr(&addr, hostname); - return addr.sin_addr; -} - - -/* - * Create new socket/endpoint for communication and returns a descriptor. - */ -PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, - pj_sock_t *sock_fd) -{ - long result; - - PJ_CHECK_STACK(); - - /* Sanity checks. */ - PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL); - - /* Initialize returned socket */ - *sock_fd = PJ_INVALID_SOCKET; - - /* Create socket. */ - result = sys_socket(af, type, proto); - if (result < 0) { - return PJ_RETURN_OS_ERROR((-result)); - } - - *sock_fd = result; - - return PJ_SUCCESS; -} - -/* - * Bind socket. - */ -PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd, - const pj_sockaddr_t *addr, - int len) -{ - long err; - mm_segment_t oldfs; - - PJ_CHECK_STACK(); - - PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr), - PJ_EINVAL); - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = sys_bind(sockfd, (struct sockaddr*)addr, len); - - set_fs(oldfs); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - - -/* - * Bind socket. - */ -PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, - pj_uint32_t addr32, - pj_uint16_t port) -{ - pj_sockaddr_in addr; - - PJ_CHECK_STACK(); - - addr.sin_family = PJ_AF_INET; - addr.sin_addr.s_addr = pj_htonl(addr32); - addr.sin_port = pj_htons(port); - - return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in)); -} - -/* - * Close socket. - */ -PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd) -{ - long err; - - err = sys_close(sockfd); - - if (err != 0) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Get remote's name. - */ -PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd, - pj_sockaddr_t *addr, - int *namelen) -{ - mm_segment_t oldfs; - long err; - - PJ_CHECK_STACK(); - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = sys_getpeername( sockfd, addr, namelen); - - set_fs(oldfs); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Get socket name. - */ -PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, - pj_sockaddr_t *addr, - int *namelen) -{ - mm_segment_t oldfs; - int err; - - PJ_CHECK_STACK(); - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = sys_getsockname( sockfd, addr, namelen ); - - set_fs(oldfs); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Send data - */ -PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd, - const void *buf, - pj_ssize_t *len, - unsigned flags) -{ - return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0); -} - - -/* - * Send data. - */ -PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd, - const void *buff, - pj_ssize_t *len, - unsigned flags, - const pj_sockaddr_t *addr, - int addr_len) -{ - long err; - mm_segment_t oldfs; - - PJ_CHECK_STACK(); - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = *len = sys_sendto( sockfd, (void*)buff, *len, flags, - (void*)addr, addr_len ); - - set_fs(oldfs); - - if (err >= 0) { - return PJ_SUCCESS; - } - else { - return PJ_RETURN_OS_ERROR(-err); - } -} - -/* - * Receive data. - */ -PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd, - void *buf, - pj_ssize_t *len, - unsigned flags) -{ - return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL); -} - -/* - * Receive data. - */ -PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, - void *buff, - pj_ssize_t *size, - unsigned flags, - pj_sockaddr_t *from, - int *fromlen) -{ - mm_segment_t oldfs; - long err; - - PJ_CHECK_STACK(); - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen); - - set_fs(oldfs); - - if (err >= 0) { - return PJ_SUCCESS; - } - else { - return PJ_RETURN_OS_ERROR(-err); - } -} - -/* - * Get socket option. - */ -PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, - int level, - int optname, - void *optval, - int *optlen) -{ - mm_segment_t oldfs; - long err; - - PJ_CHECK_STACK(); - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = sys_getsockopt( sockfd, level, optname, optval, optlen); - - set_fs(oldfs); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Set socket option. - */ -PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, - int level, - int optname, - const void *optval, - int optlen) -{ - long err; - mm_segment_t oldfs; - - PJ_CHECK_STACK(); - - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen); - - set_fs(oldfs); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Shutdown socket. - */ -#if PJ_HAS_TCP -PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, - int how) -{ - long err; - - PJ_CHECK_STACK(); - - err = sys_shutdown(sockfd, how); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Start listening to incoming connections. - */ -PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd, - int backlog) -{ - long err; - - PJ_CHECK_STACK(); - - err = sys_listen( sockfd, backlog ); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Connect socket. - */ -PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd, - const pj_sockaddr_t *addr, - int namelen) -{ - long err; - mm_segment_t oldfs; - - PJ_CHECK_STACK(); - - oldfs = get_fs(); - set_fs(KERNEL_DS); - - err = sys_connect( sockfd, (void*)addr, namelen ); - - set_fs(oldfs); - - if (err) - return PJ_RETURN_OS_ERROR(-err); - else - return PJ_SUCCESS; -} - -/* - * Accept incoming connections - */ -PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd, - pj_sock_t *newsockfd, - pj_sockaddr_t *addr, - int *addrlen) -{ - long err; - - PJ_CHECK_STACK(); - - PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL); - - err = sys_accept( sockfd, addr, addrlen); - - if (err < 0) { - *newsockfd = PJ_INVALID_SOCKET; - return PJ_RETURN_OS_ERROR(-err); - } - else { - *newsockfd = err; - return PJ_SUCCESS; - } -} -#endif /* PJ_HAS_TCP */ - - - -/* - * Permission to steal inet_ntoa() and inet_aton() as long as this notice below - * is included: - */ -/* - * Copyright (c) 1983, 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. - */ - +/* $Header: /pjproject-0.3/pjlib/src/pj/sock_linux_kernel.c 4 10/29/05 11:51a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/sock_linux_kernel.c $ + * + * 4 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 3 10/20/05 9:19a Bennylp + * Updated with new API convention (error code) + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/05/05 4:43p Bennylp + * Created. + * + */ +#include +#include +#include /* pj_memcpy() */ +#include /* PJ_CHECK_STACK() */ +#include /* pj_gethostbyname() */ +#include +#include +#include +#include + +/* Linux kernel specific. */ +#include +#include +//#include +#include +#include /* sys_xxx() */ +#include /* FIONBIO */ +#include /* for pj_gethostname() */ + +/* + * Address families conversion. + * The values here are indexed based on pj_addr_family-0xFF00. + */ +const pj_uint16_t PJ_AF_UNIX = AF_UNIX; +const pj_uint16_t PJ_AF_INET = AF_INET; +const pj_uint16_t PJ_AF_INET6 = AF_INET6; +#ifdef AF_PACKET +const pj_uint16_t PJ_AF_PACKET = AF_PACKET; +#else +# error "AF_PACKET undeclared!" +#endif +#ifdef AF_IRDA +const pj_uint16_t PJ_AF_IRDA = AF_IRDA; +#else +# error "AF_IRDA undeclared!" +#endif + +/* + * Socket types conversion. + * The values here are indexed based on pj_sock_type-0xFF00 + */ +const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM; +const pj_uint16_t PJ_SOCK_DGRAM = SOCK_DGRAM; +const pj_uint16_t PJ_SOCK_RAW = SOCK_RAW; +const pj_uint16_t PJ_SOCK_RDM = SOCK_RDM; + +/* + * Socket level values. + */ +const pj_uint16_t PJ_SOL_SOCKET = SOL_SOCKET; +#ifdef SOL_IP +const pj_uint16_t PJ_SOL_IP = SOL_IP; +#else +# error "SOL_IP undeclared!" +#endif /* SOL_IP */ +#if defined(SOL_TCP) +const pj_uint16_t PJ_SOL_TCP = SOL_TCP; +#else +# error "SOL_TCP undeclared!" +#endif /* SOL_TCP */ +#ifdef SOL_UDP +const pj_uint16_t PJ_SOL_UDP = SOL_UDP; +#else +# error "SOL_UDP undeclared!" +#endif +#ifdef SOL_IPV6 +const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; +#else +# error "SOL_IPV6 undeclared!" +#endif + +/* + * Convert 16-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort) +{ + return ntohs(netshort); +} + +/* + * Convert 16-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort) +{ + return htons(hostshort); +} + +/* + * Convert 32-bit value from network byte order to host byte order. + */ +PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong) +{ + return ntohl(netlong); +} + +/* + * Convert 32-bit value from host byte order to network byte order. + */ +PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong) +{ + return htonl(hostlong); +} + +/* + * Convert an Internet host address given in network byte order + * to string in standard numbers and dots notation. + */ +PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in) +{ +#define UC(b) (((int)b)&0xff) + static char b[18]; + char *p; + + p = (char *)∈ + pj_snprintf(b, sizeof(b), "%d.%d.%d.%d", + UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3])); + + return b; +} + +/* + * This function converts the Internet host address ccp from the standard + * numbers-and-dots notation into binary data and stores it in the structure + * that inp points to. + */ +PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr) +{ + pj_uint32_t val; + int base, n; + char c; + unsigned parts[4]; + unsigned *pp = parts; + char cp_copy[18]; + char *cp = cp_copy; + + addr->s_addr = PJ_INADDR_NONE; + + if (ccp->slen > 15) return 0; + + pj_memcpy(cp, ccp->ptr, ccp->slen); + cp[ccp->slen] = '\0'; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!pj_isdigit((int)c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + + for (;;) { + if (pj_isascii((int)c) && pj_isdigit((int)c)) { + val = (val * base) + (c - '0'); + c = *++cp; + } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) { + val = (val << 4) | + (c + 10 - (pj_islower((int)c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + + /* + * Check for trailing characters. + */ + if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + case 0: + return (0); /* initial nondigit */ + case 1: /* a -- 32 bits */ + break; + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + + if (addr) + addr->s_addr = pj_htonl(val); + return (1); +} + +/* + * Convert address string with numbers and dots to binary IP address. + */ +PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp) +{ + pj_in_addr addr; + pj_inet_aton(cp, &addr); + return addr; +} + +/* + * Set the IP address of an IP socket address from string address, + * with resolving the host if necessary. The string address may be in a + * standard numbers and dots notation or may be a hostname. If hostname + * is specified, then the function will resolve the host into the IP + * address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr, + const pj_str_t *str_addr) +{ + PJ_CHECK_STACK(); + + pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME); + + addr->sin_family = AF_INET; + + if (str_addr && str_addr->slen) { + addr->sin_addr = pj_inet_addr(str_addr); + if (addr->sin_addr.s_addr == PJ_INADDR_NONE) { + pj_hostent he; + if (pj_gethostbyname(str_addr, &he) == 0) { + addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr; + } else { + addr->sin_addr.s_addr = PJ_INADDR_NONE; + return -1; + } + } + + } else { + addr->sin_addr.s_addr = 0; + } + + return PJ_SUCCESS; +} + +/* + * Set the IP address and port of an IP socket address. + * The string address may be in a standard numbers and dots notation or + * may be a hostname. If hostname is specified, then the function will + * resolve the host into the IP address. + */ +PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr, + const pj_str_t *str_addr, + pj_uint16_t port) +{ + pj_assert(addr && str_addr); + + addr->sin_family = PJ_AF_INET; + pj_sockaddr_in_set_port(addr, port); + return pj_sockaddr_in_set_str_addr(addr, str_addr); +} + + +/* + * Get hostname. + */ +PJ_DEF(const pj_str_t*) pj_gethostname(void) +{ + static char buf[PJ_MAX_HOSTNAME]; + static pj_str_t hostname; + + PJ_CHECK_STACK(); + + if (hostname.ptr == NULL) { + hostname.ptr = buf; + down_read(&uts_sem); + hostname.slen = strlen(system_utsname.nodename); + if (hostname.slen > PJ_MAX_HOSTNAME) { + hostname.ptr[0] = '\0'; + hostname.slen = 0; + } else { + pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen); + } + up_read(&uts_sem); + } + return &hostname; +} + +/* + * Get first IP address associated with the hostname. + */ +PJ_DEF(pj_in_addr) pj_gethostaddr(void) +{ + pj_sockaddr_in addr; + const pj_str_t *hostname = pj_gethostname(); + + pj_sockaddr_in_set_str_addr(&addr, hostname); + return addr.sin_addr; +} + + +/* + * Create new socket/endpoint for communication and returns a descriptor. + */ +PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, + pj_sock_t *sock_fd) +{ + long result; + + PJ_CHECK_STACK(); + + /* Sanity checks. */ + PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL); + + /* Initialize returned socket */ + *sock_fd = PJ_INVALID_SOCKET; + + /* Create socket. */ + result = sys_socket(af, type, proto); + if (result < 0) { + return PJ_RETURN_OS_ERROR((-result)); + } + + *sock_fd = result; + + return PJ_SUCCESS; +} + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd, + const pj_sockaddr_t *addr, + int len) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr), + PJ_EINVAL); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_bind(sockfd, (struct sockaddr*)addr, len); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + + +/* + * Bind socket. + */ +PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, + pj_uint32_t addr32, + pj_uint16_t port) +{ + pj_sockaddr_in addr; + + PJ_CHECK_STACK(); + + addr.sin_family = PJ_AF_INET; + addr.sin_addr.s_addr = pj_htonl(addr32); + addr.sin_port = pj_htons(port); + + return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in)); +} + +/* + * Close socket. + */ +PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd) +{ + long err; + + err = sys_close(sockfd); + + if (err != 0) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Get remote's name. + */ +PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getpeername( sockfd, addr, namelen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Get socket name. + */ +PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd, + pj_sockaddr_t *addr, + int *namelen) +{ + mm_segment_t oldfs; + int err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getsockname( sockfd, addr, namelen ); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Send data + */ +PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd, + const void *buf, + pj_ssize_t *len, + unsigned flags) +{ + return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0); +} + + +/* + * Send data. + */ +PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd, + const void *buff, + pj_ssize_t *len, + unsigned flags, + const pj_sockaddr_t *addr, + int addr_len) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = *len = sys_sendto( sockfd, (void*)buff, *len, flags, + (void*)addr, addr_len ); + + set_fs(oldfs); + + if (err >= 0) { + return PJ_SUCCESS; + } + else { + return PJ_RETURN_OS_ERROR(-err); + } +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd, + void *buf, + pj_ssize_t *len, + unsigned flags) +{ + return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL); +} + +/* + * Receive data. + */ +PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd, + void *buff, + pj_ssize_t *size, + unsigned flags, + pj_sockaddr_t *from, + int *fromlen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen); + + set_fs(oldfs); + + if (err >= 0) { + return PJ_SUCCESS; + } + else { + return PJ_RETURN_OS_ERROR(-err); + } +} + +/* + * Get socket option. + */ +PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd, + int level, + int optname, + void *optval, + int *optlen) +{ + mm_segment_t oldfs; + long err; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_getsockopt( sockfd, level, optname, optval, optlen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Set socket option. + */ +PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd, + int level, + int optname, + const void *optval, + int optlen) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Shutdown socket. + */ +#if PJ_HAS_TCP +PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd, + int how) +{ + long err; + + PJ_CHECK_STACK(); + + err = sys_shutdown(sockfd, how); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Start listening to incoming connections. + */ +PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd, + int backlog) +{ + long err; + + PJ_CHECK_STACK(); + + err = sys_listen( sockfd, backlog ); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Connect socket. + */ +PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd, + const pj_sockaddr_t *addr, + int namelen) +{ + long err; + mm_segment_t oldfs; + + PJ_CHECK_STACK(); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + + err = sys_connect( sockfd, (void*)addr, namelen ); + + set_fs(oldfs); + + if (err) + return PJ_RETURN_OS_ERROR(-err); + else + return PJ_SUCCESS; +} + +/* + * Accept incoming connections + */ +PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd, + pj_sock_t *newsockfd, + pj_sockaddr_t *addr, + int *addrlen) +{ + long err; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL); + + err = sys_accept( sockfd, addr, addrlen); + + if (err < 0) { + *newsockfd = PJ_INVALID_SOCKET; + return PJ_RETURN_OS_ERROR(-err); + } + else { + *newsockfd = err; + return PJ_SUCCESS; + } +} +#endif /* PJ_HAS_TCP */ + + + +/* + * Permission to steal inet_ntoa() and inet_aton() as long as this notice below + * is included: + */ +/* + * Copyright (c) 1983, 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. + */ + diff --git a/pjlib/src/pj/sock_select.c b/pjlib/src/pj/sock_select.c index 49fa01167..146bbf37c 100644 --- a/pjlib/src/pj/sock_select.c +++ b/pjlib/src/pj/sock_select.c @@ -1,101 +1,105 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/sock_select.c 4 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/sock_select.c $ - * - * 4 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 3 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - * 1 9/15/05 8:40p Bennylp - * Created. - */ -#include -#include -#include -#include -#include - - -#ifdef _MSC_VER -# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_* -#endif - -#define PART_FDSET(p_fdsetp) ((fd_set*)&p_fdsetp->data[1]) -#define PART_COUNT(p_fdsetp) (p_fdsetp->data[0]) - -PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp) -{ - PJ_CHECK_STACK(); - pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); - - FD_ZERO(PART_FDSET(fdsetp)); - PART_COUNT(fdsetp) = 0; -} - - -PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp) -{ - PJ_CHECK_STACK(); - pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); - - if (!PJ_FD_ISSET(fd, fdsetp)) - ++PART_COUNT(fdsetp); - FD_SET(fd, PART_FDSET(fdsetp)); -} - - -PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp) -{ - PJ_CHECK_STACK(); - pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); - - if (PJ_FD_ISSET(fd, fdsetp)) - --PART_COUNT(fdsetp); - FD_CLR(fd, PART_FDSET(fdsetp)); -} - - -PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp) -{ - PJ_CHECK_STACK(); - PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), - 0); - - return FD_ISSET(fd, PART_FDSET(fdsetp)); -} - -PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp) -{ - return PART_COUNT(fdsetp); -} - -PJ_DEF(int) pj_sock_select( int n, - pj_fd_set_t *readfds, - pj_fd_set_t *writefds, - pj_fd_set_t *exceptfds, - const pj_time_val *timeout) -{ - struct timeval os_timeout, *p_os_timeout; - - PJ_CHECK_STACK(); - - PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), - PJ_EBUG); - - if (timeout) { - os_timeout.tv_sec = timeout->sec; - os_timeout.tv_usec = timeout->msec * 1000; - p_os_timeout = &os_timeout; - } else { - p_os_timeout = NULL; - } - - return select(n, PART_FDSET(readfds), PART_FDSET(writefds), - PART_FDSET(exceptfds), p_os_timeout); -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/sock_select.c 4 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/sock_select.c $ + * + * 4 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 3 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + * 1 9/15/05 8:40p Bennylp + * Created. + */ +#include +#include +#include +#include +#include + +#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0 +# include +#endif + +#ifdef _MSC_VER +# pragma warning(disable: 4018) // Signed/unsigned mismatch in FD_* +#endif + +#define PART_FDSET(ps) ((fd_set*)&ps->data[1]) +#define PART_FDSET_OR_NULL(ps) (ps ? PART_FDSET(ps) : NULL) +#define PART_COUNT(ps) (ps->data[0]) + +PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + FD_ZERO(PART_FDSET(fdsetp)); + PART_COUNT(fdsetp) = 0; +} + + +PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + if (!PJ_FD_ISSET(fd, fdsetp)) + ++PART_COUNT(fdsetp); + FD_SET(fd, PART_FDSET(fdsetp)); +} + + +PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set)); + + if (PJ_FD_ISSET(fd, fdsetp)) + --PART_COUNT(fdsetp); + FD_CLR(fd, PART_FDSET(fdsetp)); +} + + +PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp) +{ + PJ_CHECK_STACK(); + PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), + 0); + + return FD_ISSET(fd, PART_FDSET(fdsetp)); +} + +PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp) +{ + return PART_COUNT(fdsetp); +} + +PJ_DEF(int) pj_sock_select( int n, + pj_fd_set_t *readfds, + pj_fd_set_t *writefds, + pj_fd_set_t *exceptfds, + const pj_time_val *timeout) +{ + struct timeval os_timeout, *p_os_timeout; + + PJ_CHECK_STACK(); + + PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set), + PJ_EBUG); + + if (timeout) { + os_timeout.tv_sec = timeout->sec; + os_timeout.tv_usec = timeout->msec * 1000; + p_os_timeout = &os_timeout; + } else { + p_os_timeout = NULL; + } + + return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds), + PART_FDSET_OR_NULL(exceptfds), p_os_timeout); +} + diff --git a/pjlib/src/pj/string.c b/pjlib/src/pj/string.c index c3f1b5ff1..3430b2339 100644 --- a/pjlib/src/pj/string.c +++ b/pjlib/src/pj/string.c @@ -1,124 +1,124 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/string.c 9 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/string.c $ - * - * 9 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 8 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include - -#if PJ_FUNCTIONS_ARE_INLINED==0 -# include -#endif - - -static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - -PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str ) -{ - register char *p = str->ptr; - while (pj_isspace(*p)) - ++p; - str->slen -= (p - str->ptr); - str->ptr = p; - return str; -} - -PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str ) -{ - char *end = str->ptr + str->slen; - register char *p = end - 1; - while (p >= str->ptr && pj_isspace(*p)) - --p; - str->slen -= ((end - p) - 1); - return str; -} - -PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p) -{ - *p++ = hex[ (value & 0xF0) >> 4 ]; - *p++ = hex[ (value & 0x0F) ]; -} - -PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len) -{ - unsigned i; - char *p = str; - - PJ_CHECK_STACK(); - - for (i=0; i> 24, p+0 ); - pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 ); - pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 ); - pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 ); - p += 8; - } - for (i=i * 8; islen; ++i) { - value = value * 10 + (str->ptr[i] - '0'); - } - return value; -} - -PJ_DEF(int) pj_utoa(unsigned long val, char *buf) -{ - return pj_utoa_pad(val, buf, 0, 0); -} - -PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad) -{ - char *p; - int len; - - PJ_CHECK_STACK(); - - p = buf; - do { - unsigned long digval = (unsigned long) (val % 10); - val /= 10; - *p++ = (char) (digval + '0'); - } while (val > 0); - - len = p-buf; - while (len < min_dig) { - *p++ = (char)pad; - ++len; - } - *p-- = '\0'; - - do { - char temp = *p; - *p = *buf; - *buf = temp; - --p; - ++buf; - } while (buf < p); - - return len; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/string.c 9 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/string.c $ + * + * 9 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 8 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include + +#if PJ_FUNCTIONS_ARE_INLINED==0 +# include +#endif + + +static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str ) +{ + register char *p = str->ptr; + while (pj_isspace(*p)) + ++p; + str->slen -= (p - str->ptr); + str->ptr = p; + return str; +} + +PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str ) +{ + char *end = str->ptr + str->slen; + register char *p = end - 1; + while (p >= str->ptr && pj_isspace(*p)) + --p; + str->slen -= ((end - p) - 1); + return str; +} + +PJ_INLINE(void) pj_val_to_hex_digit(unsigned value, char *p) +{ + *p++ = hex[ (value & 0xF0) >> 4 ]; + *p++ = hex[ (value & 0x0F) ]; +} + +PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len) +{ + unsigned i; + char *p = str; + + PJ_CHECK_STACK(); + + for (i=0; i> 24, p+0 ); + pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 ); + pj_val_to_hex_digit( (val & 0x0000FF00) >> 8, p+4 ); + pj_val_to_hex_digit( (val & 0x000000FF) >> 0, p+6 ); + p += 8; + } + for (i=i * 8; islen; ++i) { + value = value * 10 + (str->ptr[i] - '0'); + } + return value; +} + +PJ_DEF(int) pj_utoa(unsigned long val, char *buf) +{ + return pj_utoa_pad(val, buf, 0, 0); +} + +PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad) +{ + char *p; + int len; + + PJ_CHECK_STACK(); + + p = buf; + do { + unsigned long digval = (unsigned long) (val % 10); + val /= 10; + *p++ = (char) (digval + '0'); + } while (val > 0); + + len = p-buf; + while (len < min_dig) { + *p++ = (char)pad; + ++len; + } + *p-- = '\0'; + + do { + char temp = *p; + *p = *buf; + *buf = temp; + --p; + ++buf; + } while (buf < p); + + return len; +} + diff --git a/pjlib/src/pj/stun.c b/pjlib/src/pj/stun.c index efd435609..670ceefc8 100644 --- a/pjlib/src/pj/stun.c +++ b/pjlib/src/pj/stun.c @@ -1,118 +1,118 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/stun.c 6 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/stun.c $ - * - * 6 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include - -#define THIS_FILE "stun" - -PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, - void **msg, pj_size_t *len, - pj_uint32_t id_hi, - pj_uint32_t id_lo) -{ - pj_stun_msg_hdr *hdr; - - PJ_CHECK_STACK(); - - PJ_LOG(5,(THIS_FILE, "pj_stun_create_bind_req")); - - hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr)); - if (!hdr) { - PJ_LOG(5,(THIS_FILE, "Error allocating memory!")); - return -1; - } - - hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST); - hdr->tsx[2] = pj_htonl(id_hi); - hdr->tsx[3] = pj_htonl(id_lo); - *msg = hdr; - *len = sizeof(pj_stun_msg_hdr); - - return 0; -} - -PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len, - pj_stun_msg *msg) -{ - pj_uint16_t msg_type, msg_len; - char *p_attr; - - PJ_CHECK_STACK(); - - PJ_LOG(5,(THIS_FILE, "pj_stun_parse_msg %p, len=%d", buf, len)); - - msg->hdr = (pj_stun_msg_hdr*)buf; - msg_type = pj_ntohs(msg->hdr->type); - - switch (msg_type) { - case PJ_STUN_BINDING_REQUEST: - case PJ_STUN_BINDING_RESPONSE: - case PJ_STUN_BINDING_ERROR_RESPONSE: - case PJ_STUN_SHARED_SECRET_REQUEST: - case PJ_STUN_SHARED_SECRET_RESPONSE: - case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE: - break; - default: - PJ_LOG(5,(THIS_FILE, "Error: unknown msg type %d", msg_type)); - return -1; - } - - msg_len = pj_ntohs(msg->hdr->length); - if (msg_len != len - sizeof(pj_stun_msg_hdr)) { - PJ_LOG(5,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)", - msg_len, len - sizeof(pj_stun_msg_hdr))); - return -1; - } - - msg->attr_count = 0; - p_attr = (char*)buf + sizeof(pj_stun_msg_hdr); - - while (msg_len > 0) { - pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count]; - pj_uint32_t len; - - *attr = (pj_stun_attr_hdr*)p_attr; - len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr); - - if (msg_len < len) { - PJ_LOG(5,(THIS_FILE, "Error: length mismatch in attr %d", - msg->attr_count)); - return -1; - } - - if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) { - PJ_LOG(5,(THIS_FILE, "Error: invalid attr type %d in attr %d", - pj_ntohs((*attr)->type), msg->attr_count)); - return -1; - } - - msg_len = (pj_uint16_t)(msg_len - len); - p_attr += len; - ++msg->attr_count; - } - - return 0; -} - -PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t) -{ - int i; - - PJ_CHECK_STACK(); - - for (i=0; iattr_count; ++i) { - pj_stun_attr_hdr *attr = msg->attr[i]; - if (pj_ntohs(attr->type) == t) - return attr; - } - - return 0; -} +/* $Header: /pjproject-0.3/pjlib/src/pj/stun.c 6 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/stun.c $ + * + * 6 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include + +#define THIS_FILE "stun" + +PJ_DEF(pj_status_t) pj_stun_create_bind_req( pj_pool_t *pool, + void **msg, pj_size_t *len, + pj_uint32_t id_hi, + pj_uint32_t id_lo) +{ + pj_stun_msg_hdr *hdr; + + PJ_CHECK_STACK(); + + PJ_LOG(5,(THIS_FILE, "pj_stun_create_bind_req")); + + hdr = pj_pool_calloc(pool, 1, sizeof(pj_stun_msg_hdr)); + if (!hdr) { + PJ_LOG(5,(THIS_FILE, "Error allocating memory!")); + return -1; + } + + hdr->type = pj_htons(PJ_STUN_BINDING_REQUEST); + hdr->tsx[2] = pj_htonl(id_hi); + hdr->tsx[3] = pj_htonl(id_lo); + *msg = hdr; + *len = sizeof(pj_stun_msg_hdr); + + return 0; +} + +PJ_DEF(pj_status_t) pj_stun_parse_msg( void *buf, pj_size_t len, + pj_stun_msg *msg) +{ + pj_uint16_t msg_type, msg_len; + char *p_attr; + + PJ_CHECK_STACK(); + + PJ_LOG(5,(THIS_FILE, "pj_stun_parse_msg %p, len=%d", buf, len)); + + msg->hdr = (pj_stun_msg_hdr*)buf; + msg_type = pj_ntohs(msg->hdr->type); + + switch (msg_type) { + case PJ_STUN_BINDING_REQUEST: + case PJ_STUN_BINDING_RESPONSE: + case PJ_STUN_BINDING_ERROR_RESPONSE: + case PJ_STUN_SHARED_SECRET_REQUEST: + case PJ_STUN_SHARED_SECRET_RESPONSE: + case PJ_STUN_SHARED_SECRET_ERROR_RESPONSE: + break; + default: + PJ_LOG(5,(THIS_FILE, "Error: unknown msg type %d", msg_type)); + return -1; + } + + msg_len = pj_ntohs(msg->hdr->length); + if (msg_len != len - sizeof(pj_stun_msg_hdr)) { + PJ_LOG(5,(THIS_FILE, "Error: invalid msg_len %d (expecting %d)", + msg_len, len - sizeof(pj_stun_msg_hdr))); + return -1; + } + + msg->attr_count = 0; + p_attr = (char*)buf + sizeof(pj_stun_msg_hdr); + + while (msg_len > 0) { + pj_stun_attr_hdr **attr = &msg->attr[msg->attr_count]; + pj_uint32_t len; + + *attr = (pj_stun_attr_hdr*)p_attr; + len = pj_ntohs((pj_uint16_t) ((*attr)->length)) + sizeof(pj_stun_attr_hdr); + + if (msg_len < len) { + PJ_LOG(5,(THIS_FILE, "Error: length mismatch in attr %d", + msg->attr_count)); + return -1; + } + + if (pj_ntohs((*attr)->type) > PJ_STUN_ATTR_REFLECTED_FORM) { + PJ_LOG(5,(THIS_FILE, "Error: invalid attr type %d in attr %d", + pj_ntohs((*attr)->type), msg->attr_count)); + return -1; + } + + msg_len = (pj_uint16_t)(msg_len - len); + p_attr += len; + ++msg->attr_count; + } + + return 0; +} + +PJ_DEF(void*) pj_stun_msg_find_attr( pj_stun_msg *msg, pj_stun_attr_type t) +{ + int i; + + PJ_CHECK_STACK(); + + for (i=0; iattr_count; ++i) { + pj_stun_attr_hdr *attr = msg->attr[i]; + if (pj_ntohs(attr->type) == t) + return attr; + } + + return 0; +} diff --git a/pjlib/src/pj/stun_client.c b/pjlib/src/pj/stun_client.c index c35a8541e..bc9b6f350 100644 --- a/pjlib/src/pj/stun_client.c +++ b/pjlib/src/pj/stun_client.c @@ -1,270 +1,270 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/stun_client.c 6 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/stun_client.c $ - * - * 6 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 5 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include - -enum { MAX_REQUEST = 3 }; -static int stun_timer[] = {1600, 1600, 1600 }; - -#define THIS_FILE "stunclient" -#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port) - - -PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf, - int sock_cnt, pj_sock_t sock[], - const pj_str_t *srv1, int port1, - const pj_str_t *srv2, int port2, - pj_sockaddr_in mapped_addr[]) -{ - pj_sockaddr_in srv_addr[2]; - int i, j, rc, send_cnt = 0; - pj_pool_t *pool; - struct { - struct { - pj_uint32_t mapped_addr; - pj_uint32_t mapped_port; - } srv[2]; - } *rec; - void *out_msg; - pj_size_t out_msg_len; - int wait_resp = 0; - int mapped_status = 0; - - PJ_CHECK_STACK(); - - /* Create pool. */ - pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL); - if (!pool) { - mapped_status = PJ_STUN_ERR_MEMORY; - return -1; - } - - /* Allocate client records */ - rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec)); - if (!rec) { - mapped_status = PJ_STUN_ERR_MEMORY; - goto on_error; - } - - /* Create the outgoing BIND REQUEST message template */ - rc = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 0, 0); - if (rc != 0) { - mapped_status = -1; - goto on_error; - } - - /* Resolve servers. */ - if (pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1) != 0) { - mapped_status = PJ_STUN_ERR_RESOLVE; - goto on_error; - } - if (pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2) != 0) { - mapped_status = PJ_STUN_ERR_RESOLVE; - goto on_error; - } - - /* Init mapped addresses to zero */ - pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in)); - - /* Main retransmission loop. */ - for (send_cnt=0; send_cnttsx[2] = pj_htonl(i); - msg_hdr->tsx[3] = pj_htonl(j); - - /* Send! */ - sent_len = out_msg_len; - rc = pj_sock_sendto(sock[i], out_msg, &sent_len, 0, - (pj_sockaddr_t*)&srv_addr[j], - sizeof(pj_sockaddr_in)); - if (sent_len != (int)out_msg_len) { - PJ_LOG(4,(THIS_FILE, - "Error sending STUN request to %s:%d", - LOG_ADDR(srv_addr[j]))); - mapped_status = PJ_STUN_ERR_TRANSPORT; - } else { - ++wait_resp; - } - } - } - - /* All requests sent. - * The loop below will wait for responses until all responses have - * been received (i.e. wait_resp==0) or timeout occurs, which then - * we'll go to the next retransmission iteration. - */ - - /* Calculate time of next retransmission. */ - pj_gettimeofday(&next_tx); - next_tx.sec += (stun_timer[send_cnt]/1000); - next_tx.msec += (stun_timer[send_cnt]%1000); - pj_time_val_normalize(&next_tx); - - for (pj_gettimeofday(&now), select_rc=1; - mapped_status==0 && select_rc==1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx); - pj_gettimeofday(&now)) - { - pj_time_val timeout; - - timeout = next_tx; - PJ_TIME_VAL_SUB(timeout, now); - - for (i=0; itsx[2]); - srv_idx = pj_ntohl(msg.hdr->tsx[3]); - - if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) { - PJ_LOG(4,(THIS_FILE, - "Invalid transaction ID from %s:%d", - LOG_ADDR(addr))); - mapped_status = PJ_STUN_ERR_INVALID_MSG; - continue; - } - - if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) { - PJ_LOG(4,(THIS_FILE, - "Non binding response %d from %s:%d", - pj_ntohs(msg.hdr->type), LOG_ADDR(addr))); - mapped_status = PJ_STUN_ERR_INVALID_MSG; - continue; - } - - if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) { - PJ_LOG(4,(THIS_FILE, - "Got STUN error attribute from %s:%d", - LOG_ADDR(addr))); - mapped_status = PJ_STUN_ERR_INVALID_MSG; - continue; - } - - attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR); - if (!attr) { - PJ_LOG(4,(THIS_FILE, - "No mapped address in response from %s:%d", - LOG_ADDR(addr))); - mapped_status = PJ_STUN_ERR_INVALID_MSG; - continue; - } - - rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr; - rec[sock_idx].srv[srv_idx].mapped_port = attr->port; - } - } - - /* The best scenario is if all requests have been replied. - * Then we don't need to go to the next retransmission iteration. - */ - if (wait_resp <= 0) - break; - } - - for (i=0; i +#include +#include +#include +#include +#include + +enum { MAX_REQUEST = 3 }; +static int stun_timer[] = {1600, 1600, 1600 }; + +#define THIS_FILE "stunclient" +#define LOG_ADDR(addr) pj_inet_ntoa(addr.sin_addr), pj_ntohs(addr.sin_port) + + +PJ_DECL(pj_status_t) pj_stun_get_mapped_addr( pj_pool_factory *pf, + int sock_cnt, pj_sock_t sock[], + const pj_str_t *srv1, int port1, + const pj_str_t *srv2, int port2, + pj_sockaddr_in mapped_addr[]) +{ + pj_sockaddr_in srv_addr[2]; + int i, j, rc, send_cnt = 0; + pj_pool_t *pool; + struct { + struct { + pj_uint32_t mapped_addr; + pj_uint32_t mapped_port; + } srv[2]; + } *rec; + void *out_msg; + pj_size_t out_msg_len; + int wait_resp = 0; + int mapped_status = 0; + + PJ_CHECK_STACK(); + + /* Create pool. */ + pool = pj_pool_create(pf, "stun%p", 1024, 1024, NULL); + if (!pool) { + mapped_status = PJ_STUN_ERR_MEMORY; + return -1; + } + + /* Allocate client records */ + rec = pj_pool_calloc(pool, sock_cnt, sizeof(*rec)); + if (!rec) { + mapped_status = PJ_STUN_ERR_MEMORY; + goto on_error; + } + + /* Create the outgoing BIND REQUEST message template */ + rc = pj_stun_create_bind_req( pool, &out_msg, &out_msg_len, 0, 0); + if (rc != 0) { + mapped_status = -1; + goto on_error; + } + + /* Resolve servers. */ + if (pj_sockaddr_in_init(&srv_addr[0], srv1, (pj_uint16_t)port1) != 0) { + mapped_status = PJ_STUN_ERR_RESOLVE; + goto on_error; + } + if (pj_sockaddr_in_init(&srv_addr[1], srv2, (pj_uint16_t)port2) != 0) { + mapped_status = PJ_STUN_ERR_RESOLVE; + goto on_error; + } + + /* Init mapped addresses to zero */ + pj_memset(mapped_addr, 0, sock_cnt * sizeof(pj_sockaddr_in)); + + /* Main retransmission loop. */ + for (send_cnt=0; send_cnttsx[2] = pj_htonl(i); + msg_hdr->tsx[3] = pj_htonl(j); + + /* Send! */ + sent_len = out_msg_len; + rc = pj_sock_sendto(sock[i], out_msg, &sent_len, 0, + (pj_sockaddr_t*)&srv_addr[j], + sizeof(pj_sockaddr_in)); + if (sent_len != (int)out_msg_len) { + PJ_LOG(4,(THIS_FILE, + "Error sending STUN request to %s:%d", + LOG_ADDR(srv_addr[j]))); + mapped_status = PJ_STUN_ERR_TRANSPORT; + } else { + ++wait_resp; + } + } + } + + /* All requests sent. + * The loop below will wait for responses until all responses have + * been received (i.e. wait_resp==0) or timeout occurs, which then + * we'll go to the next retransmission iteration. + */ + + /* Calculate time of next retransmission. */ + pj_gettimeofday(&next_tx); + next_tx.sec += (stun_timer[send_cnt]/1000); + next_tx.msec += (stun_timer[send_cnt]%1000); + pj_time_val_normalize(&next_tx); + + for (pj_gettimeofday(&now), select_rc=1; + mapped_status==0 && select_rc==1 && wait_resp>0 && PJ_TIME_VAL_LT(now, next_tx); + pj_gettimeofday(&now)) + { + pj_time_val timeout; + + timeout = next_tx; + PJ_TIME_VAL_SUB(timeout, now); + + for (i=0; itsx[2]); + srv_idx = pj_ntohl(msg.hdr->tsx[3]); + + if (sock_idx<0 || sock_idx>=sock_cnt || srv_idx<0 || srv_idx>=2) { + PJ_LOG(4,(THIS_FILE, + "Invalid transaction ID from %s:%d", + LOG_ADDR(addr))); + mapped_status = PJ_STUN_ERR_INVALID_MSG; + continue; + } + + if (pj_ntohs(msg.hdr->type) != PJ_STUN_BINDING_RESPONSE) { + PJ_LOG(4,(THIS_FILE, + "Non binding response %d from %s:%d", + pj_ntohs(msg.hdr->type), LOG_ADDR(addr))); + mapped_status = PJ_STUN_ERR_INVALID_MSG; + continue; + } + + if (pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_ERROR_CODE) != NULL) { + PJ_LOG(4,(THIS_FILE, + "Got STUN error attribute from %s:%d", + LOG_ADDR(addr))); + mapped_status = PJ_STUN_ERR_INVALID_MSG; + continue; + } + + attr = (void*)pj_stun_msg_find_attr(&msg, PJ_STUN_ATTR_MAPPED_ADDR); + if (!attr) { + PJ_LOG(4,(THIS_FILE, + "No mapped address in response from %s:%d", + LOG_ADDR(addr))); + mapped_status = PJ_STUN_ERR_INVALID_MSG; + continue; + } + + rec[sock_idx].srv[srv_idx].mapped_addr = attr->addr; + rec[sock_idx].srv[srv_idx].mapped_port = attr->port; + } + } + + /* The best scenario is if all requests have been replied. + * Then we don't need to go to the next retransmission iteration. + */ + if (wait_resp <= 0) + break; + } + + for (i=0; i - -/* - * addr_resolv.h - */ -PJ_EXPORT_SYMBOL(pj_gethostbyname) - -/* - * array.h - */ -PJ_EXPORT_SYMBOL(pj_array_insert) -PJ_EXPORT_SYMBOL(pj_array_erase) -PJ_EXPORT_SYMBOL(pj_array_find) - -/* - * config.h - */ -PJ_EXPORT_SYMBOL(pj_dump_config) - -/* - * errno.h - */ -PJ_EXPORT_SYMBOL(pj_get_os_error) -PJ_EXPORT_SYMBOL(pj_set_os_error) -PJ_EXPORT_SYMBOL(pj_get_netos_error) -PJ_EXPORT_SYMBOL(pj_set_netos_error) -PJ_EXPORT_SYMBOL(pj_strerror) - -/* - * except.h - */ -PJ_EXPORT_SYMBOL(pj_throw_exception_) -PJ_EXPORT_SYMBOL(pj_push_exception_handler_) -PJ_EXPORT_SYMBOL(pj_pop_exception_handler_) -PJ_EXPORT_SYMBOL(pj_setjmp) -PJ_EXPORT_SYMBOL(pj_longjmp) -PJ_EXPORT_SYMBOL(pj_exception_id_alloc) -PJ_EXPORT_SYMBOL(pj_exception_id_free) -PJ_EXPORT_SYMBOL(pj_exception_id_name) - - -/* - * fifobuf.h - */ -PJ_EXPORT_SYMBOL(pj_fifobuf_init) -PJ_EXPORT_SYMBOL(pj_fifobuf_max_size) -PJ_EXPORT_SYMBOL(pj_fifobuf_alloc) -PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc) -PJ_EXPORT_SYMBOL(pj_fifobuf_free) - -/* - * guid.h - */ -PJ_EXPORT_SYMBOL(pj_generate_unique_string) -PJ_EXPORT_SYMBOL(pj_create_unique_string) - -/* - * hash.h - */ -PJ_EXPORT_SYMBOL(pj_hash_calc) -PJ_EXPORT_SYMBOL(pj_hash_create) -PJ_EXPORT_SYMBOL(pj_hash_get) -PJ_EXPORT_SYMBOL(pj_hash_set) -PJ_EXPORT_SYMBOL(pj_hash_count) -PJ_EXPORT_SYMBOL(pj_hash_first) -PJ_EXPORT_SYMBOL(pj_hash_next) -PJ_EXPORT_SYMBOL(pj_hash_this) - -/* - * ioqueue.h - */ -PJ_EXPORT_SYMBOL(pj_ioqueue_create) -PJ_EXPORT_SYMBOL(pj_ioqueue_destroy) -PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock) -PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock) -PJ_EXPORT_SYMBOL(pj_ioqueue_unregister) -PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data) -PJ_EXPORT_SYMBOL(pj_ioqueue_poll) -PJ_EXPORT_SYMBOL(pj_ioqueue_read) -PJ_EXPORT_SYMBOL(pj_ioqueue_recv) -PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom) -PJ_EXPORT_SYMBOL(pj_ioqueue_write) -PJ_EXPORT_SYMBOL(pj_ioqueue_send) -PJ_EXPORT_SYMBOL(pj_ioqueue_sendto) -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 -PJ_EXPORT_SYMBOL(pj_ioqueue_accept) -PJ_EXPORT_SYMBOL(pj_ioqueue_connect) -#endif - -/* - * list.h - */ -PJ_EXPORT_SYMBOL(pj_list_insert_before) -PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before) -PJ_EXPORT_SYMBOL(pj_list_insert_after) -PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after) -PJ_EXPORT_SYMBOL(pj_list_merge_first) -PJ_EXPORT_SYMBOL(pj_list_merge_last) -PJ_EXPORT_SYMBOL(pj_list_erase) -PJ_EXPORT_SYMBOL(pj_list_find_node) -PJ_EXPORT_SYMBOL(pj_list_search) - - -/* - * log.h - */ -PJ_EXPORT_SYMBOL(pj_log_write) -#if PJ_LOG_MAX_LEVEL >= 1 -PJ_EXPORT_SYMBOL(pj_log_set_log_func) -PJ_EXPORT_SYMBOL(pj_log_get_log_func) -PJ_EXPORT_SYMBOL(pj_log_set_level) -PJ_EXPORT_SYMBOL(pj_log_get_level) -PJ_EXPORT_SYMBOL(pj_log_set_decor) -PJ_EXPORT_SYMBOL(pj_log_get_decor) -PJ_EXPORT_SYMBOL(pj_log_1) -#endif -#if PJ_LOG_MAX_LEVEL >= 2 -PJ_EXPORT_SYMBOL(pj_log_2) -#endif -#if PJ_LOG_MAX_LEVEL >= 3 -PJ_EXPORT_SYMBOL(pj_log_3) -#endif -#if PJ_LOG_MAX_LEVEL >= 4 -PJ_EXPORT_SYMBOL(pj_log_4) -#endif -#if PJ_LOG_MAX_LEVEL >= 5 -PJ_EXPORT_SYMBOL(pj_log_5) -#endif -#if PJ_LOG_MAX_LEVEL >= 6 -PJ_EXPORT_SYMBOL(pj_log_6) -#endif - -/* - * md5.h - */ -PJ_EXPORT_SYMBOL(md5_init) -PJ_EXPORT_SYMBOL(md5_append) -PJ_EXPORT_SYMBOL(md5_finish) - - -/* - * os.h - */ -PJ_EXPORT_SYMBOL(pj_init) -PJ_EXPORT_SYMBOL(pj_getpid) -PJ_EXPORT_SYMBOL(pj_thread_register) -PJ_EXPORT_SYMBOL(pj_thread_create) -PJ_EXPORT_SYMBOL(pj_thread_get_name) -PJ_EXPORT_SYMBOL(pj_thread_resume) -PJ_EXPORT_SYMBOL(pj_thread_this) -PJ_EXPORT_SYMBOL(pj_thread_join) -PJ_EXPORT_SYMBOL(pj_thread_destroy) -PJ_EXPORT_SYMBOL(pj_thread_sleep) -#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 -PJ_EXPORT_SYMBOL(pj_thread_check_stack) -PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage) -PJ_EXPORT_SYMBOL(pj_thread_get_stack_info) -#endif -PJ_EXPORT_SYMBOL(pj_atomic_create) -PJ_EXPORT_SYMBOL(pj_atomic_destroy) -PJ_EXPORT_SYMBOL(pj_atomic_set) -PJ_EXPORT_SYMBOL(pj_atomic_get) -PJ_EXPORT_SYMBOL(pj_atomic_inc) -PJ_EXPORT_SYMBOL(pj_atomic_dec) -PJ_EXPORT_SYMBOL(pj_thread_local_alloc) -PJ_EXPORT_SYMBOL(pj_thread_local_free) -PJ_EXPORT_SYMBOL(pj_thread_local_set) -PJ_EXPORT_SYMBOL(pj_thread_local_get) -PJ_EXPORT_SYMBOL(pj_enter_critical_section) -PJ_EXPORT_SYMBOL(pj_leave_critical_section) -PJ_EXPORT_SYMBOL(pj_mutex_create) -PJ_EXPORT_SYMBOL(pj_mutex_lock) -PJ_EXPORT_SYMBOL(pj_mutex_unlock) -PJ_EXPORT_SYMBOL(pj_mutex_trylock) -PJ_EXPORT_SYMBOL(pj_mutex_destroy) -#if defined(PJ_DEBUG) && PJ_DEBUG != 0 -PJ_EXPORT_SYMBOL(pj_mutex_is_locked) -#endif -#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 -PJ_EXPORT_SYMBOL(pj_sem_create) -PJ_EXPORT_SYMBOL(pj_sem_wait) -PJ_EXPORT_SYMBOL(pj_sem_trywait) -PJ_EXPORT_SYMBOL(pj_sem_post) -PJ_EXPORT_SYMBOL(pj_sem_destroy) -#endif -PJ_EXPORT_SYMBOL(pj_gettimeofday) -PJ_EXPORT_SYMBOL(pj_time_decode) -#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 -PJ_EXPORT_SYMBOL(pj_get_timestamp) -PJ_EXPORT_SYMBOL(pj_get_timestamp_freq) -PJ_EXPORT_SYMBOL(pj_elapsed_time) -PJ_EXPORT_SYMBOL(pj_elapsed_usec) -PJ_EXPORT_SYMBOL(pj_elapsed_nanosec) -PJ_EXPORT_SYMBOL(pj_elapsed_cycle) -#endif - - -/* - * pool.h - */ -PJ_EXPORT_SYMBOL(pj_pool_create) -PJ_EXPORT_SYMBOL(pj_pool_release) -PJ_EXPORT_SYMBOL(pj_pool_getobjname) -PJ_EXPORT_SYMBOL(pj_pool_reset) -PJ_EXPORT_SYMBOL(pj_pool_get_capacity) -PJ_EXPORT_SYMBOL(pj_pool_get_used_size) -PJ_EXPORT_SYMBOL(pj_pool_alloc) -PJ_EXPORT_SYMBOL(pj_pool_calloc) -PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy) -PJ_EXPORT_SYMBOL(pj_pool_create_int) -PJ_EXPORT_SYMBOL(pj_pool_init_int) -PJ_EXPORT_SYMBOL(pj_pool_destroy_int) -PJ_EXPORT_SYMBOL(pj_caching_pool_init) -PJ_EXPORT_SYMBOL(pj_caching_pool_destroy) - -/* - * rand.h - */ -PJ_EXPORT_SYMBOL(pj_rand) -PJ_EXPORT_SYMBOL(pj_srand) - -/* - * rbtree.h - */ -PJ_EXPORT_SYMBOL(pj_rbtree_init) -PJ_EXPORT_SYMBOL(pj_rbtree_first) -PJ_EXPORT_SYMBOL(pj_rbtree_last) -PJ_EXPORT_SYMBOL(pj_rbtree_next) -PJ_EXPORT_SYMBOL(pj_rbtree_prev) -PJ_EXPORT_SYMBOL(pj_rbtree_insert) -PJ_EXPORT_SYMBOL(pj_rbtree_find) -PJ_EXPORT_SYMBOL(pj_rbtree_erase) -PJ_EXPORT_SYMBOL(pj_rbtree_max_height) -PJ_EXPORT_SYMBOL(pj_rbtree_min_height) - -/* - * scanner.h - */ -PJ_EXPORT_SYMBOL(pj_cs_init) -PJ_EXPORT_SYMBOL(pj_cs_set) -PJ_EXPORT_SYMBOL(pj_cs_add_range) -PJ_EXPORT_SYMBOL(pj_cs_add_alpha) -PJ_EXPORT_SYMBOL(pj_cs_add_num) -PJ_EXPORT_SYMBOL(pj_cs_add_str) -PJ_EXPORT_SYMBOL(pj_cs_del_range) -PJ_EXPORT_SYMBOL(pj_cs_del_str) -PJ_EXPORT_SYMBOL(pj_cs_invert) -PJ_EXPORT_SYMBOL(pj_scan_init) -PJ_EXPORT_SYMBOL(pj_scan_fini) -PJ_EXPORT_SYMBOL(pj_scan_peek) -PJ_EXPORT_SYMBOL(pj_scan_peek_n) -PJ_EXPORT_SYMBOL(pj_scan_peek_until) -PJ_EXPORT_SYMBOL(pj_scan_get) -PJ_EXPORT_SYMBOL(pj_scan_get_quote) -PJ_EXPORT_SYMBOL(pj_scan_get_n) -PJ_EXPORT_SYMBOL(pj_scan_get_char) -PJ_EXPORT_SYMBOL(pj_scan_get_newline) -PJ_EXPORT_SYMBOL(pj_scan_get_until) -PJ_EXPORT_SYMBOL(pj_scan_get_until_ch) -PJ_EXPORT_SYMBOL(pj_scan_get_until_chr) -PJ_EXPORT_SYMBOL(pj_scan_advance_n) -PJ_EXPORT_SYMBOL(pj_scan_strcmp) -PJ_EXPORT_SYMBOL(pj_scan_stricmp) -PJ_EXPORT_SYMBOL(pj_scan_skip_whitespace) -PJ_EXPORT_SYMBOL(pj_scan_save_state) -PJ_EXPORT_SYMBOL(pj_scan_restore_state) - -/* - * sock.h - */ -PJ_EXPORT_SYMBOL(PJ_AF_UNIX) -PJ_EXPORT_SYMBOL(PJ_AF_INET) -PJ_EXPORT_SYMBOL(PJ_AF_INET6) -PJ_EXPORT_SYMBOL(PJ_AF_PACKET) -PJ_EXPORT_SYMBOL(PJ_AF_IRDA) -PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM) -PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM) -PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) -PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) -PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) -PJ_EXPORT_SYMBOL(PJ_SOL_IP) -PJ_EXPORT_SYMBOL(PJ_SOL_TCP) -PJ_EXPORT_SYMBOL(PJ_SOL_UDP) -PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) -PJ_EXPORT_SYMBOL(pj_ntohs) -PJ_EXPORT_SYMBOL(pj_htons) -PJ_EXPORT_SYMBOL(pj_ntohl) -PJ_EXPORT_SYMBOL(pj_htonl) -PJ_EXPORT_SYMBOL(pj_inet_ntoa) -PJ_EXPORT_SYMBOL(pj_inet_aton) -PJ_EXPORT_SYMBOL(pj_inet_addr) -PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr) -PJ_EXPORT_SYMBOL(pj_sockaddr_in_init) -PJ_EXPORT_SYMBOL(pj_gethostname) -PJ_EXPORT_SYMBOL(pj_gethostaddr) -PJ_EXPORT_SYMBOL(pj_sock_socket) -PJ_EXPORT_SYMBOL(pj_sock_close) -PJ_EXPORT_SYMBOL(pj_sock_bind) -PJ_EXPORT_SYMBOL(pj_sock_bind_in) -#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 -PJ_EXPORT_SYMBOL(pj_sock_listen) -PJ_EXPORT_SYMBOL(pj_sock_accept) -PJ_EXPORT_SYMBOL(pj_sock_shutdown) -#endif -PJ_EXPORT_SYMBOL(pj_sock_connect) -PJ_EXPORT_SYMBOL(pj_sock_getpeername) -PJ_EXPORT_SYMBOL(pj_sock_getsockname) -PJ_EXPORT_SYMBOL(pj_sock_getsockopt) -PJ_EXPORT_SYMBOL(pj_sock_setsockopt) -PJ_EXPORT_SYMBOL(pj_sock_recv) -PJ_EXPORT_SYMBOL(pj_sock_recvfrom) -PJ_EXPORT_SYMBOL(pj_sock_send) -PJ_EXPORT_SYMBOL(pj_sock_sendto) - -/* - * sock_select.h - */ -PJ_EXPORT_SYMBOL(PJ_FD_ZERO) -PJ_EXPORT_SYMBOL(PJ_FD_SET) -PJ_EXPORT_SYMBOL(PJ_FD_CLR) -PJ_EXPORT_SYMBOL(PJ_FD_ISSET) -PJ_EXPORT_SYMBOL(pj_sock_select) - -/* - * string.h - */ -PJ_EXPORT_SYMBOL(pj_str) -PJ_EXPORT_SYMBOL(pj_strassign) -PJ_EXPORT_SYMBOL(pj_strcpy) -PJ_EXPORT_SYMBOL(pj_strcpy2) -PJ_EXPORT_SYMBOL(pj_strdup) -PJ_EXPORT_SYMBOL(pj_strdup_with_null) -PJ_EXPORT_SYMBOL(pj_strdup2) -PJ_EXPORT_SYMBOL(pj_strdup3) -PJ_EXPORT_SYMBOL(pj_strcmp) -PJ_EXPORT_SYMBOL(pj_strcmp2) -PJ_EXPORT_SYMBOL(pj_strncmp) -PJ_EXPORT_SYMBOL(pj_strncmp2) -PJ_EXPORT_SYMBOL(pj_stricmp) -PJ_EXPORT_SYMBOL(pj_stricmp2) -PJ_EXPORT_SYMBOL(pj_strnicmp) -PJ_EXPORT_SYMBOL(pj_strnicmp2) -PJ_EXPORT_SYMBOL(pj_strcat) -PJ_EXPORT_SYMBOL(pj_strltrim) -PJ_EXPORT_SYMBOL(pj_strrtrim) -PJ_EXPORT_SYMBOL(pj_strtrim) -PJ_EXPORT_SYMBOL(pj_create_random_string) -PJ_EXPORT_SYMBOL(pj_strtoul) -PJ_EXPORT_SYMBOL(pj_utoa) -PJ_EXPORT_SYMBOL(pj_utoa_pad) - -/* - * stun.h - */ -PJ_EXPORT_SYMBOL(pj_stun_create_bind_req) -PJ_EXPORT_SYMBOL(pj_stun_parse_msg) -PJ_EXPORT_SYMBOL(pj_stun_msg_find_attr) -PJ_EXPORT_SYMBOL(pj_stun_get_mapped_addr) -PJ_EXPORT_SYMBOL(pj_stun_get_err_msg) - -/* - * timer.h - */ -PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size) -PJ_EXPORT_SYMBOL(pj_timer_heap_create) -PJ_EXPORT_SYMBOL(pj_timer_entry_init) -PJ_EXPORT_SYMBOL(pj_timer_heap_schedule) -PJ_EXPORT_SYMBOL(pj_timer_heap_cancel) -PJ_EXPORT_SYMBOL(pj_timer_heap_count) -PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time) -PJ_EXPORT_SYMBOL(pj_timer_heap_poll) - -/* - * types.h - */ -PJ_EXPORT_SYMBOL(pj_time_val_normalize) - -/* - * xml.h - */ -PJ_EXPORT_SYMBOL(pj_xml_parse) -PJ_EXPORT_SYMBOL(pj_xml_print) -PJ_EXPORT_SYMBOL(pj_xml_add_node) -PJ_EXPORT_SYMBOL(pj_xml_add_attr) -PJ_EXPORT_SYMBOL(pj_xml_find_node) -PJ_EXPORT_SYMBOL(pj_xml_find_next_node) -PJ_EXPORT_SYMBOL(pj_xml_find_attr) -PJ_EXPORT_SYMBOL(pj_xml_find) - +/* $Header: /pjproject-0.3/pjlib/src/pj/symbols.c 3 10/29/05 11:51a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pj/symbols.c $ + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/05/05 4:43p Bennylp + * Created. + * + */ +#include + +/* + * addr_resolv.h + */ +PJ_EXPORT_SYMBOL(pj_gethostbyname) + +/* + * array.h + */ +PJ_EXPORT_SYMBOL(pj_array_insert) +PJ_EXPORT_SYMBOL(pj_array_erase) +PJ_EXPORT_SYMBOL(pj_array_find) + +/* + * config.h + */ +PJ_EXPORT_SYMBOL(pj_dump_config) + +/* + * errno.h + */ +PJ_EXPORT_SYMBOL(pj_get_os_error) +PJ_EXPORT_SYMBOL(pj_set_os_error) +PJ_EXPORT_SYMBOL(pj_get_netos_error) +PJ_EXPORT_SYMBOL(pj_set_netos_error) +PJ_EXPORT_SYMBOL(pj_strerror) + +/* + * except.h + */ +PJ_EXPORT_SYMBOL(pj_throw_exception_) +PJ_EXPORT_SYMBOL(pj_push_exception_handler_) +PJ_EXPORT_SYMBOL(pj_pop_exception_handler_) +PJ_EXPORT_SYMBOL(pj_setjmp) +PJ_EXPORT_SYMBOL(pj_longjmp) +PJ_EXPORT_SYMBOL(pj_exception_id_alloc) +PJ_EXPORT_SYMBOL(pj_exception_id_free) +PJ_EXPORT_SYMBOL(pj_exception_id_name) + + +/* + * fifobuf.h + */ +PJ_EXPORT_SYMBOL(pj_fifobuf_init) +PJ_EXPORT_SYMBOL(pj_fifobuf_max_size) +PJ_EXPORT_SYMBOL(pj_fifobuf_alloc) +PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc) +PJ_EXPORT_SYMBOL(pj_fifobuf_free) + +/* + * guid.h + */ +PJ_EXPORT_SYMBOL(pj_generate_unique_string) +PJ_EXPORT_SYMBOL(pj_create_unique_string) + +/* + * hash.h + */ +PJ_EXPORT_SYMBOL(pj_hash_calc) +PJ_EXPORT_SYMBOL(pj_hash_create) +PJ_EXPORT_SYMBOL(pj_hash_get) +PJ_EXPORT_SYMBOL(pj_hash_set) +PJ_EXPORT_SYMBOL(pj_hash_count) +PJ_EXPORT_SYMBOL(pj_hash_first) +PJ_EXPORT_SYMBOL(pj_hash_next) +PJ_EXPORT_SYMBOL(pj_hash_this) + +/* + * ioqueue.h + */ +PJ_EXPORT_SYMBOL(pj_ioqueue_create) +PJ_EXPORT_SYMBOL(pj_ioqueue_destroy) +PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock) +PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock) +PJ_EXPORT_SYMBOL(pj_ioqueue_unregister) +PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data) +PJ_EXPORT_SYMBOL(pj_ioqueue_poll) +PJ_EXPORT_SYMBOL(pj_ioqueue_read) +PJ_EXPORT_SYMBOL(pj_ioqueue_recv) +PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom) +PJ_EXPORT_SYMBOL(pj_ioqueue_write) +PJ_EXPORT_SYMBOL(pj_ioqueue_send) +PJ_EXPORT_SYMBOL(pj_ioqueue_sendto) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +PJ_EXPORT_SYMBOL(pj_ioqueue_accept) +PJ_EXPORT_SYMBOL(pj_ioqueue_connect) +#endif + +/* + * list.h + */ +PJ_EXPORT_SYMBOL(pj_list_insert_before) +PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before) +PJ_EXPORT_SYMBOL(pj_list_insert_after) +PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after) +PJ_EXPORT_SYMBOL(pj_list_merge_first) +PJ_EXPORT_SYMBOL(pj_list_merge_last) +PJ_EXPORT_SYMBOL(pj_list_erase) +PJ_EXPORT_SYMBOL(pj_list_find_node) +PJ_EXPORT_SYMBOL(pj_list_search) + + +/* + * log.h + */ +PJ_EXPORT_SYMBOL(pj_log_write) +#if PJ_LOG_MAX_LEVEL >= 1 +PJ_EXPORT_SYMBOL(pj_log_set_log_func) +PJ_EXPORT_SYMBOL(pj_log_get_log_func) +PJ_EXPORT_SYMBOL(pj_log_set_level) +PJ_EXPORT_SYMBOL(pj_log_get_level) +PJ_EXPORT_SYMBOL(pj_log_set_decor) +PJ_EXPORT_SYMBOL(pj_log_get_decor) +PJ_EXPORT_SYMBOL(pj_log_1) +#endif +#if PJ_LOG_MAX_LEVEL >= 2 +PJ_EXPORT_SYMBOL(pj_log_2) +#endif +#if PJ_LOG_MAX_LEVEL >= 3 +PJ_EXPORT_SYMBOL(pj_log_3) +#endif +#if PJ_LOG_MAX_LEVEL >= 4 +PJ_EXPORT_SYMBOL(pj_log_4) +#endif +#if PJ_LOG_MAX_LEVEL >= 5 +PJ_EXPORT_SYMBOL(pj_log_5) +#endif +#if PJ_LOG_MAX_LEVEL >= 6 +PJ_EXPORT_SYMBOL(pj_log_6) +#endif + +/* + * md5.h + */ +PJ_EXPORT_SYMBOL(md5_init) +PJ_EXPORT_SYMBOL(md5_append) +PJ_EXPORT_SYMBOL(md5_finish) + + +/* + * os.h + */ +PJ_EXPORT_SYMBOL(pj_init) +PJ_EXPORT_SYMBOL(pj_getpid) +PJ_EXPORT_SYMBOL(pj_thread_register) +PJ_EXPORT_SYMBOL(pj_thread_create) +PJ_EXPORT_SYMBOL(pj_thread_get_name) +PJ_EXPORT_SYMBOL(pj_thread_resume) +PJ_EXPORT_SYMBOL(pj_thread_this) +PJ_EXPORT_SYMBOL(pj_thread_join) +PJ_EXPORT_SYMBOL(pj_thread_destroy) +PJ_EXPORT_SYMBOL(pj_thread_sleep) +#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0 +PJ_EXPORT_SYMBOL(pj_thread_check_stack) +PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage) +PJ_EXPORT_SYMBOL(pj_thread_get_stack_info) +#endif +PJ_EXPORT_SYMBOL(pj_atomic_create) +PJ_EXPORT_SYMBOL(pj_atomic_destroy) +PJ_EXPORT_SYMBOL(pj_atomic_set) +PJ_EXPORT_SYMBOL(pj_atomic_get) +PJ_EXPORT_SYMBOL(pj_atomic_inc) +PJ_EXPORT_SYMBOL(pj_atomic_dec) +PJ_EXPORT_SYMBOL(pj_thread_local_alloc) +PJ_EXPORT_SYMBOL(pj_thread_local_free) +PJ_EXPORT_SYMBOL(pj_thread_local_set) +PJ_EXPORT_SYMBOL(pj_thread_local_get) +PJ_EXPORT_SYMBOL(pj_enter_critical_section) +PJ_EXPORT_SYMBOL(pj_leave_critical_section) +PJ_EXPORT_SYMBOL(pj_mutex_create) +PJ_EXPORT_SYMBOL(pj_mutex_lock) +PJ_EXPORT_SYMBOL(pj_mutex_unlock) +PJ_EXPORT_SYMBOL(pj_mutex_trylock) +PJ_EXPORT_SYMBOL(pj_mutex_destroy) +#if defined(PJ_DEBUG) && PJ_DEBUG != 0 +PJ_EXPORT_SYMBOL(pj_mutex_is_locked) +#endif +#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0 +PJ_EXPORT_SYMBOL(pj_sem_create) +PJ_EXPORT_SYMBOL(pj_sem_wait) +PJ_EXPORT_SYMBOL(pj_sem_trywait) +PJ_EXPORT_SYMBOL(pj_sem_post) +PJ_EXPORT_SYMBOL(pj_sem_destroy) +#endif +PJ_EXPORT_SYMBOL(pj_gettimeofday) +PJ_EXPORT_SYMBOL(pj_time_decode) +#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0 +PJ_EXPORT_SYMBOL(pj_get_timestamp) +PJ_EXPORT_SYMBOL(pj_get_timestamp_freq) +PJ_EXPORT_SYMBOL(pj_elapsed_time) +PJ_EXPORT_SYMBOL(pj_elapsed_usec) +PJ_EXPORT_SYMBOL(pj_elapsed_nanosec) +PJ_EXPORT_SYMBOL(pj_elapsed_cycle) +#endif + + +/* + * pool.h + */ +PJ_EXPORT_SYMBOL(pj_pool_create) +PJ_EXPORT_SYMBOL(pj_pool_release) +PJ_EXPORT_SYMBOL(pj_pool_getobjname) +PJ_EXPORT_SYMBOL(pj_pool_reset) +PJ_EXPORT_SYMBOL(pj_pool_get_capacity) +PJ_EXPORT_SYMBOL(pj_pool_get_used_size) +PJ_EXPORT_SYMBOL(pj_pool_alloc) +PJ_EXPORT_SYMBOL(pj_pool_calloc) +PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy) +PJ_EXPORT_SYMBOL(pj_pool_create_int) +PJ_EXPORT_SYMBOL(pj_pool_init_int) +PJ_EXPORT_SYMBOL(pj_pool_destroy_int) +PJ_EXPORT_SYMBOL(pj_caching_pool_init) +PJ_EXPORT_SYMBOL(pj_caching_pool_destroy) + +/* + * rand.h + */ +PJ_EXPORT_SYMBOL(pj_rand) +PJ_EXPORT_SYMBOL(pj_srand) + +/* + * rbtree.h + */ +PJ_EXPORT_SYMBOL(pj_rbtree_init) +PJ_EXPORT_SYMBOL(pj_rbtree_first) +PJ_EXPORT_SYMBOL(pj_rbtree_last) +PJ_EXPORT_SYMBOL(pj_rbtree_next) +PJ_EXPORT_SYMBOL(pj_rbtree_prev) +PJ_EXPORT_SYMBOL(pj_rbtree_insert) +PJ_EXPORT_SYMBOL(pj_rbtree_find) +PJ_EXPORT_SYMBOL(pj_rbtree_erase) +PJ_EXPORT_SYMBOL(pj_rbtree_max_height) +PJ_EXPORT_SYMBOL(pj_rbtree_min_height) + +/* + * scanner.h + */ +PJ_EXPORT_SYMBOL(pj_cs_init) +PJ_EXPORT_SYMBOL(pj_cs_set) +PJ_EXPORT_SYMBOL(pj_cs_add_range) +PJ_EXPORT_SYMBOL(pj_cs_add_alpha) +PJ_EXPORT_SYMBOL(pj_cs_add_num) +PJ_EXPORT_SYMBOL(pj_cs_add_str) +PJ_EXPORT_SYMBOL(pj_cs_del_range) +PJ_EXPORT_SYMBOL(pj_cs_del_str) +PJ_EXPORT_SYMBOL(pj_cs_invert) +PJ_EXPORT_SYMBOL(pj_scan_init) +PJ_EXPORT_SYMBOL(pj_scan_fini) +PJ_EXPORT_SYMBOL(pj_scan_peek) +PJ_EXPORT_SYMBOL(pj_scan_peek_n) +PJ_EXPORT_SYMBOL(pj_scan_peek_until) +PJ_EXPORT_SYMBOL(pj_scan_get) +PJ_EXPORT_SYMBOL(pj_scan_get_quote) +PJ_EXPORT_SYMBOL(pj_scan_get_n) +PJ_EXPORT_SYMBOL(pj_scan_get_char) +PJ_EXPORT_SYMBOL(pj_scan_get_newline) +PJ_EXPORT_SYMBOL(pj_scan_get_until) +PJ_EXPORT_SYMBOL(pj_scan_get_until_ch) +PJ_EXPORT_SYMBOL(pj_scan_get_until_chr) +PJ_EXPORT_SYMBOL(pj_scan_advance_n) +PJ_EXPORT_SYMBOL(pj_scan_strcmp) +PJ_EXPORT_SYMBOL(pj_scan_stricmp) +PJ_EXPORT_SYMBOL(pj_scan_skip_whitespace) +PJ_EXPORT_SYMBOL(pj_scan_save_state) +PJ_EXPORT_SYMBOL(pj_scan_restore_state) + +/* + * sock.h + */ +PJ_EXPORT_SYMBOL(PJ_AF_UNIX) +PJ_EXPORT_SYMBOL(PJ_AF_INET) +PJ_EXPORT_SYMBOL(PJ_AF_INET6) +PJ_EXPORT_SYMBOL(PJ_AF_PACKET) +PJ_EXPORT_SYMBOL(PJ_AF_IRDA) +PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM) +PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM) +PJ_EXPORT_SYMBOL(PJ_SOCK_RAW) +PJ_EXPORT_SYMBOL(PJ_SOCK_RDM) +PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET) +PJ_EXPORT_SYMBOL(PJ_SOL_IP) +PJ_EXPORT_SYMBOL(PJ_SOL_TCP) +PJ_EXPORT_SYMBOL(PJ_SOL_UDP) +PJ_EXPORT_SYMBOL(PJ_SOL_IPV6) +PJ_EXPORT_SYMBOL(pj_ntohs) +PJ_EXPORT_SYMBOL(pj_htons) +PJ_EXPORT_SYMBOL(pj_ntohl) +PJ_EXPORT_SYMBOL(pj_htonl) +PJ_EXPORT_SYMBOL(pj_inet_ntoa) +PJ_EXPORT_SYMBOL(pj_inet_aton) +PJ_EXPORT_SYMBOL(pj_inet_addr) +PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr) +PJ_EXPORT_SYMBOL(pj_sockaddr_in_init) +PJ_EXPORT_SYMBOL(pj_gethostname) +PJ_EXPORT_SYMBOL(pj_gethostaddr) +PJ_EXPORT_SYMBOL(pj_sock_socket) +PJ_EXPORT_SYMBOL(pj_sock_close) +PJ_EXPORT_SYMBOL(pj_sock_bind) +PJ_EXPORT_SYMBOL(pj_sock_bind_in) +#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0 +PJ_EXPORT_SYMBOL(pj_sock_listen) +PJ_EXPORT_SYMBOL(pj_sock_accept) +PJ_EXPORT_SYMBOL(pj_sock_shutdown) +#endif +PJ_EXPORT_SYMBOL(pj_sock_connect) +PJ_EXPORT_SYMBOL(pj_sock_getpeername) +PJ_EXPORT_SYMBOL(pj_sock_getsockname) +PJ_EXPORT_SYMBOL(pj_sock_getsockopt) +PJ_EXPORT_SYMBOL(pj_sock_setsockopt) +PJ_EXPORT_SYMBOL(pj_sock_recv) +PJ_EXPORT_SYMBOL(pj_sock_recvfrom) +PJ_EXPORT_SYMBOL(pj_sock_send) +PJ_EXPORT_SYMBOL(pj_sock_sendto) + +/* + * sock_select.h + */ +PJ_EXPORT_SYMBOL(PJ_FD_ZERO) +PJ_EXPORT_SYMBOL(PJ_FD_SET) +PJ_EXPORT_SYMBOL(PJ_FD_CLR) +PJ_EXPORT_SYMBOL(PJ_FD_ISSET) +PJ_EXPORT_SYMBOL(pj_sock_select) + +/* + * string.h + */ +PJ_EXPORT_SYMBOL(pj_str) +PJ_EXPORT_SYMBOL(pj_strassign) +PJ_EXPORT_SYMBOL(pj_strcpy) +PJ_EXPORT_SYMBOL(pj_strcpy2) +PJ_EXPORT_SYMBOL(pj_strdup) +PJ_EXPORT_SYMBOL(pj_strdup_with_null) +PJ_EXPORT_SYMBOL(pj_strdup2) +PJ_EXPORT_SYMBOL(pj_strdup3) +PJ_EXPORT_SYMBOL(pj_strcmp) +PJ_EXPORT_SYMBOL(pj_strcmp2) +PJ_EXPORT_SYMBOL(pj_strncmp) +PJ_EXPORT_SYMBOL(pj_strncmp2) +PJ_EXPORT_SYMBOL(pj_stricmp) +PJ_EXPORT_SYMBOL(pj_stricmp2) +PJ_EXPORT_SYMBOL(pj_strnicmp) +PJ_EXPORT_SYMBOL(pj_strnicmp2) +PJ_EXPORT_SYMBOL(pj_strcat) +PJ_EXPORT_SYMBOL(pj_strltrim) +PJ_EXPORT_SYMBOL(pj_strrtrim) +PJ_EXPORT_SYMBOL(pj_strtrim) +PJ_EXPORT_SYMBOL(pj_create_random_string) +PJ_EXPORT_SYMBOL(pj_strtoul) +PJ_EXPORT_SYMBOL(pj_utoa) +PJ_EXPORT_SYMBOL(pj_utoa_pad) + +/* + * stun.h + */ +PJ_EXPORT_SYMBOL(pj_stun_create_bind_req) +PJ_EXPORT_SYMBOL(pj_stun_parse_msg) +PJ_EXPORT_SYMBOL(pj_stun_msg_find_attr) +PJ_EXPORT_SYMBOL(pj_stun_get_mapped_addr) +PJ_EXPORT_SYMBOL(pj_stun_get_err_msg) + +/* + * timer.h + */ +PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size) +PJ_EXPORT_SYMBOL(pj_timer_heap_create) +PJ_EXPORT_SYMBOL(pj_timer_entry_init) +PJ_EXPORT_SYMBOL(pj_timer_heap_schedule) +PJ_EXPORT_SYMBOL(pj_timer_heap_cancel) +PJ_EXPORT_SYMBOL(pj_timer_heap_count) +PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time) +PJ_EXPORT_SYMBOL(pj_timer_heap_poll) + +/* + * types.h + */ +PJ_EXPORT_SYMBOL(pj_time_val_normalize) + +/* + * xml.h + */ +PJ_EXPORT_SYMBOL(pj_xml_parse) +PJ_EXPORT_SYMBOL(pj_xml_print) +PJ_EXPORT_SYMBOL(pj_xml_add_node) +PJ_EXPORT_SYMBOL(pj_xml_add_attr) +PJ_EXPORT_SYMBOL(pj_xml_find_node) +PJ_EXPORT_SYMBOL(pj_xml_find_next_node) +PJ_EXPORT_SYMBOL(pj_xml_find_attr) +PJ_EXPORT_SYMBOL(pj_xml_find) + diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c index 5cd09ea44..2bf9842a2 100644 --- a/pjlib/src/pj/timer.c +++ b/pjlib/src/pj/timer.c @@ -1,504 +1,504 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/timer.c 8 10/14/05 12:26a Bennylp $ */ -/* (C)1993-2003 Douglas C. Schmidt - * - * This file is originaly from ACE library by Doug Schmidt - * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research - * group at Washington University, University of California, Irvine, and Vanderbilt - * University Copyright (c) 1993-2003, all rights reserved. - */ -/* $Log: /pjproject-0.3/pjlib/src/pj/timer.c $ - * - * 8 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 7 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 6 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include - -#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2)) -#define HEAP_LEFT(X) (((X)+(X))+1) - - -/** - * The implementation of timer heap. - */ -struct pj_timer_heap_t -{ - /** Pool from which the timer heap resize will get the storage from */ - pj_pool_t *pool; - - /** Maximum size of the heap. */ - pj_size_t max_size; - - /** Current size of the heap. */ - pj_size_t cur_size; - - /** Mutex for synchronization, or NULL */ - pj_mutex_t *mutex; - - /** - * Current contents of the Heap, which is organized as a "heap" of - * pj_timer_entry *'s. In this context, a heap is a "partially - * ordered, almost complete" binary tree, which is stored in an - * array. - */ - pj_timer_entry **heap; - - /** - * An array of "pointers" that allows each pj_timer_entry in the - * to be located in O(1) time. Basically, - * contains the slot in the array where an pj_timer_entry - * with timer id resides. Thus, the timer id passed back from - * is really an slot into the array. The - * array serves two purposes: negative values are - * treated as "pointers" for the , whereas positive - * values are treated as "pointers" into the array. - */ - pj_timer_id_t *timer_ids; - - /** - * "Pointer" to the first element in the freelist contained within - * the array, which is organized as a stack. - */ - pj_timer_id_t timer_ids_freelist; - - /** Callback to be called when a timer expires. */ - pj_timer_heap_callback *callback; - -}; - - - -PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht ) -{ - if (ht->mutex) { - pj_mutex_lock(ht->mutex); - } -} - -PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht ) -{ - if (ht->mutex) { - pj_mutex_unlock(ht->mutex); - } -} - - -static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node ) -{ - PJ_CHECK_STACK(); - - // Insert into its new location in the heap. - ht->heap[slot] = moved_node; - - // Update the corresponding slot in the parallel array. - ht->timer_ids[moved_node->_timer_id] = slot; -} - -static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht ) -{ - // We need to truncate this to for backwards compatibility. - pj_timer_id_t new_id = ht->timer_ids_freelist; - - PJ_CHECK_STACK(); - - // The freelist values in the are negative, so we need - // to negate them to get the next freelist "pointer." - ht->timer_ids_freelist = - -ht->timer_ids[ht->timer_ids_freelist]; - - return new_id; - -} - -static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id) -{ - PJ_CHECK_STACK(); - - // The freelist values in the are negative, so we need - // to negate them to get the next freelist "pointer." - ht->timer_ids[old_id] = -ht->timer_ids_freelist; - ht->timer_ids_freelist = old_id; -} - - -static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node, - size_t slot, size_t child) -{ - PJ_CHECK_STACK(); - - // Restore the heap property after a deletion. - - while (child < ht->cur_size) - { - // Choose the smaller of the two children. - if (child + 1 < ht->cur_size - && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value)) - child++; - - // Perform a if the child has a larger timeout value than - // the . - if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value)) - { - copy_node( ht, slot, ht->heap[child]); - slot = child; - child = HEAP_LEFT(child); - } - else - // We've found our location in the heap. - break; - } - - copy_node( ht, slot, moved_node); -} - -static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node, - size_t slot, size_t parent) -{ - // Restore the heap property after an insertion. - - while (slot > 0) - { - // If the parent node is greater than the we need - // to copy it down. - if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value)) - { - copy_node(ht, slot, ht->heap[parent]); - slot = parent; - parent = HEAP_PARENT(slot); - } - else - break; - } - - // Insert the new node into its proper resting place in the heap and - // update the corresponding slot in the parallel array. - copy_node(ht, slot, moved_node); -} - - -static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot) -{ - pj_timer_entry *removed_node = ht->heap[slot]; - - // Return this timer id to the freelist. - push_freelist( ht, removed_node->_timer_id ); - - // Decrement the size of the heap by one since we're removing the - // "slot"th node. - ht->cur_size--; - - // Set the ID - removed_node->_timer_id = -1; - - // Only try to reheapify if we're not deleting the last entry. - - if (slot < ht->cur_size) - { - int parent; - pj_timer_entry *moved_node = ht->heap[ht->cur_size]; - - // Move the end node to the location being removed and update - // the corresponding slot in the parallel array. - copy_node( ht, slot, moved_node); - - // If the time_value_> is great than or equal its - // parent it needs be moved down the heap. - parent = HEAP_PARENT (slot); - - if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value)) - reheap_down( ht, moved_node, slot, HEAP_LEFT(slot)); - else - reheap_up( ht, moved_node, slot, parent); - } - - return removed_node; -} - -static void grow_heap(pj_timer_heap_t *ht) -{ - // All the containers will double in size from max_size_ - size_t new_size = ht->max_size * 2; - pj_timer_id_t *new_timer_ids; - pj_size_t i; - - // First grow the heap itself. - - pj_timer_entry **new_heap = 0; - - new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size); - memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*)); - //delete [] this->heap_; - ht->heap = new_heap; - - // Grow the array of timer ids. - - new_timer_ids = 0; - new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t)); - - memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t)); - - //delete [] timer_ids_; - ht->timer_ids = new_timer_ids; - - // And add the new elements to the end of the "freelist". - for (i = ht->max_size; i < new_size; i++) - ht->timer_ids[i] = -((pj_timer_id_t) (i + 1)); - - ht->max_size = new_size; -} - -static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node) -{ - if (ht->cur_size + 2 >= ht->max_size) - grow_heap(ht); - - reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size)); - ht->cur_size++; -} - - -static pj_status_t schedule_entry( pj_timer_heap_t *ht, - pj_timer_entry *entry, - const pj_time_val *future_time ) -{ - if (ht->cur_size < ht->max_size) - { - // Obtain the next unique sequence number. - // Set the entry - entry->_timer_id = pop_freelist(ht); - entry->_timer_value = *future_time; - insert_node( ht, entry); - return 0; - } - else - return -1; -} - - -static int cancel( pj_timer_heap_t *ht, - pj_timer_entry *entry, - int dont_call) -{ - long timer_node_slot; - - PJ_CHECK_STACK(); - - // Check to see if the timer_id is out of range - if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) - return 0; - - timer_node_slot = ht->timer_ids[entry->_timer_id]; - - if (timer_node_slot < 0) // Check to see if timer_id is still valid. - return 0; - - if (entry != ht->heap[timer_node_slot]) - { - pj_assert(entry == ht->heap[timer_node_slot]); - return 0; - } - else - { - remove_node( ht, timer_node_slot); - - if (dont_call == 0) - // Call the close hook. - (*ht->callback)(ht, entry); - return 1; - } -} - - -/* - * Calculate memory size required to create a timer heap. - */ -PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count) -{ - return /* size of the timer heap itself: */ - sizeof(pj_timer_heap_t) + - /* size of each entry: */ - (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) + - /* mutex, pool etc: */ - 132; -} - -/* - * Create a new timer heap. - */ -PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, - pj_size_t size, - unsigned flag, - pj_timer_heap_t **p_heap) -{ - pj_timer_heap_t *ht; - pj_size_t i; - - PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); - - *p_heap = NULL; - - /* Magic? */ - size += 2; - - /* Allocate timer heap data structure from the pool */ - ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t)); - if (!ht) - return PJ_ENOMEM; - - /* Initialize timer heap sizes */ - ht->max_size = size; - ht->cur_size = 0; - ht->timer_ids_freelist = 1; - ht->pool = pool; - - /* Mutex. */ - if (flag & PJ_TIMER_HEAP_NO_SYNCHRONIZE) { - ht->mutex = NULL; - } else { - pj_status_t rc; - - /* Mutex must be the recursive types. - * See commented code inside pj_timer_heap_poll() - */ - rc = pj_mutex_create(pool, "tmhp%p", PJ_MUTEX_RECURSE, &ht->mutex); - if (rc != PJ_SUCCESS) - return rc; - } - - // Create the heap array. - ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size); - if (!ht->heap) - return PJ_ENOMEM; - - // Create the parallel - ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size); - if (!ht->timer_ids) - return PJ_ENOMEM; - - // Initialize the "freelist," which uses negative values to - // distinguish freelist elements from "pointers" into the - // array. - for (i=0; itimer_ids[i] = -((pj_timer_id_t) (i + 1)); - - *p_heap = ht; - return PJ_SUCCESS; -} - -PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, - int id, - void *user_data, - pj_timer_heap_callback *cb ) -{ - pj_assert(entry && cb); - - entry->id = id; - entry->user_data = user_data; - entry->cb = cb; - - return entry; -} - -PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, - pj_timer_entry *entry, - const pj_time_val *delay) -{ - pj_status_t status; - pj_time_val expires; - - PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); - - pj_gettimeofday(&expires); - PJ_TIME_VAL_ADD(expires, *delay); - - lock_timer_heap(ht); - status = schedule_entry(ht, entry, &expires); - unlock_timer_heap(ht); - - return status; -} - -PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, - pj_timer_entry *entry) -{ - int count; - - PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); - - lock_timer_heap(ht); - count = cancel(ht, entry, 1); - unlock_timer_heap(ht); - - return count; -} - -PJ_DEF(int) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay ) -{ - pj_time_val now; - int count; - - PJ_ASSERT_RETURN(ht, -1); - - if (!ht->cur_size && next_delay) { - next_delay->sec = next_delay->msec = PJ_MAXINT32; - return 0; - } - - count = 0; - pj_gettimeofday(&now); - - lock_timer_heap(ht); - while ( ht->cur_size && - PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) ) - { - pj_timer_entry *node = remove_node(ht, 0); - ++count; - - //Better not to temporarily release mutex to save some syscalls. - //But then make sure the mutex must be the recursive types (PJ_MUTEX_RECURSE)! - //unlock_timer_heap(ht); - (*node->cb)(ht, node); - //lock_timer_heap(ht); - } - if (ht->cur_size && next_delay) { - *next_delay = ht->heap[0]->_timer_value; - PJ_TIME_VAL_SUB(*next_delay, now); - } else if (next_delay) { - next_delay->sec = next_delay->msec = PJ_MAXINT32; - } - unlock_timer_heap(ht); - - return count; -} - -PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ) -{ - return ht->cur_size; -} - -PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht, - pj_time_val *timeval) -{ - pj_assert(ht->cur_size != 0); - if (ht->cur_size == 0) - return PJ_ENOTFOUND; - - lock_timer_heap(ht); - *timeval = ht->heap[0]->_timer_value; - unlock_timer_heap(ht); - - return PJ_SUCCESS; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/timer.c 8 10/14/05 12:26a Bennylp $ */ +/* (C)1993-2003 Douglas C. Schmidt + * + * This file is originaly from ACE library by Doug Schmidt + * ACE(TM), TAO(TM) and CIAO(TM) are copyrighted by Douglas C. Schmidt and his research + * group at Washington University, University of California, Irvine, and Vanderbilt + * University Copyright (c) 1993-2003, all rights reserved. + */ +/* $Log: /pjproject-0.3/pjlib/src/pj/timer.c $ + * + * 8 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 7 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 6 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include + +#define HEAP_PARENT(X) (X == 0 ? 0 : (((X) - 1) / 2)) +#define HEAP_LEFT(X) (((X)+(X))+1) + + +/** + * The implementation of timer heap. + */ +struct pj_timer_heap_t +{ + /** Pool from which the timer heap resize will get the storage from */ + pj_pool_t *pool; + + /** Maximum size of the heap. */ + pj_size_t max_size; + + /** Current size of the heap. */ + pj_size_t cur_size; + + /** Mutex for synchronization, or NULL */ + pj_mutex_t *mutex; + + /** + * Current contents of the Heap, which is organized as a "heap" of + * pj_timer_entry *'s. In this context, a heap is a "partially + * ordered, almost complete" binary tree, which is stored in an + * array. + */ + pj_timer_entry **heap; + + /** + * An array of "pointers" that allows each pj_timer_entry in the + * to be located in O(1) time. Basically, + * contains the slot in the array where an pj_timer_entry + * with timer id resides. Thus, the timer id passed back from + * is really an slot into the array. The + * array serves two purposes: negative values are + * treated as "pointers" for the , whereas positive + * values are treated as "pointers" into the array. + */ + pj_timer_id_t *timer_ids; + + /** + * "Pointer" to the first element in the freelist contained within + * the array, which is organized as a stack. + */ + pj_timer_id_t timer_ids_freelist; + + /** Callback to be called when a timer expires. */ + pj_timer_heap_callback *callback; + +}; + + + +PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht ) +{ + if (ht->mutex) { + pj_mutex_lock(ht->mutex); + } +} + +PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht ) +{ + if (ht->mutex) { + pj_mutex_unlock(ht->mutex); + } +} + + +static void copy_node( pj_timer_heap_t *ht, int slot, pj_timer_entry *moved_node ) +{ + PJ_CHECK_STACK(); + + // Insert into its new location in the heap. + ht->heap[slot] = moved_node; + + // Update the corresponding slot in the parallel array. + ht->timer_ids[moved_node->_timer_id] = slot; +} + +static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht ) +{ + // We need to truncate this to for backwards compatibility. + pj_timer_id_t new_id = ht->timer_ids_freelist; + + PJ_CHECK_STACK(); + + // The freelist values in the are negative, so we need + // to negate them to get the next freelist "pointer." + ht->timer_ids_freelist = + -ht->timer_ids[ht->timer_ids_freelist]; + + return new_id; + +} + +static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id) +{ + PJ_CHECK_STACK(); + + // The freelist values in the are negative, so we need + // to negate them to get the next freelist "pointer." + ht->timer_ids[old_id] = -ht->timer_ids_freelist; + ht->timer_ids_freelist = old_id; +} + + +static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node, + size_t slot, size_t child) +{ + PJ_CHECK_STACK(); + + // Restore the heap property after a deletion. + + while (child < ht->cur_size) + { + // Choose the smaller of the two children. + if (child + 1 < ht->cur_size + && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value)) + child++; + + // Perform a if the child has a larger timeout value than + // the . + if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value)) + { + copy_node( ht, slot, ht->heap[child]); + slot = child; + child = HEAP_LEFT(child); + } + else + // We've found our location in the heap. + break; + } + + copy_node( ht, slot, moved_node); +} + +static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node, + size_t slot, size_t parent) +{ + // Restore the heap property after an insertion. + + while (slot > 0) + { + // If the parent node is greater than the we need + // to copy it down. + if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value)) + { + copy_node(ht, slot, ht->heap[parent]); + slot = parent; + parent = HEAP_PARENT(slot); + } + else + break; + } + + // Insert the new node into its proper resting place in the heap and + // update the corresponding slot in the parallel array. + copy_node(ht, slot, moved_node); +} + + +static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot) +{ + pj_timer_entry *removed_node = ht->heap[slot]; + + // Return this timer id to the freelist. + push_freelist( ht, removed_node->_timer_id ); + + // Decrement the size of the heap by one since we're removing the + // "slot"th node. + ht->cur_size--; + + // Set the ID + removed_node->_timer_id = -1; + + // Only try to reheapify if we're not deleting the last entry. + + if (slot < ht->cur_size) + { + int parent; + pj_timer_entry *moved_node = ht->heap[ht->cur_size]; + + // Move the end node to the location being removed and update + // the corresponding slot in the parallel array. + copy_node( ht, slot, moved_node); + + // If the time_value_> is great than or equal its + // parent it needs be moved down the heap. + parent = HEAP_PARENT (slot); + + if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value)) + reheap_down( ht, moved_node, slot, HEAP_LEFT(slot)); + else + reheap_up( ht, moved_node, slot, parent); + } + + return removed_node; +} + +static void grow_heap(pj_timer_heap_t *ht) +{ + // All the containers will double in size from max_size_ + size_t new_size = ht->max_size * 2; + pj_timer_id_t *new_timer_ids; + pj_size_t i; + + // First grow the heap itself. + + pj_timer_entry **new_heap = 0; + + new_heap = pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size); + memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*)); + //delete [] this->heap_; + ht->heap = new_heap; + + // Grow the array of timer ids. + + new_timer_ids = 0; + new_timer_ids = pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t)); + + memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t)); + + //delete [] timer_ids_; + ht->timer_ids = new_timer_ids; + + // And add the new elements to the end of the "freelist". + for (i = ht->max_size; i < new_size; i++) + ht->timer_ids[i] = -((pj_timer_id_t) (i + 1)); + + ht->max_size = new_size; +} + +static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node) +{ + if (ht->cur_size + 2 >= ht->max_size) + grow_heap(ht); + + reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size)); + ht->cur_size++; +} + + +static pj_status_t schedule_entry( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *future_time ) +{ + if (ht->cur_size < ht->max_size) + { + // Obtain the next unique sequence number. + // Set the entry + entry->_timer_id = pop_freelist(ht); + entry->_timer_value = *future_time; + insert_node( ht, entry); + return 0; + } + else + return -1; +} + + +static int cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry, + int dont_call) +{ + long timer_node_slot; + + PJ_CHECK_STACK(); + + // Check to see if the timer_id is out of range + if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) + return 0; + + timer_node_slot = ht->timer_ids[entry->_timer_id]; + + if (timer_node_slot < 0) // Check to see if timer_id is still valid. + return 0; + + if (entry != ht->heap[timer_node_slot]) + { + pj_assert(entry == ht->heap[timer_node_slot]); + return 0; + } + else + { + remove_node( ht, timer_node_slot); + + if (dont_call == 0) + // Call the close hook. + (*ht->callback)(ht, entry); + return 1; + } +} + + +/* + * Calculate memory size required to create a timer heap. + */ +PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count) +{ + return /* size of the timer heap itself: */ + sizeof(pj_timer_heap_t) + + /* size of each entry: */ + (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) + + /* mutex, pool etc: */ + 132; +} + +/* + * Create a new timer heap. + */ +PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool, + pj_size_t size, + unsigned flag, + pj_timer_heap_t **p_heap) +{ + pj_timer_heap_t *ht; + pj_size_t i; + + PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); + + *p_heap = NULL; + + /* Magic? */ + size += 2; + + /* Allocate timer heap data structure from the pool */ + ht = pj_pool_alloc(pool, sizeof(pj_timer_heap_t)); + if (!ht) + return PJ_ENOMEM; + + /* Initialize timer heap sizes */ + ht->max_size = size; + ht->cur_size = 0; + ht->timer_ids_freelist = 1; + ht->pool = pool; + + /* Mutex. */ + if (flag & PJ_TIMER_HEAP_NO_SYNCHRONIZE) { + ht->mutex = NULL; + } else { + pj_status_t rc; + + /* Mutex must be the recursive types. + * See commented code inside pj_timer_heap_poll() + */ + rc = pj_mutex_create(pool, "tmhp%p", PJ_MUTEX_RECURSE, &ht->mutex); + if (rc != PJ_SUCCESS) + return rc; + } + + // Create the heap array. + ht->heap = pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size); + if (!ht->heap) + return PJ_ENOMEM; + + // Create the parallel + ht->timer_ids = pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size); + if (!ht->timer_ids) + return PJ_ENOMEM; + + // Initialize the "freelist," which uses negative values to + // distinguish freelist elements from "pointers" into the + // array. + for (i=0; itimer_ids[i] = -((pj_timer_id_t) (i + 1)); + + *p_heap = ht; + return PJ_SUCCESS; +} + +PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry, + int id, + void *user_data, + pj_timer_heap_callback *cb ) +{ + pj_assert(entry && cb); + + entry->id = id; + entry->user_data = user_data; + entry->cb = cb; + + return entry; +} + +PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht, + pj_timer_entry *entry, + const pj_time_val *delay) +{ + pj_status_t status; + pj_time_val expires; + + PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); + + pj_gettimeofday(&expires); + PJ_TIME_VAL_ADD(expires, *delay); + + lock_timer_heap(ht); + status = schedule_entry(ht, entry, &expires); + unlock_timer_heap(ht); + + return status; +} + +PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht, + pj_timer_entry *entry) +{ + int count; + + PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); + + lock_timer_heap(ht); + count = cancel(ht, entry, 1); + unlock_timer_heap(ht); + + return count; +} + +PJ_DEF(int) pj_timer_heap_poll( pj_timer_heap_t *ht, pj_time_val *next_delay ) +{ + pj_time_val now; + int count; + + PJ_ASSERT_RETURN(ht, -1); + + if (!ht->cur_size && next_delay) { + next_delay->sec = next_delay->msec = PJ_MAXINT32; + return 0; + } + + count = 0; + pj_gettimeofday(&now); + + lock_timer_heap(ht); + while ( ht->cur_size && + PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) ) + { + pj_timer_entry *node = remove_node(ht, 0); + ++count; + + //Better not to temporarily release mutex to save some syscalls. + //But then make sure the mutex must be the recursive types (PJ_MUTEX_RECURSE)! + //unlock_timer_heap(ht); + (*node->cb)(ht, node); + //lock_timer_heap(ht); + } + if (ht->cur_size && next_delay) { + *next_delay = ht->heap[0]->_timer_value; + PJ_TIME_VAL_SUB(*next_delay, now); + } else if (next_delay) { + next_delay->sec = next_delay->msec = PJ_MAXINT32; + } + unlock_timer_heap(ht); + + return count; +} + +PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht ) +{ + return ht->cur_size; +} + +PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht, + pj_time_val *timeval) +{ + pj_assert(ht->cur_size != 0); + if (ht->cur_size == 0) + return PJ_ENOTFOUND; + + lock_timer_heap(ht); + *timeval = ht->heap[0]->_timer_value; + unlock_timer_heap(ht); + + return PJ_SUCCESS; +} + diff --git a/pjlib/src/pj/types.c b/pjlib/src/pj/types.c index ee1a8588f..4a7252383 100644 --- a/pjlib/src/pj/types.c +++ b/pjlib/src/pj/types.c @@ -1,36 +1,36 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/types.c 4 9/17/05 10:37a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/types.c $ - * - * 4 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include - -void pj_time_val_normalize(pj_time_val *t) -{ - PJ_CHECK_STACK(); - - if (t->msec >= 1000) { - do { - t->sec++; - t->msec -= 1000; - } while (t->msec >= 1000); - } - else if (t->msec <= -1000) { - do { - t->sec--; - t->msec += 1000; - } while (t->msec <= -1000); - } - - if (t->sec >= 1 && t->msec < 0) { - t->sec--; - t->msec += 1000; - - } else if (t->sec < 0 && t->msec > 0) { - t->sec++; - t->msec -= 1000; - } -} +/* $Header: /pjproject-0.3/pjlib/src/pj/types.c 4 9/17/05 10:37a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/types.c $ + * + * 4 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include + +void pj_time_val_normalize(pj_time_val *t) +{ + PJ_CHECK_STACK(); + + if (t->msec >= 1000) { + do { + t->sec++; + t->msec -= 1000; + } while (t->msec >= 1000); + } + else if (t->msec <= -1000) { + do { + t->sec--; + t->msec += 1000; + } while (t->msec <= -1000); + } + + if (t->sec >= 1 && t->msec < 0) { + t->sec--; + t->msec += 1000; + + } else if (t->sec < 0 && t->msec > 0) { + t->sec++; + t->msec -= 1000; + } +} diff --git a/pjlib/src/pj/xml.c b/pjlib/src/pj/xml.c index ff3684a81..8dccedecc 100644 --- a/pjlib/src/pj/xml.c +++ b/pjlib/src/pj/xml.c @@ -1,392 +1,392 @@ -/* $Header: /pjproject-0.3/pjlib/src/pj/xml.c 9 10/14/05 12:26a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pj/xml.c $ - * - * 9 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 8 9/21/05 1:39p Bennylp - * Periodic checkin for backup. - * - * 7 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - */ -#include -#include -#include -#include -#include -#include -#include - -#define EX_SYNTAX_ERROR 12 -#define THIS_FILE "xml.c" - -static void on_syntax_error(struct pj_scanner *scanner) -{ - PJ_UNUSED_ARG(scanner); - PJ_THROW(EX_SYNTAX_ERROR); -} - -static pj_xml_node *alloc_node( pj_pool_t *pool ) -{ - pj_xml_node *node; - - node = pj_pool_calloc(pool, 1, sizeof(pj_xml_node)); - pj_list_init( &node->attr_head ); - pj_list_init( &node->node_head ); - - return node; -} - -static pj_xml_attr *alloc_attr( pj_pool_t *pool ) -{ - return pj_pool_calloc(pool, 1, sizeof(pj_xml_attr)); -} - -/* This is a recursive function! */ -static pj_xml_node *xml_parse_node( pj_pool_t *pool, pj_scanner *scanner) -{ - pj_xml_node *node; - pj_str_t end_name; - - PJ_CHECK_STACK(); - - if (*scanner->curptr != '<') - on_syntax_error(scanner); - - /* Handle Processing Instructino (PI) construct (i.e. "curptr == '<' && *(scanner->curptr+1) == '?') { - pj_scan_advance_n(scanner, 2, PJ_FALSE); - for (;;) { - pj_str_t dummy; - pj_scan_get_until_ch(scanner, '?', &dummy); - if (*scanner->curptr=='?' && *(scanner->curptr+1)=='>') { - pj_scan_advance_n(scanner, 2, PJ_TRUE); - break; - } else { - pj_scan_advance_n(scanner, 1, PJ_FALSE); - } - } - return xml_parse_node(pool, scanner); - } - - /* Handle comments construct (i.e. "", 3) == 0) { - pj_scan_advance_n(scanner, 3, PJ_TRUE); - break; - } else { - pj_scan_advance_n(scanner, 1, PJ_FALSE); - } - } - return xml_parse_node(pool, scanner); - } - - /* Alloc node. */ - node = alloc_node(pool); - - /* Get '<' */ - pj_scan_get_char(scanner); - - /* Get node name. */ - pj_scan_get_until_chr( scanner, " />\t", &node->name); - - /* Get attributes. */ - while (*scanner->curptr != '>' && *scanner->curptr != '/') { - pj_xml_attr *attr = alloc_attr(pool); - - pj_scan_get_until_chr( scanner, "=> \t", &attr->name); - if (*scanner->curptr == '=') { - pj_scan_get_char( scanner ); - pj_scan_get_quote(scanner, '"', '"', &attr->value); - /* remove quote characters */ - ++attr->value.ptr; - attr->value.slen -= 2; - } - - pj_list_insert_before( &node->attr_head, attr ); - } - - if (*scanner->curptr == '/') { - pj_scan_get_char(scanner); - if (pj_scan_get_char(scanner) != '>') - on_syntax_error(scanner); - return node; - } - - /* Enclosing bracket. */ - if (pj_scan_get_char(scanner) != '>') - on_syntax_error(scanner); - - /* Sub nodes. */ - while (*scanner->curptr == '<' && *(scanner->curptr+1) != '/') { - pj_xml_node *sub_node = xml_parse_node(pool, scanner); - pj_list_insert_before( &node->node_head, sub_node ); - } - - /* Content. */ - if (!pj_scan_is_eof(scanner) && *scanner->curptr != '<') { - pj_scan_get_until_ch(scanner, '<', &node->content); - } - - /* Enclosing node. */ - if (pj_scan_get_char(scanner) != '<' || pj_scan_get_char(scanner) != '/') - on_syntax_error(scanner); - - pj_scan_get_until_chr(scanner, " \t>", &end_name); - - /* Compare name. */ - if (pj_stricmp(&node->name, &end_name) != 0) - on_syntax_error(scanner); - - /* Enclosing '>' */ - if (pj_scan_get_char(scanner) != '>') - on_syntax_error(scanner); - - return node; -} - -PJ_DEF(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len) -{ - pj_xml_node *node = NULL; - pj_scanner scanner; - PJ_USE_EXCEPTION; - - if (!msg || !len || !pool) - return NULL; - - pj_scan_init( &scanner, msg, len, - PJ_SCAN_AUTOSKIP_WS|PJ_SCAN_AUTOSKIP_NEWLINE, - &on_syntax_error); - PJ_TRY { - node = xml_parse_node(pool, &scanner); - } - PJ_DEFAULT { - PJ_LOG(4,(THIS_FILE, "Syntax error parsing XML in line %d column %d", - scanner.line, scanner.col)); - } - PJ_END; - pj_scan_fini( &scanner ); - return node; -} - -/* This is a recursive function. */ -static int xml_print_node( const pj_xml_node *node, int indent, - char *buf, pj_size_t len ) -{ - int i; - char *p = buf; - pj_xml_attr *attr; - pj_xml_node *sub_node; - -#define SIZE_LEFT() ((int)(len - (p-buf))) - - PJ_CHECK_STACK(); - - /* Print name. */ - if (SIZE_LEFT() < node->name.slen + indent + 5) - return -1; - for (i=0; iname.ptr, node->name.slen); - p += node->name.slen; - - /* Print attributes. */ - attr = node->attr_head.next; - while (attr != &node->attr_head) { - - if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4) - return -1; - - *p++ = ' '; - - /* Attribute name. */ - pj_memcpy(p, attr->name.ptr, attr->name.slen); - p += attr->name.slen; - - /* Attribute value. */ - if (attr->value.slen) { - *p++ = '='; - *p++ = '"'; - pj_memcpy(p, attr->value.ptr, attr->value.slen); - p += attr->value.slen; - *p++ = '"'; - } - - attr = attr->next; - } - - /* Check for empty node. */ - if (node->content.slen==0 && - node->node_head.next==(pj_xml_node*)&node->node_head) - { - *p++ = ' '; - *p++ = '/'; - *p++ = '>'; - return p-buf; - } - - /* Enclosing '>' */ - if (SIZE_LEFT() < 1) return -1; - *p++ = '>'; - - /* Print sub nodes. */ - sub_node = node->node_head.next; - while (sub_node != (pj_xml_node*)&node->node_head) { - int printed; - - if (SIZE_LEFT() < indent + 3) - return -1; - //*p++ = '\r'; - *p++ = '\n'; - - printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT()); - if (printed < 0) - return -1; - - p += printed; - sub_node = sub_node->next; - } - - /* Content. */ - if (node->content.slen) { - if (SIZE_LEFT() < node->content.slen) return -1; - pj_memcpy(p, node->content.ptr, node->content.slen); - p += node->content.slen; - } - - /* Enclosing node. */ - if (node->node_head.next != (pj_xml_node*)&node->node_head) { - if (SIZE_LEFT() < node->name.slen + 5 + indent) - return -1; - //*p++ = '\r'; - *p++ = '\n'; - for (i=0; iname.slen + 3) - return -1; - } - *p++ = '<'; - *p++ = '/'; - pj_memcpy(p, node->name.ptr, node->name.slen); - p += node->name.slen; - *p++ = '>'; - -#undef SIZE_LEFT - - return p - buf; -} - -PJ_DEF(int) pj_xml_print(const pj_xml_node *node, char *buf, pj_size_t len, - pj_bool_t include_prolog) -{ - int prolog_len = 0; - int printed; - - if (!node || !buf || !len) - return 0; - - if (include_prolog) { - pj_str_t prolog = {"\n", 39}; - if ((int)len < prolog.slen) - return -1; - pj_memcpy(buf, prolog.ptr, prolog.slen); - prolog_len = prolog.slen; - } - - printed = xml_print_node(node, 0, buf+prolog_len, len-prolog_len) + prolog_len; - if (printed > 0 && len-printed >= 1) { - buf[printed++] = '\n'; - } - return printed; -} - - -PJ_DEF(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node ) -{ - pj_list_insert_before(&parent->node_head, node); -} - -PJ_DEF(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr ) -{ - pj_list_insert_before(&node->attr_head, attr); -} - -PJ_DEF(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name) -{ - pj_xml_node *node = parent->node_head.next; - - PJ_CHECK_STACK(); - - while (node != (void*)&parent->node_head) { - if (pj_stricmp(&node->name, name) == 0) - return node; - node = node->next; - } - return NULL; -} - - -PJ_DEF(pj_xml_node*) pj_xml_find_next_node( pj_xml_node *parent, pj_xml_node *node, - const pj_str_t *name) -{ - PJ_CHECK_STACK(); - - node = node->next; - while (node != (void*)&parent->node_head) { - if (pj_stricmp(&node->name, name) == 0) - return node; - node = node->next; - } - return NULL; -} - - -PJ_DEF(pj_xml_attr*) pj_xml_find_attr( pj_xml_node *node, const pj_str_t *name, - const pj_str_t *value) -{ - pj_xml_attr *attr = node->attr_head.next; - while (attr != (void*)&node->attr_head) { - if (pj_stricmp(&attr->name, name)==0) { - if (value) { - if (pj_stricmp(&attr->value, value)==0) - return attr; - } else { - return attr; - } - } - attr = attr->next; - } - return NULL; -} - - - -PJ_DEF(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name, - const void *data, - pj_bool_t (*match)(pj_xml_node *, const void*)) -{ - pj_xml_node *head = (void*)&parent->node_head, *node = head->next; - - while (node != (void*)head) { - if (name && pj_stricmp(&node->name, name)==0) { - if (match) { - if (match(node, data)) - return node; - } else { - return node; - } - } - node = node->next; - } - return NULL; -} - +/* $Header: /pjproject-0.3/pjlib/src/pj/xml.c 9 10/14/05 12:26a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pj/xml.c $ + * + * 9 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 8 9/21/05 1:39p Bennylp + * Periodic checkin for backup. + * + * 7 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + */ +#include +#include +#include +#include +#include +#include +#include + +#define EX_SYNTAX_ERROR 12 +#define THIS_FILE "xml.c" + +static void on_syntax_error(struct pj_scanner *scanner) +{ + PJ_UNUSED_ARG(scanner); + PJ_THROW(EX_SYNTAX_ERROR); +} + +static pj_xml_node *alloc_node( pj_pool_t *pool ) +{ + pj_xml_node *node; + + node = pj_pool_calloc(pool, 1, sizeof(pj_xml_node)); + pj_list_init( &node->attr_head ); + pj_list_init( &node->node_head ); + + return node; +} + +static pj_xml_attr *alloc_attr( pj_pool_t *pool ) +{ + return pj_pool_calloc(pool, 1, sizeof(pj_xml_attr)); +} + +/* This is a recursive function! */ +static pj_xml_node *xml_parse_node( pj_pool_t *pool, pj_scanner *scanner) +{ + pj_xml_node *node; + pj_str_t end_name; + + PJ_CHECK_STACK(); + + if (*scanner->curptr != '<') + on_syntax_error(scanner); + + /* Handle Processing Instructino (PI) construct (i.e. "curptr == '<' && *(scanner->curptr+1) == '?') { + pj_scan_advance_n(scanner, 2, PJ_FALSE); + for (;;) { + pj_str_t dummy; + pj_scan_get_until_ch(scanner, '?', &dummy); + if (*scanner->curptr=='?' && *(scanner->curptr+1)=='>') { + pj_scan_advance_n(scanner, 2, PJ_TRUE); + break; + } else { + pj_scan_advance_n(scanner, 1, PJ_FALSE); + } + } + return xml_parse_node(pool, scanner); + } + + /* Handle comments construct (i.e. "", 3) == 0) { + pj_scan_advance_n(scanner, 3, PJ_TRUE); + break; + } else { + pj_scan_advance_n(scanner, 1, PJ_FALSE); + } + } + return xml_parse_node(pool, scanner); + } + + /* Alloc node. */ + node = alloc_node(pool); + + /* Get '<' */ + pj_scan_get_char(scanner); + + /* Get node name. */ + pj_scan_get_until_chr( scanner, " />\t", &node->name); + + /* Get attributes. */ + while (*scanner->curptr != '>' && *scanner->curptr != '/') { + pj_xml_attr *attr = alloc_attr(pool); + + pj_scan_get_until_chr( scanner, "=> \t", &attr->name); + if (*scanner->curptr == '=') { + pj_scan_get_char( scanner ); + pj_scan_get_quote(scanner, '"', '"', &attr->value); + /* remove quote characters */ + ++attr->value.ptr; + attr->value.slen -= 2; + } + + pj_list_insert_before( &node->attr_head, attr ); + } + + if (*scanner->curptr == '/') { + pj_scan_get_char(scanner); + if (pj_scan_get_char(scanner) != '>') + on_syntax_error(scanner); + return node; + } + + /* Enclosing bracket. */ + if (pj_scan_get_char(scanner) != '>') + on_syntax_error(scanner); + + /* Sub nodes. */ + while (*scanner->curptr == '<' && *(scanner->curptr+1) != '/') { + pj_xml_node *sub_node = xml_parse_node(pool, scanner); + pj_list_insert_before( &node->node_head, sub_node ); + } + + /* Content. */ + if (!pj_scan_is_eof(scanner) && *scanner->curptr != '<') { + pj_scan_get_until_ch(scanner, '<', &node->content); + } + + /* Enclosing node. */ + if (pj_scan_get_char(scanner) != '<' || pj_scan_get_char(scanner) != '/') + on_syntax_error(scanner); + + pj_scan_get_until_chr(scanner, " \t>", &end_name); + + /* Compare name. */ + if (pj_stricmp(&node->name, &end_name) != 0) + on_syntax_error(scanner); + + /* Enclosing '>' */ + if (pj_scan_get_char(scanner) != '>') + on_syntax_error(scanner); + + return node; +} + +PJ_DEF(pj_xml_node*) pj_xml_parse( pj_pool_t *pool, char *msg, pj_size_t len) +{ + pj_xml_node *node = NULL; + pj_scanner scanner; + PJ_USE_EXCEPTION; + + if (!msg || !len || !pool) + return NULL; + + pj_scan_init( &scanner, msg, len, + PJ_SCAN_AUTOSKIP_WS|PJ_SCAN_AUTOSKIP_NEWLINE, + &on_syntax_error); + PJ_TRY { + node = xml_parse_node(pool, &scanner); + } + PJ_DEFAULT { + PJ_LOG(4,(THIS_FILE, "Syntax error parsing XML in line %d column %d", + scanner.line, scanner.col)); + } + PJ_END; + pj_scan_fini( &scanner ); + return node; +} + +/* This is a recursive function. */ +static int xml_print_node( const pj_xml_node *node, int indent, + char *buf, pj_size_t len ) +{ + int i; + char *p = buf; + pj_xml_attr *attr; + pj_xml_node *sub_node; + +#define SIZE_LEFT() ((int)(len - (p-buf))) + + PJ_CHECK_STACK(); + + /* Print name. */ + if (SIZE_LEFT() < node->name.slen + indent + 5) + return -1; + for (i=0; iname.ptr, node->name.slen); + p += node->name.slen; + + /* Print attributes. */ + attr = node->attr_head.next; + while (attr != &node->attr_head) { + + if (SIZE_LEFT() < attr->name.slen + attr->value.slen + 4) + return -1; + + *p++ = ' '; + + /* Attribute name. */ + pj_memcpy(p, attr->name.ptr, attr->name.slen); + p += attr->name.slen; + + /* Attribute value. */ + if (attr->value.slen) { + *p++ = '='; + *p++ = '"'; + pj_memcpy(p, attr->value.ptr, attr->value.slen); + p += attr->value.slen; + *p++ = '"'; + } + + attr = attr->next; + } + + /* Check for empty node. */ + if (node->content.slen==0 && + node->node_head.next==(pj_xml_node*)&node->node_head) + { + *p++ = ' '; + *p++ = '/'; + *p++ = '>'; + return p-buf; + } + + /* Enclosing '>' */ + if (SIZE_LEFT() < 1) return -1; + *p++ = '>'; + + /* Print sub nodes. */ + sub_node = node->node_head.next; + while (sub_node != (pj_xml_node*)&node->node_head) { + int printed; + + if (SIZE_LEFT() < indent + 3) + return -1; + //*p++ = '\r'; + *p++ = '\n'; + + printed = xml_print_node(sub_node, indent + 1, p, SIZE_LEFT()); + if (printed < 0) + return -1; + + p += printed; + sub_node = sub_node->next; + } + + /* Content. */ + if (node->content.slen) { + if (SIZE_LEFT() < node->content.slen) return -1; + pj_memcpy(p, node->content.ptr, node->content.slen); + p += node->content.slen; + } + + /* Enclosing node. */ + if (node->node_head.next != (pj_xml_node*)&node->node_head) { + if (SIZE_LEFT() < node->name.slen + 5 + indent) + return -1; + //*p++ = '\r'; + *p++ = '\n'; + for (i=0; iname.slen + 3) + return -1; + } + *p++ = '<'; + *p++ = '/'; + pj_memcpy(p, node->name.ptr, node->name.slen); + p += node->name.slen; + *p++ = '>'; + +#undef SIZE_LEFT + + return p - buf; +} + +PJ_DEF(int) pj_xml_print(const pj_xml_node *node, char *buf, pj_size_t len, + pj_bool_t include_prolog) +{ + int prolog_len = 0; + int printed; + + if (!node || !buf || !len) + return 0; + + if (include_prolog) { + pj_str_t prolog = {"\n", 39}; + if ((int)len < prolog.slen) + return -1; + pj_memcpy(buf, prolog.ptr, prolog.slen); + prolog_len = prolog.slen; + } + + printed = xml_print_node(node, 0, buf+prolog_len, len-prolog_len) + prolog_len; + if (printed > 0 && len-printed >= 1) { + buf[printed++] = '\n'; + } + return printed; +} + + +PJ_DEF(void) pj_xml_add_node( pj_xml_node *parent, pj_xml_node *node ) +{ + pj_list_insert_before(&parent->node_head, node); +} + +PJ_DEF(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr ) +{ + pj_list_insert_before(&node->attr_head, attr); +} + +PJ_DEF(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name) +{ + pj_xml_node *node = parent->node_head.next; + + PJ_CHECK_STACK(); + + while (node != (void*)&parent->node_head) { + if (pj_stricmp(&node->name, name) == 0) + return node; + node = node->next; + } + return NULL; +} + + +PJ_DEF(pj_xml_node*) pj_xml_find_next_node( pj_xml_node *parent, pj_xml_node *node, + const pj_str_t *name) +{ + PJ_CHECK_STACK(); + + node = node->next; + while (node != (void*)&parent->node_head) { + if (pj_stricmp(&node->name, name) == 0) + return node; + node = node->next; + } + return NULL; +} + + +PJ_DEF(pj_xml_attr*) pj_xml_find_attr( pj_xml_node *node, const pj_str_t *name, + const pj_str_t *value) +{ + pj_xml_attr *attr = node->attr_head.next; + while (attr != (void*)&node->attr_head) { + if (pj_stricmp(&attr->name, name)==0) { + if (value) { + if (pj_stricmp(&attr->value, value)==0) + return attr; + } else { + return attr; + } + } + attr = attr->next; + } + return NULL; +} + + + +PJ_DEF(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name, + const void *data, + pj_bool_t (*match)(pj_xml_node *, const void*)) +{ + pj_xml_node *head = (void*)&parent->node_head, *node = head->next; + + while (node != (void*)head) { + if (name && pj_stricmp(&node->name, name)==0) { + if (match) { + if (match(node, data)) + return node; + } else { + return node; + } + } + node = node->next; + } + return NULL; +} + diff --git a/pjlib/src/pjlib-samples/except.c b/pjlib/src/pjlib-samples/except.c index c6b7f556c..e3648d203 100644 --- a/pjlib/src/pjlib-samples/except.c +++ b/pjlib/src/pjlib-samples/except.c @@ -1,79 +1,79 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-samples/except.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-samples/except.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/10/05 3:16p Bennylp - * Created. - * - */ -#include -#include -#include -#include - -/** - * \page page_pjlib_samples_except_c Example: Exception Handling - * - * Below is sample program to demonstrate how to use exception handling. - * - * \includelineno pjlib-samples/except.c - */ - -static pj_exception_id_t NO_MEMORY, OTHER_EXCEPTION; - -static void randomly_throw_exception() -{ - if (pj_rand() % 2) - PJ_THROW(OTHER_EXCEPTION); -} - -static void *my_malloc(size_t size) -{ - void *ptr = malloc(size); - if (!ptr) - PJ_THROW(NO_MEMORY); - return ptr; -} - -static int test_exception() -{ - PJ_USE_EXCEPTION; - - PJ_TRY { - void *data = my_malloc(200); - free(data); - randomly_throw_exception(); - } - PJ_CATCH( NO_MEMORY ) { - puts("Can't allocate memory"); - return 0; - } - PJ_DEFAULT { - pj_exception_id_t x_id; - - x_id = PJ_GET_EXCEPTION(); - printf("Caught exception %d (%s)\n", - x_id, pj_exception_id_name(x_id)); - } - PJ_END - return 1; -} - -int main() -{ - pj_status_t rc; - - // Error handling is omited for clarity. - - rc = pj_init(); - - rc = pj_exception_id_alloc("No Memory", &NO_MEMORY); - rc = pj_exception_id_alloc("Other Exception", &OTHER_EXCEPTION); - - return test_exception(); -} - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-samples/except.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-samples/except.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/10/05 3:16p Bennylp + * Created. + * + */ +#include +#include +#include +#include + +/** + * \page page_pjlib_samples_except_c Example: Exception Handling + * + * Below is sample program to demonstrate how to use exception handling. + * + * \includelineno pjlib-samples/except.c + */ + +static pj_exception_id_t NO_MEMORY, OTHER_EXCEPTION; + +static void randomly_throw_exception() +{ + if (pj_rand() % 2) + PJ_THROW(OTHER_EXCEPTION); +} + +static void *my_malloc(size_t size) +{ + void *ptr = malloc(size); + if (!ptr) + PJ_THROW(NO_MEMORY); + return ptr; +} + +static int test_exception() +{ + PJ_USE_EXCEPTION; + + PJ_TRY { + void *data = my_malloc(200); + free(data); + randomly_throw_exception(); + } + PJ_CATCH( NO_MEMORY ) { + puts("Can't allocate memory"); + return 0; + } + PJ_DEFAULT { + pj_exception_id_t x_id; + + x_id = PJ_GET_EXCEPTION(); + printf("Caught exception %d (%s)\n", + x_id, pj_exception_id_name(x_id)); + } + PJ_END + return 1; +} + +int main() +{ + pj_status_t rc; + + // Error handling is omited for clarity. + + rc = pj_init(); + + rc = pj_exception_id_alloc("No Memory", &NO_MEMORY); + rc = pj_exception_id_alloc("Other Exception", &OTHER_EXCEPTION); + + return test_exception(); +} + diff --git a/pjlib/src/pjlib-samples/list.c b/pjlib/src/pjlib-samples/list.c index 74e783f5a..11518ebd4 100644 --- a/pjlib/src/pjlib-samples/list.c +++ b/pjlib/src/pjlib-samples/list.c @@ -1,66 +1,66 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-samples/list.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-samples/list.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/10/05 5:12p Bennylp - * Created. - * - */ - -#include -#include -#include - -/** - * \page page_pjlib_samples_list_c Example: List Manipulation - * - * Below is sample program to demonstrate how to manipulate linked list. - * - * \includelineno pjlib-samples/list.c - */ - -struct my_node -{ - // This must be the first member declared in the struct! - PJ_DECL_LIST_MEMBER(struct my_node) - int value; -}; - - -int main() -{ - struct my_node nodes[10]; - struct my_node list; - struct my_node *it; - int i; - - // Initialize the list as empty. - pj_list_init(&list); - - // Insert nodes. - for (i=0; i<10; ++i) { - nodes[i].value = i; - pj_list_insert_before(&list, &nodes[i]); - } - - // Iterate list nodes. - it = list.next; - while (it != &list) { - PJ_LOG(3,("list", "value = %d", it->value)); - it = it->next; - } - - // Erase all nodes. - for (i=0; i<10; ++i) { - pj_list_erase(&nodes[i]); - } - - // List must be empty by now. - pj_assert( pj_list_empty(&list) ); - - return 0; -}; +/* $Header: /pjproject-0.3/pjlib/src/pjlib-samples/list.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-samples/list.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/10/05 5:12p Bennylp + * Created. + * + */ + +#include +#include +#include + +/** + * \page page_pjlib_samples_list_c Example: List Manipulation + * + * Below is sample program to demonstrate how to manipulate linked list. + * + * \includelineno pjlib-samples/list.c + */ + +struct my_node +{ + // This must be the first member declared in the struct! + PJ_DECL_LIST_MEMBER(struct my_node) + int value; +}; + + +int main() +{ + struct my_node nodes[10]; + struct my_node list; + struct my_node *it; + int i; + + // Initialize the list as empty. + pj_list_init(&list); + + // Insert nodes. + for (i=0; i<10; ++i) { + nodes[i].value = i; + pj_list_insert_before(&list, &nodes[i]); + } + + // Iterate list nodes. + it = list.next; + while (it != &list) { + PJ_LOG(3,("list", "value = %d", it->value)); + it = it->next; + } + + // Erase all nodes. + for (i=0; i<10; ++i) { + pj_list_erase(&nodes[i]); + } + + // List must be empty by now. + pj_assert( pj_list_empty(&list) ); + + return 0; +}; diff --git a/pjlib/src/pjlib-samples/log.c b/pjlib/src/pjlib-samples/log.c index b90c71d03..196bf663f 100644 --- a/pjlib/src/pjlib-samples/log.c +++ b/pjlib/src/pjlib-samples/log.c @@ -1,36 +1,36 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-samples/log.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-samples/log.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/10/05 3:16p Bennylp - * Created. - * - */ -#include - -/** - * \page page_pjlib_samples_log_c Example: Log, Hello World - * - * Very simple program to write log. - * - * \includelineno pjlib-samples/log.c - */ - -int main() -{ - pj_status_t rc; - - // Error handling omited for clarity - - // Must initialize PJLIB first! - rc = pj_init(); - - PJ_LOG(3, ("main.c", "Hello world!")); - - return 0; -} - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-samples/log.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-samples/log.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/10/05 3:16p Bennylp + * Created. + * + */ +#include + +/** + * \page page_pjlib_samples_log_c Example: Log, Hello World + * + * Very simple program to write log. + * + * \includelineno pjlib-samples/log.c + */ + +int main() +{ + pj_status_t rc; + + // Error handling omited for clarity + + // Must initialize PJLIB first! + rc = pj_init(); + + PJ_LOG(3, ("main.c", "Hello world!")); + + return 0; +} + diff --git a/pjlib/src/pjlib-test/atomic.c b/pjlib/src/pjlib-test/atomic.c index 0a1ebb7df..a68db2542 100644 --- a/pjlib/src/pjlib-test/atomic.c +++ b/pjlib/src/pjlib-test/atomic.c @@ -1,94 +1,94 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/atomic.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/atomic.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/07/05 9:49p Bennylp - * Created. - * - */ -#include "test.h" -#include - -/** - * \page page_pjlib_atomic_test Test: Atomic Variable - * - * This file provides implementation of \b atomic_test(). It tests the - * functionality of the atomic variable API. - * - * \section atomic_test_sec Scope of the Test - * - * API tested: - * - pj_atomic_create() - * - pj_atomic_get() - * - pj_atomic_inc() - * - pj_atomic_dec() - * - pj_atomic_set() - * - pj_atomic_destroy() - * - * - * This file is pjlib-test/atomic.c - * - * \include pjlib-test/atomic.c - */ - - -#if INCLUDE_ATOMIC_TEST - -int atomic_test(void) -{ - pj_pool_t *pool; - pj_atomic_t *atomic_var; - pj_status_t rc; - - pool = pj_pool_create(mem, NULL, 4096, 0, NULL); - if (!pool) - return -10; - - /* create() */ - rc = pj_atomic_create(pool, 111, &atomic_var); - if (rc != 0) { - return -20; - } - - /* get: check the value. */ - if (pj_atomic_get(atomic_var) != 111) - return -30; - - /* increment. */ - if (pj_atomic_inc(atomic_var) != 112) - return -40; - - /* decrement. */ - if (pj_atomic_dec(atomic_var) != 111) - return -50; - - /* set */ - if (pj_atomic_set(atomic_var, 211) != 111) - return -60; - - /* check the value again. */ - if (pj_atomic_get(atomic_var) != 211) - return -70; - - /* destroy */ - rc = pj_atomic_destroy(atomic_var); - if (rc != 0) - return -80; - - pj_pool_release(pool); - - return 0; -} - - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_atomic_test; -#endif /* INCLUDE_ATOMIC_TEST */ - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/atomic.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/atomic.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/07/05 9:49p Bennylp + * Created. + * + */ +#include "test.h" +#include + +/** + * \page page_pjlib_atomic_test Test: Atomic Variable + * + * This file provides implementation of \b atomic_test(). It tests the + * functionality of the atomic variable API. + * + * \section atomic_test_sec Scope of the Test + * + * API tested: + * - pj_atomic_create() + * - pj_atomic_get() + * - pj_atomic_inc() + * - pj_atomic_dec() + * - pj_atomic_set() + * - pj_atomic_destroy() + * + * + * This file is pjlib-test/atomic.c + * + * \include pjlib-test/atomic.c + */ + + +#if INCLUDE_ATOMIC_TEST + +int atomic_test(void) +{ + pj_pool_t *pool; + pj_atomic_t *atomic_var; + pj_status_t rc; + + pool = pj_pool_create(mem, NULL, 4096, 0, NULL); + if (!pool) + return -10; + + /* create() */ + rc = pj_atomic_create(pool, 111, &atomic_var); + if (rc != 0) { + return -20; + } + + /* get: check the value. */ + if (pj_atomic_get(atomic_var) != 111) + return -30; + + /* increment. */ + if (pj_atomic_inc(atomic_var) != 112) + return -40; + + /* decrement. */ + if (pj_atomic_dec(atomic_var) != 111) + return -50; + + /* set */ + if (pj_atomic_set(atomic_var, 211) != 111) + return -60; + + /* check the value again. */ + if (pj_atomic_get(atomic_var) != 211) + return -70; + + /* destroy */ + rc = pj_atomic_destroy(atomic_var); + if (rc != 0) + return -80; + + pj_pool_release(pool); + + return 0; +} + + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_atomic_test; +#endif /* INCLUDE_ATOMIC_TEST */ + diff --git a/pjlib/src/pjlib-test/echo_clt.c b/pjlib/src/pjlib-test/echo_clt.c index 565d5607c..7a31f0d97 100644 --- a/pjlib/src/pjlib-test/echo_clt.c +++ b/pjlib/src/pjlib-test/echo_clt.c @@ -1,267 +1,277 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/echo_clt.c 3 10/29/05 10:25p Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/echo_clt.c $ - * - * 3 10/29/05 10:25p Bennylp - * Tested. - * - * 2 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 1 10/24/05 11:28a Bennylp - * Created. - * - */ -#include "test.h" -#include - -#if INCLUDE_ECHO_CLIENT - -enum { BUF_SIZE = 512 }; - -struct client -{ - int sock_type; - const char *server; - int port; -}; - -static pj_sem_t *sem; -static pj_mutex_t *mutex; -static pj_size_t total_bw; -static unsigned total_poster; -static pj_time_val first_report; - -#define MSEC_PRINT_DURATION 1000 - -static int wait_socket(pj_sock_t sock, unsigned msec_timeout) -{ - pj_fd_set_t fdset; - pj_time_val timeout; - - timeout.sec = 0; - timeout.msec = msec_timeout; - pj_time_val_normalize(&timeout); - - PJ_FD_ZERO(&fdset); - PJ_FD_SET(sock, &fdset); - - return pj_sock_select(1, &fdset, NULL, NULL, &timeout); -} - -static int echo_client_thread(void *arg) -{ - pj_sock_t sock; - char send_buf[BUF_SIZE]; - char recv_buf[BUF_SIZE]; - pj_sockaddr_in addr; - pj_str_t s; - pj_status_t rc; - struct client *client = arg; - pj_status_t last_recv_err = PJ_SUCCESS, last_send_err = PJ_SUCCESS; - - pj_time_val last_report, next_report; - pj_size_t thread_total; - - rc = app_socket(PJ_AF_INET, client->sock_type, 0, -1, &sock); - if (rc != PJ_SUCCESS) { - app_perror("...unable to create socket", rc); - return -10; - } - - rc = pj_sockaddr_in_init( &addr, pj_cstr(&s, client->server), - (pj_uint16_t)client->port); - if (rc != PJ_SUCCESS) { - app_perror("...unable to resolve server", rc); - return -15; - } - - rc = pj_sock_connect(sock, &addr, sizeof(addr)); - if (rc != PJ_SUCCESS) { - app_perror("...connect() error", rc); - pj_sock_close(sock); - return -20; - } - - pj_create_random_string(send_buf, BUF_SIZE); - thread_total = 0; - - /* Give other thread chance to initialize themselves! */ - pj_thread_sleep(500); - - pj_gettimeofday(&last_report); - next_report = first_report; - - //PJ_LOG(3,("", "...thread %p running", pj_thread_this())); - - for (;;) { - int rc; - pj_ssize_t bytes; - pj_time_val now; - - /* Send a packet. */ - bytes = BUF_SIZE; - rc = pj_sock_send(sock, send_buf, &bytes, 0); - if (rc != PJ_SUCCESS || bytes != BUF_SIZE) { - if (rc != last_send_err) { - app_perror("...send() error", rc); - PJ_LOG(3,("", "...ignoring subsequent error..")); - last_send_err = rc; - pj_thread_sleep(100); - } - continue; - } - - rc = wait_socket(sock, 500); - if (rc == 0) { - PJ_LOG(3,("", "...timeout")); - } else { - /* Receive back the original packet. */ - bytes = 0; - do { - pj_ssize_t received = BUF_SIZE - bytes; - rc = pj_sock_recv(sock, recv_buf+bytes, &received, 0); - if (rc != PJ_SUCCESS || received == 0) { - if (rc != last_recv_err) { - app_perror("...recv() error", rc); - PJ_LOG(3,("", "...ignoring subsequent error..")); - last_recv_err = rc; - pj_thread_sleep(100); - } - bytes = 0; - break; - } - bytes += received; - } while (bytes != BUF_SIZE); - } - - /* Accumulate total received. */ - thread_total = thread_total + bytes; - - /* Report current bandwidth on due. */ - pj_gettimeofday(&now); - - if (PJ_TIME_VAL_GTE(now, next_report)) { - pj_uint32_t bw; - pj_bool_t signal_parent = 0; - pj_time_val duration; - pj_uint32_t msec; - - duration = now; - PJ_TIME_VAL_SUB(duration, last_report); - msec = PJ_TIME_VAL_MSEC(duration); - - bw = thread_total * 1000 / msec; - - /* Post result to parent */ - pj_mutex_lock(mutex); - total_bw += bw; - total_poster++; - //PJ_LOG(3,("", "...thread %p posting result", pj_thread_this())); - if (total_poster >= ECHO_CLIENT_MAX_THREADS) - signal_parent = 1; - pj_mutex_unlock(mutex); - - thread_total = 0; - last_report = now; - next_report.sec++; - - if (signal_parent) { - pj_sem_post(sem); - } - - pj_thread_sleep(0); - } - - if (bytes == 0) - continue; - - if (pj_memcmp(send_buf, recv_buf, BUF_SIZE) != 0) { - PJ_LOG(3,("", "...error: buffer has changed!")); - break; - } - } - - pj_sock_close(sock); - return 0; -} - -int echo_client(int sock_type, const char *server, int port) -{ - pj_pool_t *pool; - pj_thread_t *thread[ECHO_CLIENT_MAX_THREADS]; - pj_status_t rc; - struct client client; - int i; - - client.sock_type = sock_type; - client.server = server; - client.port = port; - - pool = pj_pool_create( mem, NULL, 4000, 4000, NULL ); - - rc = pj_sem_create(pool, NULL, 0, ECHO_CLIENT_MAX_THREADS+1, &sem); - if (rc != PJ_SUCCESS) { - PJ_LOG(3,("", "...error: unable to create semaphore", rc)); - return -10; - } - - rc = pj_mutex_create_simple(pool, NULL, &mutex); - if (rc != PJ_SUCCESS) { - PJ_LOG(3,("", "...error: unable to create mutex", rc)); - return -20; - } - - /* - rc = pj_atomic_create(pool, 0, &atom); - if (rc != PJ_SUCCESS) { - PJ_LOG(3,("", "...error: unable to create atomic variable", rc)); - return -30; - } - */ - - PJ_LOG(3,("", "Echo client started")); - PJ_LOG(3,("", " Destination: %s:%d", - ECHO_SERVER_ADDRESS, ECHO_SERVER_START_PORT)); - PJ_LOG(3,("", " Press Ctrl-C to exit")); - - pj_gettimeofday(&first_report); - first_report.sec += 2; - - for (i=0; i + +#if INCLUDE_ECHO_CLIENT + +enum { BUF_SIZE = 512 }; + +struct client +{ + int sock_type; + const char *server; + int port; +}; + +static pj_sem_t *sem; +static pj_mutex_t *mutex; +static pj_size_t total_bw; +static unsigned total_poster; +static pj_time_val first_report; + +#define MSEC_PRINT_DURATION 1000 + +static int wait_socket(pj_sock_t sock, unsigned msec_timeout) +{ + pj_fd_set_t fdset; + pj_time_val timeout; + + timeout.sec = 0; + timeout.msec = msec_timeout; + pj_time_val_normalize(&timeout); + + PJ_FD_ZERO(&fdset); + PJ_FD_SET(sock, &fdset); + + return pj_sock_select(FD_SETSIZE, &fdset, NULL, NULL, &timeout); +} + +static int echo_client_thread(void *arg) +{ + pj_sock_t sock; + char send_buf[BUF_SIZE]; + char recv_buf[BUF_SIZE]; + pj_sockaddr_in addr; + pj_str_t s; + pj_status_t rc; + struct client *client = arg; + pj_status_t last_recv_err = PJ_SUCCESS, last_send_err = PJ_SUCCESS; + + pj_time_val last_report, next_report; + pj_size_t thread_total; + + rc = app_socket(PJ_AF_INET, client->sock_type, 0, -1, &sock); + if (rc != PJ_SUCCESS) { + app_perror("...unable to create socket", rc); + return -10; + } + + rc = pj_sockaddr_in_init( &addr, pj_cstr(&s, client->server), + (pj_uint16_t)client->port); + if (rc != PJ_SUCCESS) { + app_perror("...unable to resolve server", rc); + return -15; + } + + rc = pj_sock_connect(sock, &addr, sizeof(addr)); + if (rc != PJ_SUCCESS) { + app_perror("...connect() error", rc); + pj_sock_close(sock); + return -20; + } + + PJ_LOG(3,("", "...socket connected to %s:%d", + pj_inet_ntoa(addr.sin_addr), + pj_ntohs(addr.sin_port))); + + pj_create_random_string(send_buf, BUF_SIZE); + thread_total = 0; + + /* Give other thread chance to initialize themselves! */ + pj_thread_sleep(500); + + pj_gettimeofday(&last_report); + next_report = first_report; + + //PJ_LOG(3,("", "...thread %p running", pj_thread_this())); + + for (;;) { + int rc; + pj_ssize_t bytes; + pj_time_val now; + + /* Send a packet. */ + bytes = BUF_SIZE; + rc = pj_sock_send(sock, send_buf, &bytes, 0); + if (rc != PJ_SUCCESS || bytes != BUF_SIZE) { + if (rc != last_send_err) { + app_perror("...send() error", rc); + PJ_LOG(3,("", "...ignoring subsequent error..")); + last_send_err = rc; + pj_thread_sleep(100); + } + continue; + } + + rc = wait_socket(sock, 500); + if (rc == 0) { + PJ_LOG(3,("", "...timeout")); + bytes = 0; + } else if (rc < 0) { + rc = pj_get_netos_error(); + app_perror("...select() error", rc); + break; + } else { + /* Receive back the original packet. */ + bytes = 0; + do { + pj_ssize_t received = BUF_SIZE - bytes; + rc = pj_sock_recv(sock, recv_buf+bytes, &received, 0); + if (rc != PJ_SUCCESS || received == 0) { + if (rc != last_recv_err) { + app_perror("...recv() error", rc); + PJ_LOG(3,("", "...ignoring subsequent error..")); + last_recv_err = rc; + pj_thread_sleep(100); + } + bytes = 0; + received = 0; + break; + } + bytes += received; + } while (bytes != BUF_SIZE && bytes != 0); + } + + /* Accumulate total received. */ + thread_total = thread_total + bytes; + + /* Report current bandwidth on due. */ + pj_gettimeofday(&now); + + if (PJ_TIME_VAL_GTE(now, next_report)) { + pj_uint32_t bw; + pj_bool_t signal_parent = 0; + pj_time_val duration; + pj_uint32_t msec; + + duration = now; + PJ_TIME_VAL_SUB(duration, last_report); + msec = PJ_TIME_VAL_MSEC(duration); + + bw = thread_total * 1000 / msec; + + /* Post result to parent */ + pj_mutex_lock(mutex); + total_bw += bw; + total_poster++; + //PJ_LOG(3,("", "...thread %p posting result", pj_thread_this())); + if (total_poster >= ECHO_CLIENT_MAX_THREADS) + signal_parent = 1; + pj_mutex_unlock(mutex); + + thread_total = 0; + last_report = now; + next_report.sec++; + + if (signal_parent) { + pj_sem_post(sem); + } + + pj_thread_sleep(0); + } + + if (bytes == 0) + continue; + + if (pj_memcmp(send_buf, recv_buf, BUF_SIZE) != 0) { + //PJ_LOG(3,("", "...error: buffer has changed!")); + break; + } + } + + pj_sock_close(sock); + return 0; +} + +int echo_client(int sock_type, const char *server, int port) +{ + pj_pool_t *pool; + pj_thread_t *thread[ECHO_CLIENT_MAX_THREADS]; + pj_status_t rc; + struct client client; + int i; + + client.sock_type = sock_type; + client.server = server; + client.port = port; + + pool = pj_pool_create( mem, NULL, 4000, 4000, NULL ); + + rc = pj_sem_create(pool, NULL, 0, ECHO_CLIENT_MAX_THREADS+1, &sem); + if (rc != PJ_SUCCESS) { + PJ_LOG(3,("", "...error: unable to create semaphore", rc)); + return -10; + } + + rc = pj_mutex_create_simple(pool, NULL, &mutex); + if (rc != PJ_SUCCESS) { + PJ_LOG(3,("", "...error: unable to create mutex", rc)); + return -20; + } + + /* + rc = pj_atomic_create(pool, 0, &atom); + if (rc != PJ_SUCCESS) { + PJ_LOG(3,("", "...error: unable to create atomic variable", rc)); + return -30; + } + */ + + PJ_LOG(3,("", "Echo client started")); + PJ_LOG(3,("", " Destination: %s:%d", + ECHO_SERVER_ADDRESS, ECHO_SERVER_START_PORT)); + PJ_LOG(3,("", " Press Ctrl-C to exit")); + + pj_gettimeofday(&first_report); + first_report.sec += 2; + + for (i=0; i -#include - -#if INCLUDE_ECHO_SERVER - -static pj_bool_t thread_quit_flag; - -struct server -{ - pj_pool_t *pool; - int sock_type; - int thread_count; - pj_ioqueue_t *ioqueue; - pj_sock_t sock; - pj_sock_t client_sock; - pj_ioqueue_key_t *key; - pj_ioqueue_callback cb; - char *buf; - pj_size_t bufsize; - pj_sockaddr_in addr; - int addrlen; - pj_size_t bytes_recv; - pj_timestamp start_time; -}; - -static void on_read_complete(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) -{ - struct server *server = pj_ioqueue_get_user_data(key); - pj_status_t rc; - - if (server->sock_type == PJ_SOCK_DGRAM) { - if (bytes_read > 0) { - /* Send data back to sender. */ - rc = pj_ioqueue_sendto( server->ioqueue, server->key, - server->buf, bytes_read, 0, - &server->addr, server->addrlen); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...sendto() error", rc); - } - } else { - PJ_LOG(3,("", "...read error (bytes_read=%d)", bytes_read)); - } - - /* Start next receive. */ - rc = pj_ioqueue_recvfrom( server->ioqueue, server->key, - server->buf, server->bufsize, 0, - &server->addr, &server->addrlen); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...recvfrom() error", rc); - } - - } - else if (server->sock_type == PJ_SOCK_STREAM) { - if (bytes_read > 0) { - /* Send data back to sender. */ - rc = pj_ioqueue_send( server->ioqueue, server->key, - server->buf, bytes_read, 0); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...send() error", rc); - bytes_read = 0; - } - } - - if (bytes_read <= 0) { - PJ_LOG(3,("", "...tcp closed")); - pj_ioqueue_unregister( server->ioqueue, server->key ); - pj_sock_close( server->sock ); - pj_pool_release( server->pool ); - return; - } - - /* Start next receive. */ - rc = pj_ioqueue_recv( server->ioqueue, server->key, - server->buf, server->bufsize, 0); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...recv() error", rc); - } - } - - /* Add counter. */ - if (bytes_read > 0) { - if (server->bytes_recv == 0) { - pj_get_timestamp(&server->start_time); - server->bytes_recv += bytes_read; - } else { - enum { USECS_IN_SECOND = 1000000 }; - pj_timestamp now; - pj_uint32_t usec_elapsed; - - server->bytes_recv += bytes_read; - - pj_get_timestamp(&now); - usec_elapsed = pj_elapsed_usec(&server->start_time, &now); - if (usec_elapsed > USECS_IN_SECOND) { - if (usec_elapsed < 2 * USECS_IN_SECOND) { - pj_highprec_t bw; - pj_uint32_t bw32; - const char *type_name; - - /* bandwidth(bw) = server->bytes_recv * USECS/elapsed */ - bw = server->bytes_recv; - pj_highprec_mul(bw, USECS_IN_SECOND); - pj_highprec_div(bw, usec_elapsed); - - bw32 = (pj_uint32_t) bw; - - if (server->sock_type==PJ_SOCK_STREAM) - type_name = "tcp"; - else if (server->sock_type==PJ_SOCK_DGRAM) - type_name = "udp"; - else - type_name = "???"; - - PJ_LOG(3,("", - "...[%s:%d (%d threads)] Current bandwidth=%u KBps", - type_name, - ECHO_SERVER_START_PORT+server->thread_count, - server->thread_count, - bw32/1024)); - } - server->start_time = now; - server->bytes_recv = 0; - } - } - } -} - -static void on_accept_complete( pj_ioqueue_key_t *key, pj_sock_t sock, - int status) -{ - struct server *server_server = pj_ioqueue_get_user_data(key); - pj_status_t rc; - - PJ_UNUSED_ARG(sock); - - if (status == 0) { - pj_pool_t *pool; - struct server *new_server; - - pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); - new_server = pj_pool_zalloc(pool, sizeof(struct server)); - - new_server->pool = pool; - new_server->ioqueue = server_server->ioqueue; - new_server->sock_type = server_server->sock_type; - new_server->thread_count = server_server->thread_count; - new_server->sock = server_server->client_sock; - new_server->bufsize = 4096; - new_server->buf = pj_pool_alloc(pool, new_server->bufsize); - new_server->cb = server_server->cb; - - rc = pj_ioqueue_register_sock( new_server->pool, new_server->ioqueue, - new_server->sock, new_server, - &server_server->cb, &new_server->key); - if (rc != PJ_SUCCESS) { - app_perror("...registering new tcp sock", rc); - pj_sock_close(new_server->sock); - pj_pool_release(pool); - thread_quit_flag = 1; - return; - } - - rc = pj_ioqueue_recv( new_server->ioqueue, new_server->key, - new_server->buf, new_server->bufsize, 0); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...recv() error", rc); - pj_sock_close(new_server->sock); - pj_pool_release(pool); - thread_quit_flag = 1; - return; - } - } - - rc = pj_ioqueue_accept( server_server->ioqueue, server_server->key, - &server_server->client_sock, - NULL, NULL, NULL); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...accept() error", rc); - thread_quit_flag = 1; - } -} - -static int thread_proc(void *arg) -{ - pj_ioqueue_t *ioqueue = arg; - - while (!thread_quit_flag) { - pj_time_val timeout; - int count; - - timeout.sec = 0; timeout.msec = 50; - count = pj_ioqueue_poll( ioqueue, &timeout ); - if (count > 0) { - count = 0; - } - } - - return 0; -} - -static int start_echo_server( int sock_type, int port, int thread_count ) -{ - pj_pool_t *pool; - struct server *server; - int i; - pj_status_t rc; - - - pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); - if (!pool) - return -10; - - server = pj_pool_zalloc(pool, sizeof(struct server)); - - server->sock_type = sock_type; - server->thread_count = thread_count; - server->cb.on_read_complete = &on_read_complete; - server->cb.on_accept_complete = &on_accept_complete; - - /* create ioqueue */ - rc = pj_ioqueue_create( pool, 32, thread_count, &server->ioqueue); - if (rc != PJ_SUCCESS) { - app_perror("...error creating ioqueue", rc); - return -20; - } - - /* create and register socket to ioqueue. */ - rc = app_socket(PJ_AF_INET, sock_type, 0, port, &server->sock); - if (rc != PJ_SUCCESS) { - app_perror("...error initializing socket", rc); - return -30; - } - - rc = pj_ioqueue_register_sock( pool, server->ioqueue, - server->sock, - server, &server->cb, - &server->key); - if (rc != PJ_SUCCESS) { - app_perror("...error registering socket to ioqueue", rc); - return -40; - } - - /* create receive buffer. */ - server->bufsize = 4096; - server->buf = pj_pool_alloc(pool, server->bufsize); - - if (sock_type == PJ_SOCK_DGRAM) { - server->addrlen = sizeof(server->addr); - rc = pj_ioqueue_recvfrom( server->ioqueue, server->key, - server->buf, server->bufsize, - 0, - &server->addr, &server->addrlen); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...read error", rc); - return -50; - } - } else { - rc = pj_ioqueue_accept( server->ioqueue, server->key, - &server->client_sock, NULL, NULL, NULL ); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...accept() error", rc); - return -60; - } - } - - /* create threads. */ - - for (i=0; iioqueue, - PJ_THREAD_DEFAULT_STACK_SIZE, 0, &thread); - if (rc != PJ_SUCCESS) { - app_perror("...unable to create thread", rc); - return -70; - } - } - - /* Done. */ - return PJ_SUCCESS; -} - -int echo_server(void) -{ - enum { MAX_THREADS = 4 }; - int sock_types[2]; - int i, j, rc; - - sock_types[0] = PJ_SOCK_DGRAM; - sock_types[1] = PJ_SOCK_STREAM; - - for (i=0; i<2; ++i) { - for (j=0; j +#include + +#if INCLUDE_ECHO_SERVER + +static pj_bool_t thread_quit_flag; + +struct server +{ + pj_pool_t *pool; + int sock_type; + int thread_count; + pj_ioqueue_t *ioqueue; + pj_sock_t sock; + pj_sock_t client_sock; + pj_ioqueue_key_t *key; + pj_ioqueue_callback cb; + char *buf; + pj_size_t bufsize; + pj_sockaddr_in addr; + int addrlen; + pj_size_t bytes_recv; + pj_timestamp start_time; +}; + +static void on_read_complete(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) +{ + struct server *server = pj_ioqueue_get_user_data(key); + pj_status_t rc; + + if (server->sock_type == PJ_SOCK_DGRAM) { + if (bytes_read > 0) { + /* Send data back to sender. */ + rc = pj_ioqueue_sendto( server->ioqueue, server->key, + server->buf, bytes_read, 0, + &server->addr, server->addrlen); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...sendto() error", rc); + } + } else { + PJ_LOG(3,("", "...read error (bytes_read=%d)", bytes_read)); + } + + /* Start next receive. */ + rc = pj_ioqueue_recvfrom( server->ioqueue, server->key, + server->buf, server->bufsize, 0, + &server->addr, &server->addrlen); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...recvfrom() error", rc); + } + + } + else if (server->sock_type == PJ_SOCK_STREAM) { + if (bytes_read > 0) { + /* Send data back to sender. */ + rc = pj_ioqueue_send( server->ioqueue, server->key, + server->buf, bytes_read, 0); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...send() error", rc); + bytes_read = 0; + } + } + + if (bytes_read <= 0) { + PJ_LOG(3,("", "...tcp closed")); + pj_ioqueue_unregister( server->ioqueue, server->key ); + pj_sock_close( server->sock ); + pj_pool_release( server->pool ); + return; + } + + /* Start next receive. */ + rc = pj_ioqueue_recv( server->ioqueue, server->key, + server->buf, server->bufsize, 0); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...recv() error", rc); + } + } + + /* Add counter. */ + if (bytes_read > 0) { + if (server->bytes_recv == 0) { + pj_get_timestamp(&server->start_time); + server->bytes_recv += bytes_read; + } else { + enum { USECS_IN_SECOND = 1000000 }; + pj_timestamp now; + pj_uint32_t usec_elapsed; + + server->bytes_recv += bytes_read; + + pj_get_timestamp(&now); + usec_elapsed = pj_elapsed_usec(&server->start_time, &now); + if (usec_elapsed > USECS_IN_SECOND) { + if (usec_elapsed < 2 * USECS_IN_SECOND) { + pj_highprec_t bw; + pj_uint32_t bw32; + const char *type_name; + + /* bandwidth(bw) = server->bytes_recv * USECS/elapsed */ + bw = server->bytes_recv; + pj_highprec_mul(bw, USECS_IN_SECOND); + pj_highprec_div(bw, usec_elapsed); + + bw32 = (pj_uint32_t) bw; + + if (server->sock_type==PJ_SOCK_STREAM) + type_name = "tcp"; + else if (server->sock_type==PJ_SOCK_DGRAM) + type_name = "udp"; + else + type_name = "???"; + + PJ_LOG(3,("", + "...[%s:%d (%d threads)] Current bandwidth=%u KBps", + type_name, + ECHO_SERVER_START_PORT+server->thread_count, + server->thread_count, + bw32/1024)); + } + server->start_time = now; + server->bytes_recv = 0; + } + } + } +} + +static void on_accept_complete( pj_ioqueue_key_t *key, pj_sock_t sock, + int status) +{ + struct server *server_server = pj_ioqueue_get_user_data(key); + pj_status_t rc; + + PJ_UNUSED_ARG(sock); + + if (status == 0) { + pj_pool_t *pool; + struct server *new_server; + + pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); + new_server = pj_pool_zalloc(pool, sizeof(struct server)); + + new_server->pool = pool; + new_server->ioqueue = server_server->ioqueue; + new_server->sock_type = server_server->sock_type; + new_server->thread_count = server_server->thread_count; + new_server->sock = server_server->client_sock; + new_server->bufsize = 4096; + new_server->buf = pj_pool_alloc(pool, new_server->bufsize); + new_server->cb = server_server->cb; + + rc = pj_ioqueue_register_sock( new_server->pool, new_server->ioqueue, + new_server->sock, new_server, + &server_server->cb, &new_server->key); + if (rc != PJ_SUCCESS) { + app_perror("...registering new tcp sock", rc); + pj_sock_close(new_server->sock); + pj_pool_release(pool); + thread_quit_flag = 1; + return; + } + + rc = pj_ioqueue_recv( new_server->ioqueue, new_server->key, + new_server->buf, new_server->bufsize, 0); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...recv() error", rc); + pj_sock_close(new_server->sock); + pj_pool_release(pool); + thread_quit_flag = 1; + return; + } + } + + rc = pj_ioqueue_accept( server_server->ioqueue, server_server->key, + &server_server->client_sock, + NULL, NULL, NULL); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...accept() error", rc); + thread_quit_flag = 1; + } +} + +static int thread_proc(void *arg) +{ + pj_ioqueue_t *ioqueue = arg; + + while (!thread_quit_flag) { + pj_time_val timeout; + int count; + + timeout.sec = 0; timeout.msec = 50; + count = pj_ioqueue_poll( ioqueue, &timeout ); + if (count > 0) { + count = 0; + } + } + + return 0; +} + +static int start_echo_server( int sock_type, int port, int thread_count ) +{ + pj_pool_t *pool; + struct server *server; + int i; + pj_status_t rc; + + + pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); + if (!pool) + return -10; + + server = pj_pool_zalloc(pool, sizeof(struct server)); + + server->sock_type = sock_type; + server->thread_count = thread_count; + server->cb.on_read_complete = &on_read_complete; + server->cb.on_accept_complete = &on_accept_complete; + + /* create ioqueue */ + rc = pj_ioqueue_create( pool, 32, thread_count, &server->ioqueue); + if (rc != PJ_SUCCESS) { + app_perror("...error creating ioqueue", rc); + return -20; + } + + /* create and register socket to ioqueue. */ + rc = app_socket(PJ_AF_INET, sock_type, 0, port, &server->sock); + if (rc != PJ_SUCCESS) { + app_perror("...error initializing socket", rc); + return -30; + } + + rc = pj_ioqueue_register_sock( pool, server->ioqueue, + server->sock, + server, &server->cb, + &server->key); + if (rc != PJ_SUCCESS) { + app_perror("...error registering socket to ioqueue", rc); + return -40; + } + + /* create receive buffer. */ + server->bufsize = 4096; + server->buf = pj_pool_alloc(pool, server->bufsize); + + if (sock_type == PJ_SOCK_DGRAM) { + server->addrlen = sizeof(server->addr); + rc = pj_ioqueue_recvfrom( server->ioqueue, server->key, + server->buf, server->bufsize, + 0, + &server->addr, &server->addrlen); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...read error", rc); + return -50; + } + } else { + rc = pj_ioqueue_accept( server->ioqueue, server->key, + &server->client_sock, NULL, NULL, NULL ); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...accept() error", rc); + return -60; + } + } + + /* create threads. */ + + for (i=0; iioqueue, + PJ_THREAD_DEFAULT_STACK_SIZE, 0, &thread); + if (rc != PJ_SUCCESS) { + app_perror("...unable to create thread", rc); + return -70; + } + } + + /* Done. */ + return PJ_SUCCESS; +} + +int echo_server(void) +{ + enum { MAX_THREADS = 4 }; + int sock_types[2]; + int i, j, rc; + + sock_types[0] = PJ_SOCK_DGRAM; + sock_types[1] = PJ_SOCK_STREAM; + + for (i=0; i<2; ++i) { + for (j=0; j -#include -#include -#include -#include - -#if INCLUDE_ERRNO_TEST - -#define THIS_FILE "errno" - -#if defined(PJ_WIN32) && PJ_WIN32 != 0 -# include -#endif - -#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0 -# include -#endif - -static void trim_newlines(char *s) -{ - while (*s) { - if (*s == '\r' || *s == '\n') - *s = ' '; - ++s; - } -} - -int my_strncasecmp(const char *s1, const char *s2, int max_len) -{ - while (*s1 && *s2 && max_len > 0) { - if (pj_tolower(*s1) != pj_tolower(*s2)) - return -1; - ++s1; - ++s2; - --max_len; - } - return 0; -} - -const char *my_stristr(const char *whole, const char *part) -{ - int part_len = strlen(part); - while (*whole) { - if (my_strncasecmp(whole, part, part_len) == 0) - return whole; - ++whole; - } - return NULL; -} - -int errno_test(void) -{ - enum { CUT = 6 }; - pj_status_t rc; - char errbuf[256]; - - PJ_LOG(3,(THIS_FILE, "...errno test: check the msg carefully")); - - /* - * Windows platform error. - */ -# ifdef ERROR_INVALID_DATA - rc = PJ_STATUS_FROM_OS(ERROR_INVALID_DATA); - pj_set_os_error(rc); - - /* Whole */ - pj_strerror(rc, errbuf, sizeof(errbuf)); - trim_newlines(errbuf); - PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA: '%s'", errbuf)); - if (my_stristr(errbuf, "invalid") == NULL) { - PJ_LOG(3, (THIS_FILE, - "...error: expecting \"invalid\" string in the msg")); - return -20; - } - - /* Cut version. */ - pj_strerror(rc, errbuf, CUT); - PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA (cut): '%s'", errbuf)); -# endif - - /* - * Unix errors - */ -# ifdef EINVAL - rc = PJ_STATUS_FROM_OS(EINVAL); - pj_set_os_error(rc); - - /* Whole */ - pj_strerror(rc, errbuf, sizeof(errbuf)); - trim_newlines(errbuf); - PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL: '%s'", errbuf)); - if (my_stristr(errbuf, "invalid") == NULL) { - PJ_LOG(3, (THIS_FILE, - "...error: expecting \"invalid\" string in the msg")); - return -30; - } - - /* Cut */ - pj_strerror(rc, errbuf, CUT); - PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL (cut): '%s'", errbuf)); -# endif - - /* - * Windows WSA errors - */ -# ifdef WSAEINVAL - rc = PJ_STATUS_FROM_OS(WSAEINVAL); - pj_set_os_error(rc); - - /* Whole */ - pj_strerror(rc, errbuf, sizeof(errbuf)); - trim_newlines(errbuf); - PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL: '%s'", errbuf)); - if (my_stristr(errbuf, "invalid") == NULL) { - PJ_LOG(3, (THIS_FILE, - "...error: expecting \"invalid\" string in the msg")); - return -40; - } - - /* Cut */ - pj_strerror(rc, errbuf, CUT); - PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL (cut): '%s'", errbuf)); -# endif - - pj_strerror(PJ_EBUG, errbuf, sizeof(errbuf)); - PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG: '%s'", errbuf)); - if (my_stristr(errbuf, "BUG") == NULL) { - PJ_LOG(3, (THIS_FILE, - "...error: expecting \"BUG\" string in the msg")); - return -20; - } - - pj_strerror(PJ_EBUG, errbuf, CUT); - PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG, cut at %d chars: '%s'", - CUT, errbuf)); - - return 0; -} - - -#endif /* INCLUDE_ERRNO_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/errno.c 4 10/14/05 3:05p Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/errno.c $ + * + * 4 10/14/05 3:05p Bennylp + * Fixed warning about strlen() on Linux. + * + * 3 14/10/05 11:30 Bennylp + * Verify the error message. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/09/05 9:56p Bennylp + * Created. + * + */ +#include "test.h" +#include +#include +#include +#include +#include + +#if INCLUDE_ERRNO_TEST + +#define THIS_FILE "errno" + +#if defined(PJ_WIN32) && PJ_WIN32 != 0 +# include +#endif + +#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0 +# include +#endif + +static void trim_newlines(char *s) +{ + while (*s) { + if (*s == '\r' || *s == '\n') + *s = ' '; + ++s; + } +} + +int my_strncasecmp(const char *s1, const char *s2, int max_len) +{ + while (*s1 && *s2 && max_len > 0) { + if (pj_tolower(*s1) != pj_tolower(*s2)) + return -1; + ++s1; + ++s2; + --max_len; + } + return 0; +} + +const char *my_stristr(const char *whole, const char *part) +{ + int part_len = strlen(part); + while (*whole) { + if (my_strncasecmp(whole, part, part_len) == 0) + return whole; + ++whole; + } + return NULL; +} + +int errno_test(void) +{ + enum { CUT = 6 }; + pj_status_t rc; + char errbuf[256]; + + PJ_LOG(3,(THIS_FILE, "...errno test: check the msg carefully")); + + /* + * Windows platform error. + */ +# ifdef ERROR_INVALID_DATA + rc = PJ_STATUS_FROM_OS(ERROR_INVALID_DATA); + pj_set_os_error(rc); + + /* Whole */ + pj_strerror(rc, errbuf, sizeof(errbuf)); + trim_newlines(errbuf); + PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA: '%s'", errbuf)); + if (my_stristr(errbuf, "invalid") == NULL) { + PJ_LOG(3, (THIS_FILE, + "...error: expecting \"invalid\" string in the msg")); + return -20; + } + + /* Cut version. */ + pj_strerror(rc, errbuf, CUT); + PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA (cut): '%s'", errbuf)); +# endif + + /* + * Unix errors + */ +# ifdef EINVAL + rc = PJ_STATUS_FROM_OS(EINVAL); + pj_set_os_error(rc); + + /* Whole */ + pj_strerror(rc, errbuf, sizeof(errbuf)); + trim_newlines(errbuf); + PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL: '%s'", errbuf)); + if (my_stristr(errbuf, "invalid") == NULL) { + PJ_LOG(3, (THIS_FILE, + "...error: expecting \"invalid\" string in the msg")); + return -30; + } + + /* Cut */ + pj_strerror(rc, errbuf, CUT); + PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL (cut): '%s'", errbuf)); +# endif + + /* + * Windows WSA errors + */ +# ifdef WSAEINVAL + rc = PJ_STATUS_FROM_OS(WSAEINVAL); + pj_set_os_error(rc); + + /* Whole */ + pj_strerror(rc, errbuf, sizeof(errbuf)); + trim_newlines(errbuf); + PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL: '%s'", errbuf)); + if (my_stristr(errbuf, "invalid") == NULL) { + PJ_LOG(3, (THIS_FILE, + "...error: expecting \"invalid\" string in the msg")); + return -40; + } + + /* Cut */ + pj_strerror(rc, errbuf, CUT); + PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL (cut): '%s'", errbuf)); +# endif + + pj_strerror(PJ_EBUG, errbuf, sizeof(errbuf)); + PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG: '%s'", errbuf)); + if (my_stristr(errbuf, "BUG") == NULL) { + PJ_LOG(3, (THIS_FILE, + "...error: expecting \"BUG\" string in the msg")); + return -20; + } + + pj_strerror(PJ_EBUG, errbuf, CUT); + PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG, cut at %d chars: '%s'", + CUT, errbuf)); + + return 0; +} + + +#endif /* INCLUDE_ERRNO_TEST */ + + diff --git a/pjlib/src/pjlib-test/exception.c b/pjlib/src/pjlib-test/exception.c index 2fe62e6e1..4a7036073 100644 --- a/pjlib/src/pjlib-test/exception.c +++ b/pjlib/src/pjlib-test/exception.c @@ -1,156 +1,156 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/exception.c 2 10/14/05 12:26a Bennylp $ - */ -#include "test.h" - - -/** - * \page page_pjlib_exception_test Test: Exception Handling - * - * This file provides implementation of \b exception_test(). It tests the - * functionality of the exception handling API. - * - * @note This test use static ID not acquired through proper registration. - * This is not recommended, since it may create ID collissions. - * - * \section exception_test_sec Scope of the Test - * - * Some scenarios tested: - * - no exception situation - * - basic TRY/CATCH - * - multiple exception handlers - * - default handlers - * - * - * This file is pjlib-test/exception.c - * - * \include pjlib-test/exception.c - */ - - -#if INCLUDE_EXCEPTION_TEST - -#include - -#define ID_1 1 -#define ID_2 2 - -static int throw_id_1(void) -{ - PJ_THROW( ID_1 ); - return -1; -} - -static int throw_id_2(void) -{ - PJ_THROW( ID_2 ); - return -1; -} - - -static int test(void) -{ - PJ_USE_EXCEPTION; - int rc = 0; - - /* - * No exception situation. - */ - PJ_TRY { - rc = rc; - } - PJ_CATCH( ID_1 ) { - rc = -2; - } - PJ_DEFAULT { - rc = -3; - } - PJ_END; - - if (rc != 0) - return rc; - - - /* - * Basic TRY/CATCH - */ - PJ_TRY { - rc = throw_id_1(); - - // should not reach here. - rc = -10; - } - PJ_CATCH( ID_1 ) { - if (!rc) rc = 0; - } - PJ_DEFAULT { - if (!rc) rc = -20; - } - PJ_END; - - if (rc != 0) - return rc; - - /* - * Multiple exceptions handlers - */ - PJ_TRY { - rc = throw_id_2(); - // should not reach here. - rc = -25; - } - PJ_CATCH( ID_1 ) { - if (!rc) rc = -30; - } - PJ_CATCH( ID_2 ) { - if (!rc) rc = 0; - } - PJ_DEFAULT { - if (!rc) rc = -40; - } - PJ_END; - - if (rc != 0) - return rc; - - /* - * Test default handler. - */ - PJ_TRY { - rc = throw_id_1(); - // should not reach here - rc = -50; - } - PJ_CATCH( ID_2 ) { - if (!rc) rc = -60; - } - PJ_DEFAULT { - if (!rc) rc = 0; - } - PJ_END; - - if (rc != 0) - return rc; - - return 0; -} - -int exception_test(void) -{ - int i, rc; - enum { LOOP = 10 }; - - for (i=0; ipjlib-test/exception.c + * + * \include pjlib-test/exception.c + */ + + +#if INCLUDE_EXCEPTION_TEST + +#include + +#define ID_1 1 +#define ID_2 2 + +static int throw_id_1(void) +{ + PJ_THROW( ID_1 ); + return -1; +} + +static int throw_id_2(void) +{ + PJ_THROW( ID_2 ); + return -1; +} + + +static int test(void) +{ + PJ_USE_EXCEPTION; + int rc = 0; + + /* + * No exception situation. + */ + PJ_TRY { + rc = rc; + } + PJ_CATCH( ID_1 ) { + rc = -2; + } + PJ_DEFAULT { + rc = -3; + } + PJ_END; + + if (rc != 0) + return rc; + + + /* + * Basic TRY/CATCH + */ + PJ_TRY { + rc = throw_id_1(); + + // should not reach here. + rc = -10; + } + PJ_CATCH( ID_1 ) { + if (!rc) rc = 0; + } + PJ_DEFAULT { + if (!rc) rc = -20; + } + PJ_END; + + if (rc != 0) + return rc; + + /* + * Multiple exceptions handlers + */ + PJ_TRY { + rc = throw_id_2(); + // should not reach here. + rc = -25; + } + PJ_CATCH( ID_1 ) { + if (!rc) rc = -30; + } + PJ_CATCH( ID_2 ) { + if (!rc) rc = 0; + } + PJ_DEFAULT { + if (!rc) rc = -40; + } + PJ_END; + + if (rc != 0) + return rc; + + /* + * Test default handler. + */ + PJ_TRY { + rc = throw_id_1(); + // should not reach here + rc = -50; + } + PJ_CATCH( ID_2 ) { + if (!rc) rc = -60; + } + PJ_DEFAULT { + if (!rc) rc = 0; + } + PJ_END; + + if (rc != 0) + return rc; + + return 0; +} + +int exception_test(void) +{ + int i, rc; + enum { LOOP = 10 }; + + for (i=0; i - -int fifobuf_test() -{ - enum { SIZE = 1024, MAX_ENTRIES = 128, - MIN_SIZE = 4, MAX_SIZE = 64, - LOOP=10000 }; - pj_pool_t *pool; - pj_fifobuf_t fifo; - unsigned available = SIZE; - void *entries[MAX_ENTRIES]; - void *buffer; - int i; - - pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL); - if (!pool) - return -10; - - buffer = pj_pool_alloc(pool, SIZE); - if (!buffer) - return -20; - - pj_fifobuf_init (&fifo, buffer, SIZE); - - // Test 1 - for (i=0; i=MIN_SIZE+4 && count < MAX_ENTRIES;) { - int size = MIN_SIZE+(pj_rand() % MAX_SIZE); - entries[count] = pj_fifobuf_alloc (&fifo, size); - if (entries[count]) { - available -= (size+4); - ++count; - } - } - for (j=0; j + +int fifobuf_test() +{ + enum { SIZE = 1024, MAX_ENTRIES = 128, + MIN_SIZE = 4, MAX_SIZE = 64, + LOOP=10000 }; + pj_pool_t *pool; + pj_fifobuf_t fifo; + unsigned available = SIZE; + void *entries[MAX_ENTRIES]; + void *buffer; + int i; + + pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL); + if (!pool) + return -10; + + buffer = pj_pool_alloc(pool, SIZE); + if (!buffer) + return -20; + + pj_fifobuf_init (&fifo, buffer, SIZE); + + // Test 1 + for (i=0; i=MIN_SIZE+4 && count < MAX_ENTRIES;) { + int size = MIN_SIZE+(pj_rand() % MAX_SIZE); + entries[count] = pj_fifobuf_alloc (&fifo, size); + if (entries[count]) { + available -= (size+4); + ++count; + } + } + for (j=0; j -#include - -/** - * \page page_pjlib_ioqueue_perf_test Test: I/O Queue Performance - * - * Test the performance of the I/O queue, using typical producer - * consumer test. The test should examine the effect of using multiple - * threads on the performance. - * - * This file is pjlib-test/ioq_perf.c - * - * \include pjlib-test/ioq_perf.c - */ - -#if INCLUDE_IOQUEUE_PERF_TEST - -#ifdef _MSC_VER -# pragma warning ( disable: 4204) // non-constant aggregate initializer -#endif - -#define THIS_FILE "ioq_perf" -//#define TRACE_(expr) PJ_LOG(3,expr) -#define TRACE_(expr) - - -static pj_bool_t thread_quit_flag; -static pj_status_t last_error; -static unsigned last_error_counter; - -/* Descriptor for each producer/consumer pair. */ -typedef struct test_item -{ - pj_sock_t server_fd, - client_fd; - pj_ioqueue_t *ioqueue; - pj_ioqueue_key_t *server_key, - *client_key; - pj_size_t buffer_size; - char *outgoing_buffer; - char *incoming_buffer; - pj_size_t bytes_sent, - bytes_recv; -} test_item; - -/* Callback when data has been read. - * Increment item->bytes_recv and ready to read the next data. - */ -static void on_read_complete(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) -{ - test_item *item = pj_ioqueue_get_user_data(key); - pj_status_t rc; - - //TRACE_((THIS_FILE, " read complete, bytes_read=%d", bytes_read)); - - if (thread_quit_flag) - return; - - if (bytes_read < 0) { - pj_status_t rc = -bytes_read; - char errmsg[128]; - - if (rc != last_error) { - last_error = rc; - pj_strerror(rc, errmsg, sizeof(errmsg)); - PJ_LOG(3,(THIS_FILE, "...error: read error, bytes_read=%d (%s)", - bytes_read, errmsg)); - PJ_LOG(3,(THIS_FILE, - ".....additional info: total read=%u, total written=%u", - item->bytes_recv, item->bytes_sent)); - } else { - last_error_counter++; - } - bytes_read = 0; - - } else if (bytes_read == 0) { - PJ_LOG(3,(THIS_FILE, "...socket has closed!")); - } - - item->bytes_recv += bytes_read; - - /* To assure that the test quits, even if main thread - * doesn't have time to run. - */ - if (item->bytes_recv > item->buffer_size * 10000) - thread_quit_flag = 1; - - rc = pj_ioqueue_recv( item->ioqueue, item->server_key, - item->incoming_buffer, item->buffer_size, 0 ); - - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - if (rc != last_error) { - last_error = rc; - app_perror("...error: read error", rc); - } else { - last_error_counter++; - } - } -} - -/* Callback when data has been written. - * Increment item->bytes_sent and write the next data. - */ -static void on_write_complete(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent) -{ - test_item *item = pj_ioqueue_get_user_data(key); - - //TRACE_((THIS_FILE, " write complete: sent = %d", bytes_sent)); - - if (thread_quit_flag) - return; - - item->bytes_sent += bytes_sent; - - if (bytes_sent <= 0) { - PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d", - bytes_sent)); - } - else { - pj_status_t rc; - - rc = pj_ioqueue_write(item->ioqueue, item->client_key, - item->outgoing_buffer, item->buffer_size); - if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { - app_perror("...error: write error", rc); - } - } -} - -/* The worker thread. */ -static int worker_thread(void *arg) -{ - pj_ioqueue_t *ioqueue = arg; - const pj_time_val timeout = {0, 100}; - int rc; - - while (!thread_quit_flag) { - rc = pj_ioqueue_poll(ioqueue, &timeout); - //TRACE_((THIS_FILE, " thread: poll returned rc=%d", rc)); - if (rc < 0) { - app_perror("...error in pj_ioqueue_poll()", pj_get_netos_error()); - return -1; - } - } - return 0; -} - -/* Calculate the bandwidth for the specific test configuration. - * The test is simple: - * - create sockpair_cnt number of producer-consumer socket pair. - * - create thread_cnt number of worker threads. - * - each producer will send buffer_size bytes data as fast and - * as soon as it can. - * - each consumer will read buffer_size bytes of data as fast - * as it could. - * - measure the total bytes received by all consumers during a - * period of time. - */ -static int perform_test(int sock_type, const char *type_name, - unsigned thread_cnt, unsigned sockpair_cnt, - pj_size_t buffer_size, - pj_size_t *p_bandwidth) -{ - enum { MSEC_DURATION = 5000 }; - pj_pool_t *pool; - test_item *items; - pj_thread_t **thread; - pj_ioqueue_t *ioqueue; - pj_status_t rc; - pj_ioqueue_callback ioqueue_callback; - pj_uint32_t total_elapsed_usec, total_received; - pj_highprec_t bandwidth; - pj_timestamp start, stop; - unsigned i; - - TRACE_((THIS_FILE, " starting test..")); - - ioqueue_callback.on_read_complete = &on_read_complete; - ioqueue_callback.on_write_complete = &on_write_complete; - - thread_quit_flag = 0; - - pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); - if (!pool) - return -10; - - items = pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item)); - thread = pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*)); - - TRACE_((THIS_FILE, " creating ioqueue..")); - rc = pj_ioqueue_create(pool, sockpair_cnt*2, thread_cnt, &ioqueue); - if (rc != PJ_SUCCESS) { - app_perror("...error: unable to create ioqueue", rc); - return -15; - } - - /* Initialize each producer-consumer pair. */ - for (i=0; i best_bandwidth) - best_bandwidth = bandwidth, best_index = i; - - /* Give it a rest before next test. */ - pj_thread_sleep(500); - } - - PJ_LOG(3,(THIS_FILE, - " Best: Type=%s Threads=%d, Skt.Pairs=%d, Bandwidth=%u KB/s", - test_param[best_index].type_name, - test_param[best_index].thread_cnt, - test_param[best_index].sockpair_cnt, - best_bandwidth)); - PJ_LOG(3,(THIS_FILE, " (Note: packet size=%d, total errors=%u)", - BUF_SIZE, last_error_counter)); - return 0; -} - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_uiq_perf_test; -#endif /* INCLUDE_IOQUEUE_PERF_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/ioq_perf.c 4 10/29/05 11:51a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/ioq_perf.c $ + * + * 4 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 3 14/10/05 11:31 Bennylp + * More generalized test method, works for UDP too. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/11/05 3:52p Bennylp + * Created. + * + */ +#include "test.h" +#include +#include + +/** + * \page page_pjlib_ioqueue_perf_test Test: I/O Queue Performance + * + * Test the performance of the I/O queue, using typical producer + * consumer test. The test should examine the effect of using multiple + * threads on the performance. + * + * This file is pjlib-test/ioq_perf.c + * + * \include pjlib-test/ioq_perf.c + */ + +#if INCLUDE_IOQUEUE_PERF_TEST + +#ifdef _MSC_VER +# pragma warning ( disable: 4204) // non-constant aggregate initializer +#endif + +#define THIS_FILE "ioq_perf" +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(expr) + + +static pj_bool_t thread_quit_flag; +static pj_status_t last_error; +static unsigned last_error_counter; + +/* Descriptor for each producer/consumer pair. */ +typedef struct test_item +{ + pj_sock_t server_fd, + client_fd; + pj_ioqueue_t *ioqueue; + pj_ioqueue_key_t *server_key, + *client_key; + pj_size_t buffer_size; + char *outgoing_buffer; + char *incoming_buffer; + pj_size_t bytes_sent, + bytes_recv; +} test_item; + +/* Callback when data has been read. + * Increment item->bytes_recv and ready to read the next data. + */ +static void on_read_complete(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) +{ + test_item *item = pj_ioqueue_get_user_data(key); + pj_status_t rc; + + //TRACE_((THIS_FILE, " read complete, bytes_read=%d", bytes_read)); + + if (thread_quit_flag) + return; + + if (bytes_read < 0) { + pj_status_t rc = -bytes_read; + char errmsg[128]; + + if (rc != last_error) { + last_error = rc; + pj_strerror(rc, errmsg, sizeof(errmsg)); + PJ_LOG(3,(THIS_FILE, "...error: read error, bytes_read=%d (%s)", + bytes_read, errmsg)); + PJ_LOG(3,(THIS_FILE, + ".....additional info: total read=%u, total written=%u", + item->bytes_recv, item->bytes_sent)); + } else { + last_error_counter++; + } + bytes_read = 0; + + } else if (bytes_read == 0) { + PJ_LOG(3,(THIS_FILE, "...socket has closed!")); + } + + item->bytes_recv += bytes_read; + + /* To assure that the test quits, even if main thread + * doesn't have time to run. + */ + if (item->bytes_recv > item->buffer_size * 10000) + thread_quit_flag = 1; + + rc = pj_ioqueue_recv( item->ioqueue, item->server_key, + item->incoming_buffer, item->buffer_size, 0 ); + + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + if (rc != last_error) { + last_error = rc; + app_perror("...error: read error", rc); + } else { + last_error_counter++; + } + } +} + +/* Callback when data has been written. + * Increment item->bytes_sent and write the next data. + */ +static void on_write_complete(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent) +{ + test_item *item = pj_ioqueue_get_user_data(key); + + //TRACE_((THIS_FILE, " write complete: sent = %d", bytes_sent)); + + if (thread_quit_flag) + return; + + item->bytes_sent += bytes_sent; + + if (bytes_sent <= 0) { + PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d", + bytes_sent)); + } + else { + pj_status_t rc; + + rc = pj_ioqueue_write(item->ioqueue, item->client_key, + item->outgoing_buffer, item->buffer_size); + if (rc != PJ_SUCCESS && rc != PJ_EPENDING) { + app_perror("...error: write error", rc); + } + } +} + +/* The worker thread. */ +static int worker_thread(void *arg) +{ + pj_ioqueue_t *ioqueue = arg; + const pj_time_val timeout = {0, 100}; + int rc; + + while (!thread_quit_flag) { + rc = pj_ioqueue_poll(ioqueue, &timeout); + //TRACE_((THIS_FILE, " thread: poll returned rc=%d", rc)); + if (rc < 0) { + app_perror("...error in pj_ioqueue_poll()", pj_get_netos_error()); + return -1; + } + } + return 0; +} + +/* Calculate the bandwidth for the specific test configuration. + * The test is simple: + * - create sockpair_cnt number of producer-consumer socket pair. + * - create thread_cnt number of worker threads. + * - each producer will send buffer_size bytes data as fast and + * as soon as it can. + * - each consumer will read buffer_size bytes of data as fast + * as it could. + * - measure the total bytes received by all consumers during a + * period of time. + */ +static int perform_test(int sock_type, const char *type_name, + unsigned thread_cnt, unsigned sockpair_cnt, + pj_size_t buffer_size, + pj_size_t *p_bandwidth) +{ + enum { MSEC_DURATION = 5000 }; + pj_pool_t *pool; + test_item *items; + pj_thread_t **thread; + pj_ioqueue_t *ioqueue; + pj_status_t rc; + pj_ioqueue_callback ioqueue_callback; + pj_uint32_t total_elapsed_usec, total_received; + pj_highprec_t bandwidth; + pj_timestamp start, stop; + unsigned i; + + TRACE_((THIS_FILE, " starting test..")); + + ioqueue_callback.on_read_complete = &on_read_complete; + ioqueue_callback.on_write_complete = &on_write_complete; + + thread_quit_flag = 0; + + pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); + if (!pool) + return -10; + + items = pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item)); + thread = pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*)); + + TRACE_((THIS_FILE, " creating ioqueue..")); + rc = pj_ioqueue_create(pool, sockpair_cnt*2, thread_cnt, &ioqueue); + if (rc != PJ_SUCCESS) { + app_perror("...error: unable to create ioqueue", rc); + return -15; + } + + /* Initialize each producer-consumer pair. */ + for (i=0; i best_bandwidth) + best_bandwidth = bandwidth, best_index = i; + + /* Give it a rest before next test. */ + pj_thread_sleep(500); + } + + PJ_LOG(3,(THIS_FILE, + " Best: Type=%s Threads=%d, Skt.Pairs=%d, Bandwidth=%u KB/s", + test_param[best_index].type_name, + test_param[best_index].thread_cnt, + test_param[best_index].sockpair_cnt, + best_bandwidth)); + PJ_LOG(3,(THIS_FILE, " (Note: packet size=%d, total errors=%u)", + BUF_SIZE, last_error_counter)); + return 0; +} + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_uiq_perf_test; +#endif /* INCLUDE_IOQUEUE_PERF_TEST */ + + diff --git a/pjlib/src/pjlib-test/ioq_tcp.c b/pjlib/src/pjlib-test/ioq_tcp.c index 434c25ae7..99656643f 100644 --- a/pjlib/src/pjlib-test/ioq_tcp.c +++ b/pjlib/src/pjlib-test/ioq_tcp.c @@ -1,474 +1,474 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c 4 10/29/05 10:23p Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c $ - * - * 4 10/29/05 10:23p Bennylp - * Fixed no-memory exception. - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - */ -#include "test.h" - -/** - * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP) - * - * This file provides implementation to test the - * functionality of the I/O queue when TCP socket is used. - * - * - * This file is pjlib-test/ioq_tcp.c - * - * \include pjlib-test/ioq_tcp.c - */ - - -#if INCLUDE_TCP_IOQUEUE_TEST - -#include - -#if PJ_HAS_TCP - -#define THIS_FILE "test_tcp" -#define PORT 50000 -#define NON_EXISTANT_PORT 50123 -#define LOOP 100 -#define BUF_MIN_SIZE 32 -#define BUF_MAX_SIZE 2048 -#define SOCK_INACTIVE_MIN (4-2) -#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2) -#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048) - -static pj_ssize_t callback_read_size, - callback_write_size, - callback_accept_status, - callback_connect_status; -static pj_ioqueue_key_t*callback_read_key, - *callback_write_key, - *callback_accept_key, - *callback_connect_key; - -static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) -{ - callback_read_key = key; - callback_read_size = bytes_read; -} - -static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_written) -{ - callback_write_key = key; - callback_write_size = bytes_written; -} - -static void on_ioqueue_accept(pj_ioqueue_key_t *key, pj_sock_t sock, - int status) -{ - PJ_UNUSED_ARG(sock); - - callback_accept_key = key; - callback_accept_status = status; -} - -static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status) -{ - callback_connect_key = key; - callback_connect_status = status; -} - -static pj_ioqueue_callback test_cb = -{ - &on_ioqueue_read, - &on_ioqueue_write, - &on_ioqueue_accept, - &on_ioqueue_connect, -}; - -static int send_recv_test(pj_ioqueue_t *ioque, - pj_ioqueue_key_t *skey, - pj_ioqueue_key_t *ckey, - void *send_buf, - void *recv_buf, - pj_ssize_t bufsize, - pj_timestamp *t_elapsed) -{ - int rc; - pj_ssize_t bytes; - pj_timestamp t1, t2; - int pending_op = 0; - - // Start reading on the server side. - rc = pj_ioqueue_read(ioque, skey, recv_buf, bufsize); - if (rc != 0 && rc != PJ_EPENDING) { - return -100; - } - - ++pending_op; - - // Randomize send buffer. - pj_create_random_string((char*)send_buf, bufsize); - - // Starts send on the client side. - bytes = pj_ioqueue_write(ioque, ckey, send_buf, bufsize); - if (bytes != bufsize && bytes != PJ_EPENDING) { - return -120; - } - if (bytes == PJ_EPENDING) { - ++pending_op; - } - - // Begin time. - pj_get_timestamp(&t1); - - // Reset indicators - callback_read_size = callback_write_size = 0; - callback_read_key = callback_write_key = NULL; - - // Poll the queue until we've got completion event in the server side. - rc = 0; - while (pending_op > 0) { - rc = pj_ioqueue_poll(ioque, NULL); - if (rc > 0) { - if (callback_read_size) { - if (callback_read_size != bufsize) { - return -160; - } - if (callback_read_key != skey) - return -161; - } - if (callback_write_size) { - if (callback_write_key != ckey) - return -162; - } - pending_op -= rc; - } - if (rc < 0) { - return -170; - } - } - - // End time. - pj_get_timestamp(&t2); - t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo); - - if (rc < 0) { - return -150; - } - - // Compare recv buffer with send buffer. - if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) { - return -180; - } - - // Success - return 0; -} - - -/* - * Compliance test for success scenario. - */ -static int compliance_test_0(void) -{ - pj_sock_t ssock=-1, csock0=-1, csock1=-1; - pj_sockaddr_in addr, client_addr, rmt_addr; - int client_addr_len; - pj_pool_t *pool = NULL; - char *send_buf, *recv_buf; - pj_ioqueue_t *ioque = NULL; - pj_ioqueue_key_t *skey, *ckey0, *ckey1; - int bufsize = BUF_MIN_SIZE; - pj_ssize_t status = -1; - int pending_op = 0; - pj_timestamp t_elapsed; - pj_str_t s; - pj_status_t rc; - - // Create pool. - pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); - - // Allocate buffers for send and receive. - send_buf = (char*)pj_pool_alloc(pool, bufsize); - recv_buf = (char*)pj_pool_alloc(pool, bufsize); - - // Create server socket and client socket for connecting - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ssock); - if (rc != PJ_SUCCESS) { - app_perror("...error creating socket", rc); - status=-1; goto on_error; - } - - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1); - if (rc != PJ_SUCCESS) { - app_perror("...error creating socket", rc); - status=-1; goto on_error; - } - - // Bind server socket. - memset(&addr, 0, sizeof(addr)); - addr.sin_family = PJ_AF_INET; - addr.sin_port = pj_htons(PORT); - if (pj_sock_bind(ssock, &addr, sizeof(addr))) { - app_perror("...bind error", rc); - status=-10; goto on_error; - } - - // Create I/O Queue. - rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque); - if (rc != PJ_SUCCESS) { - app_perror("...ERROR in pj_ioqueue_create()", rc); - status=-20; goto on_error; - } - - // Register server socket and client socket. - rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); - if (rc == PJ_SUCCESS) - rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb, - &ckey1); - else - ckey1 = NULL; - if (rc != PJ_SUCCESS) { - app_perror("...ERROR in pj_ioqueue_register_sock()", rc); - status=-23; goto on_error; - } - - // Server socket listen(). - if (pj_sock_listen(ssock, 5)) { - app_perror("...ERROR in pj_sock_listen()", rc); - status=-25; goto on_error; - } - - // Server socket accept() - client_addr_len = sizeof(pj_sockaddr_in); - status = pj_ioqueue_accept(ioque, skey, &csock0, &client_addr, &rmt_addr, &client_addr_len); - if (status != PJ_EPENDING) { - app_perror("...ERROR in pj_ioqueue_accept()", rc); - status=-30; goto on_error; - } - if (status==PJ_EPENDING) { - ++pending_op; - } - - // Initialize remote address. - memset(&addr, 0, sizeof(addr)); - addr.sin_family = PJ_AF_INET; - addr.sin_port = pj_htons(PORT); - addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); - - // Client socket connect() - status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr)); - if (status!=PJ_SUCCESS && status != PJ_EPENDING) { - app_perror("...ERROR in pj_ioqueue_connect()", rc); - status=-40; goto on_error; - } - if (status==PJ_EPENDING) { - ++pending_op; - } - - // Poll until connected - callback_read_size = callback_write_size = 0; - callback_accept_status = callback_connect_status = -2; - - callback_read_key = callback_write_key = - callback_accept_key = callback_connect_key = NULL; - - while (pending_op) { - pj_time_val timeout = {1, 0}; - - status=pj_ioqueue_poll(ioque, &timeout); - if (status > 0) { - if (callback_accept_status != -2) { - if (callback_accept_status != 0) { - status=-41; goto on_error; - } - if (callback_accept_key != skey) { - status=-41; goto on_error; - } - } - - if (callback_connect_status != -2) { - if (callback_connect_status != 0) { - status=-50; goto on_error; - } - if (callback_connect_key != ckey1) { - status=-51; goto on_error; - } - } - - pending_op -= status; - - if (pending_op == 0) { - status = 0; - } - } - } - - // Check accepted socket. - if (csock0 == PJ_INVALID_SOCKET) { - status = -69; - app_perror("...accept() error", pj_get_os_error()); - goto on_error; - } - - // Register newly accepted socket. - rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL, - &test_cb, &ckey0); - if (rc != PJ_SUCCESS) { - app_perror("...ERROR in pj_ioqueue_register_sock", rc); - status = -70; - goto on_error; - } - - // Test send and receive. - t_elapsed.u32.lo = 0; - status = send_recv_test(ioque, ckey0, ckey1, send_buf, recv_buf, bufsize, &t_elapsed); - if (status != 0) { - goto on_error; - } - - // Success - status = 0; - -on_error: - if (ssock != PJ_INVALID_SOCKET) - pj_sock_close(ssock); - if (csock1 != PJ_INVALID_SOCKET) - pj_sock_close(csock1); - if (csock0 != PJ_INVALID_SOCKET) - pj_sock_close(csock0); - if (ioque != NULL) - pj_ioqueue_destroy(ioque); - pj_pool_release(pool); - return status; - -} - -/* - * Compliance test for failed scenario. - * In this case, the client connects to a non-existant service. - */ -static int compliance_test_1(void) -{ - pj_sock_t csock1=-1; - pj_sockaddr_in addr; - pj_pool_t *pool = NULL; - pj_ioqueue_t *ioque = NULL; - pj_ioqueue_key_t *ckey1; - pj_ssize_t status = -1; - int pending_op = 0; - pj_str_t s; - pj_status_t rc; - - // Create pool. - pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); - - // Create I/O Queue. - rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque); - if (!ioque) { - status=-20; goto on_error; - } - - // Create client socket - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1); - if (rc != PJ_SUCCESS) { - app_perror("...ERROR in pj_sock_socket()", rc); - status=-1; goto on_error; - } - - // Register client socket. - rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, - &test_cb, &ckey1); - if (rc != PJ_SUCCESS) { - app_perror("...ERROR in pj_ioqueue_register_sock()", rc); - status=-23; goto on_error; - } - - // Initialize remote address. - memset(&addr, 0, sizeof(addr)); - addr.sin_family = PJ_AF_INET; - addr.sin_port = pj_htons(NON_EXISTANT_PORT); - addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); - - // Client socket connect() - status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr)); - if (status==PJ_SUCCESS) { - // unexpectedly success! - status = -30; - goto on_error; - } - if (status != PJ_EPENDING) { - // success - } else { - ++pending_op; - } - - callback_connect_status = -2; - callback_connect_key = NULL; - - // Poll until we've got result - while (pending_op) { - pj_time_val timeout = {1, 0}; - - status=pj_ioqueue_poll(ioque, &timeout); - if (status > 0) { - if (callback_connect_key==ckey1) { - if (callback_connect_status == 0) { - // unexpectedly connected! - status = -50; - goto on_error; - } - } - - pending_op -= status; - if (pending_op == 0) { - status = 0; - } - } - } - - // Success - status = 0; - -on_error: - if (csock1 != PJ_INVALID_SOCKET) - pj_sock_close(csock1); - if (ioque != NULL) - pj_ioqueue_destroy(ioque); - pj_pool_release(pool); - return status; -} - -int tcp_ioqueue_test() -{ - int status; - - PJ_LOG(3, (THIS_FILE, "..compliance test 0 (success scenario)")); - if ((status=compliance_test_0()) != 0) { - PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status)); - return status; - } - PJ_LOG(3, (THIS_FILE, "..compliance test 1 (failed scenario)")); - if ((status=compliance_test_1()) != 0) { - PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status)); - return status; - } - - return 0; -} - -#endif /* PJ_HAS_TCP */ - - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_uiq_tcp; -#endif /* INCLUDE_TCP_IOQUEUE_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c 4 10/29/05 10:23p Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/ioq_tcp.c $ + * + * 4 10/29/05 10:23p Bennylp + * Fixed no-memory exception. + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + */ +#include "test.h" + +/** + * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP) + * + * This file provides implementation to test the + * functionality of the I/O queue when TCP socket is used. + * + * + * This file is pjlib-test/ioq_tcp.c + * + * \include pjlib-test/ioq_tcp.c + */ + + +#if INCLUDE_TCP_IOQUEUE_TEST + +#include + +#if PJ_HAS_TCP + +#define THIS_FILE "test_tcp" +#define PORT 50000 +#define NON_EXISTANT_PORT 50123 +#define LOOP 100 +#define BUF_MIN_SIZE 32 +#define BUF_MAX_SIZE 2048 +#define SOCK_INACTIVE_MIN (4-2) +#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2) +#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048) + +static pj_ssize_t callback_read_size, + callback_write_size, + callback_accept_status, + callback_connect_status; +static pj_ioqueue_key_t*callback_read_key, + *callback_write_key, + *callback_accept_key, + *callback_connect_key; + +static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) +{ + callback_read_key = key; + callback_read_size = bytes_read; +} + +static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_written) +{ + callback_write_key = key; + callback_write_size = bytes_written; +} + +static void on_ioqueue_accept(pj_ioqueue_key_t *key, pj_sock_t sock, + int status) +{ + PJ_UNUSED_ARG(sock); + + callback_accept_key = key; + callback_accept_status = status; +} + +static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status) +{ + callback_connect_key = key; + callback_connect_status = status; +} + +static pj_ioqueue_callback test_cb = +{ + &on_ioqueue_read, + &on_ioqueue_write, + &on_ioqueue_accept, + &on_ioqueue_connect, +}; + +static int send_recv_test(pj_ioqueue_t *ioque, + pj_ioqueue_key_t *skey, + pj_ioqueue_key_t *ckey, + void *send_buf, + void *recv_buf, + pj_ssize_t bufsize, + pj_timestamp *t_elapsed) +{ + int rc; + pj_ssize_t bytes; + pj_timestamp t1, t2; + int pending_op = 0; + + // Start reading on the server side. + rc = pj_ioqueue_read(ioque, skey, recv_buf, bufsize); + if (rc != 0 && rc != PJ_EPENDING) { + return -100; + } + + ++pending_op; + + // Randomize send buffer. + pj_create_random_string((char*)send_buf, bufsize); + + // Starts send on the client side. + bytes = pj_ioqueue_write(ioque, ckey, send_buf, bufsize); + if (bytes != bufsize && bytes != PJ_EPENDING) { + return -120; + } + if (bytes == PJ_EPENDING) { + ++pending_op; + } + + // Begin time. + pj_get_timestamp(&t1); + + // Reset indicators + callback_read_size = callback_write_size = 0; + callback_read_key = callback_write_key = NULL; + + // Poll the queue until we've got completion event in the server side. + rc = 0; + while (pending_op > 0) { + rc = pj_ioqueue_poll(ioque, NULL); + if (rc > 0) { + if (callback_read_size) { + if (callback_read_size != bufsize) { + return -160; + } + if (callback_read_key != skey) + return -161; + } + if (callback_write_size) { + if (callback_write_key != ckey) + return -162; + } + pending_op -= rc; + } + if (rc < 0) { + return -170; + } + } + + // End time. + pj_get_timestamp(&t2); + t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo); + + if (rc < 0) { + return -150; + } + + // Compare recv buffer with send buffer. + if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) { + return -180; + } + + // Success + return 0; +} + + +/* + * Compliance test for success scenario. + */ +static int compliance_test_0(void) +{ + pj_sock_t ssock=-1, csock0=-1, csock1=-1; + pj_sockaddr_in addr, client_addr, rmt_addr; + int client_addr_len; + pj_pool_t *pool = NULL; + char *send_buf, *recv_buf; + pj_ioqueue_t *ioque = NULL; + pj_ioqueue_key_t *skey, *ckey0, *ckey1; + int bufsize = BUF_MIN_SIZE; + pj_ssize_t status = -1; + int pending_op = 0; + pj_timestamp t_elapsed; + pj_str_t s; + pj_status_t rc; + + // Create pool. + pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); + + // Allocate buffers for send and receive. + send_buf = (char*)pj_pool_alloc(pool, bufsize); + recv_buf = (char*)pj_pool_alloc(pool, bufsize); + + // Create server socket and client socket for connecting + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ssock); + if (rc != PJ_SUCCESS) { + app_perror("...error creating socket", rc); + status=-1; goto on_error; + } + + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1); + if (rc != PJ_SUCCESS) { + app_perror("...error creating socket", rc); + status=-1; goto on_error; + } + + // Bind server socket. + memset(&addr, 0, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + addr.sin_port = pj_htons(PORT); + if (pj_sock_bind(ssock, &addr, sizeof(addr))) { + app_perror("...bind error", rc); + status=-10; goto on_error; + } + + // Create I/O Queue. + rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque); + if (rc != PJ_SUCCESS) { + app_perror("...ERROR in pj_ioqueue_create()", rc); + status=-20; goto on_error; + } + + // Register server socket and client socket. + rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey); + if (rc == PJ_SUCCESS) + rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb, + &ckey1); + else + ckey1 = NULL; + if (rc != PJ_SUCCESS) { + app_perror("...ERROR in pj_ioqueue_register_sock()", rc); + status=-23; goto on_error; + } + + // Server socket listen(). + if (pj_sock_listen(ssock, 5)) { + app_perror("...ERROR in pj_sock_listen()", rc); + status=-25; goto on_error; + } + + // Server socket accept() + client_addr_len = sizeof(pj_sockaddr_in); + status = pj_ioqueue_accept(ioque, skey, &csock0, &client_addr, &rmt_addr, &client_addr_len); + if (status != PJ_EPENDING) { + app_perror("...ERROR in pj_ioqueue_accept()", rc); + status=-30; goto on_error; + } + if (status==PJ_EPENDING) { + ++pending_op; + } + + // Initialize remote address. + memset(&addr, 0, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + addr.sin_port = pj_htons(PORT); + addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); + + // Client socket connect() + status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr)); + if (status!=PJ_SUCCESS && status != PJ_EPENDING) { + app_perror("...ERROR in pj_ioqueue_connect()", rc); + status=-40; goto on_error; + } + if (status==PJ_EPENDING) { + ++pending_op; + } + + // Poll until connected + callback_read_size = callback_write_size = 0; + callback_accept_status = callback_connect_status = -2; + + callback_read_key = callback_write_key = + callback_accept_key = callback_connect_key = NULL; + + while (pending_op) { + pj_time_val timeout = {1, 0}; + + status=pj_ioqueue_poll(ioque, &timeout); + if (status > 0) { + if (callback_accept_status != -2) { + if (callback_accept_status != 0) { + status=-41; goto on_error; + } + if (callback_accept_key != skey) { + status=-41; goto on_error; + } + } + + if (callback_connect_status != -2) { + if (callback_connect_status != 0) { + status=-50; goto on_error; + } + if (callback_connect_key != ckey1) { + status=-51; goto on_error; + } + } + + pending_op -= status; + + if (pending_op == 0) { + status = 0; + } + } + } + + // Check accepted socket. + if (csock0 == PJ_INVALID_SOCKET) { + status = -69; + app_perror("...accept() error", pj_get_os_error()); + goto on_error; + } + + // Register newly accepted socket. + rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL, + &test_cb, &ckey0); + if (rc != PJ_SUCCESS) { + app_perror("...ERROR in pj_ioqueue_register_sock", rc); + status = -70; + goto on_error; + } + + // Test send and receive. + t_elapsed.u32.lo = 0; + status = send_recv_test(ioque, ckey0, ckey1, send_buf, recv_buf, bufsize, &t_elapsed); + if (status != 0) { + goto on_error; + } + + // Success + status = 0; + +on_error: + if (ssock != PJ_INVALID_SOCKET) + pj_sock_close(ssock); + if (csock1 != PJ_INVALID_SOCKET) + pj_sock_close(csock1); + if (csock0 != PJ_INVALID_SOCKET) + pj_sock_close(csock0); + if (ioque != NULL) + pj_ioqueue_destroy(ioque); + pj_pool_release(pool); + return status; + +} + +/* + * Compliance test for failed scenario. + * In this case, the client connects to a non-existant service. + */ +static int compliance_test_1(void) +{ + pj_sock_t csock1=-1; + pj_sockaddr_in addr; + pj_pool_t *pool = NULL; + pj_ioqueue_t *ioque = NULL; + pj_ioqueue_key_t *ckey1; + pj_ssize_t status = -1; + int pending_op = 0; + pj_str_t s; + pj_status_t rc; + + // Create pool. + pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); + + // Create I/O Queue. + rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, 0, &ioque); + if (!ioque) { + status=-20; goto on_error; + } + + // Create client socket + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, 0, &csock1); + if (rc != PJ_SUCCESS) { + app_perror("...ERROR in pj_sock_socket()", rc); + status=-1; goto on_error; + } + + // Register client socket. + rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, + &test_cb, &ckey1); + if (rc != PJ_SUCCESS) { + app_perror("...ERROR in pj_ioqueue_register_sock()", rc); + status=-23; goto on_error; + } + + // Initialize remote address. + memset(&addr, 0, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + addr.sin_port = pj_htons(NON_EXISTANT_PORT); + addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); + + // Client socket connect() + status = pj_ioqueue_connect(ioque, ckey1, &addr, sizeof(addr)); + if (status==PJ_SUCCESS) { + // unexpectedly success! + status = -30; + goto on_error; + } + if (status != PJ_EPENDING) { + // success + } else { + ++pending_op; + } + + callback_connect_status = -2; + callback_connect_key = NULL; + + // Poll until we've got result + while (pending_op) { + pj_time_val timeout = {1, 0}; + + status=pj_ioqueue_poll(ioque, &timeout); + if (status > 0) { + if (callback_connect_key==ckey1) { + if (callback_connect_status == 0) { + // unexpectedly connected! + status = -50; + goto on_error; + } + } + + pending_op -= status; + if (pending_op == 0) { + status = 0; + } + } + } + + // Success + status = 0; + +on_error: + if (csock1 != PJ_INVALID_SOCKET) + pj_sock_close(csock1); + if (ioque != NULL) + pj_ioqueue_destroy(ioque); + pj_pool_release(pool); + return status; +} + +int tcp_ioqueue_test() +{ + int status; + + PJ_LOG(3, (THIS_FILE, "..compliance test 0 (success scenario)")); + if ((status=compliance_test_0()) != 0) { + PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status)); + return status; + } + PJ_LOG(3, (THIS_FILE, "..compliance test 1 (failed scenario)")); + if ((status=compliance_test_1()) != 0) { + PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status)); + return status; + } + + return 0; +} + +#endif /* PJ_HAS_TCP */ + + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_uiq_tcp; +#endif /* INCLUDE_TCP_IOQUEUE_TEST */ + + diff --git a/pjlib/src/pjlib-test/ioq_udp.c b/pjlib/src/pjlib-test/ioq_udp.c index 8b95782a7..8c15ecae4 100644 --- a/pjlib/src/pjlib-test/ioq_udp.c +++ b/pjlib/src/pjlib-test/ioq_udp.c @@ -1,664 +1,664 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/ioq_udp.c 4 10/29/05 10:23p Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/ioq_udp.c $ - * - * 4 10/29/05 10:23p Bennylp - * Fixed no-memory exception. - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - */ -#include "test.h" - - -/** - * \page page_pjlib_ioqueue_udp_test Test: I/O Queue (UDP) - * - * This file provides implementation to test the - * functionality of the I/O queue when UDP socket is used. - * - * - * This file is pjlib-test/ioq_udp.c - * - * \include pjlib-test/ioq_udp.c - */ - - -#if INCLUDE_UDP_IOQUEUE_TEST - -#include - -#include - -#define THIS_FILE "test_udp" -#define PORT 51233 -#define LOOP 100 -#define BUF_MIN_SIZE 32 -#define BUF_MAX_SIZE 2048 -#define SOCK_INACTIVE_MIN (1) -#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2) -#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048) - -#undef TRACE_ -#define TRACE_(msg) PJ_LOG(3,(THIS_FILE,"....." msg)) - -static pj_ssize_t callback_read_size, - callback_write_size, - callback_accept_status, - callback_connect_status; -static pj_ioqueue_key_t *callback_read_key, - *callback_write_key, - *callback_accept_key, - *callback_connect_key; - -static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) -{ - callback_read_key = key; - callback_read_size = bytes_read; -} - -static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_written) -{ - callback_write_key = key; - callback_write_size = bytes_written; -} - -static void on_ioqueue_accept(pj_ioqueue_key_t *key, pj_sock_t sock, int status) -{ - PJ_UNUSED_ARG(sock); - callback_accept_key = key; - callback_accept_status = status; -} - -static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status) -{ - callback_connect_key = key; - callback_connect_status = status; -} - -static pj_ioqueue_callback test_cb = -{ - &on_ioqueue_read, - &on_ioqueue_write, - &on_ioqueue_accept, - &on_ioqueue_connect, -}; - -#ifdef PJ_WIN32 -# define S_ADDR S_un.S_addr -#else -# define S_ADDR s_addr -#endif - -/* - * native_format_test() - * This is just a simple test to verify that various structures in sock.h - * are really compatible with operating system's definitions. - */ -static int native_format_test(void) -{ - pj_status_t rc; - - // Test that PJ_INVALID_SOCKET is working. - { - pj_sock_t sock; - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, -1, &sock); - if (rc == PJ_SUCCESS) - return -1020; - } - - // Previous func will set errno var. - pj_set_os_error(PJ_SUCCESS); - - return 0; -} - -/* - * compliance_test() - * To test that the basic IOQueue functionality works. It will just exchange - * data between two sockets. - */ -static int compliance_test(void) -{ - pj_sock_t ssock=-1, csock=-1; - pj_sockaddr_in addr; - int addrlen; - pj_pool_t *pool = NULL; - char *send_buf, *recv_buf; - pj_ioqueue_t *ioque = NULL; - pj_ioqueue_key_t *skey, *ckey; - int bufsize = BUF_MIN_SIZE; - pj_ssize_t bytes, status = -1; - pj_str_t temp; - pj_bool_t send_pending, recv_pending; - pj_status_t rc; - - pj_set_os_error(PJ_SUCCESS); - - // Create pool. - pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); - - // Allocate buffers for send and receive. - send_buf = (char*)pj_pool_alloc(pool, bufsize); - recv_buf = (char*)pj_pool_alloc(pool, bufsize); - - // Allocate sockets for sending and receiving. - TRACE_("creating sockets..."); - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock); - if (rc==PJ_SUCCESS) - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock); - else - csock = PJ_INVALID_SOCKET; - if (rc != PJ_SUCCESS) { - app_perror("...ERROR in pj_sock_socket()", rc); - status=-1; goto on_error; - } - - // Bind server socket. - TRACE_("bind socket..."); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = PJ_AF_INET; - addr.sin_port = pj_htons(PORT); - if (pj_sock_bind(ssock, &addr, sizeof(addr))) { - status=-10; goto on_error; - } - - // Create I/O Queue. - TRACE_("create ioqueue..."); - rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, - PJ_IOQUEUE_DEFAULT_THREADS, &ioque); - if (rc != PJ_SUCCESS) { - status=-20; goto on_error; - } - - // Register server and client socket. - // We put this after inactivity socket, hopefully this can represent the - // worst waiting time. - TRACE_("registering first sockets..."); - rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, - &test_cb, &skey); - if (rc != PJ_SUCCESS) { - app_perror("...error(10): ioqueue_register error", rc); - status=-25; goto on_error; - } - TRACE_("registering second sockets..."); - rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL, - &test_cb, &ckey); - if (rc != PJ_SUCCESS) { - app_perror("...error(11): ioqueue_register error", rc); - status=-26; goto on_error; - } - - // Set destination address to send the packet. - TRACE_("set destination address..."); - temp = pj_str("127.0.0.1"); - if ((rc=pj_sockaddr_in_init(&addr, &temp, PORT)) != 0) { - app_perror("...error: unable to resolve 127.0.0.1", rc); - status=-26; goto on_error; - } - - // Randomize send_buf. - pj_create_random_string(send_buf, bufsize); - - // Register reading from ioqueue. - TRACE_("start recvfrom..."); - addrlen = sizeof(addr); - bytes = pj_ioqueue_recvfrom(ioque, skey, recv_buf, bufsize, 0, - &addr, &addrlen); - if (bytes < 0 && bytes != PJ_EPENDING) { - status=-28; goto on_error; - } else if (bytes == PJ_EPENDING) { - recv_pending = 1; - PJ_LOG(3, (THIS_FILE, - "......ok: recvfrom returned pending")); - } else { - PJ_LOG(3, (THIS_FILE, - "......error: recvfrom returned immediate ok!")); - status=-29; goto on_error; - } - - // Write must return the number of bytes. - TRACE_("start sendto..."); - bytes = pj_ioqueue_sendto(ioque, ckey, send_buf, bufsize, 0, &addr, - sizeof(addr)); - if (bytes != bufsize && bytes != PJ_EPENDING) { - PJ_LOG(1,(THIS_FILE, - "......error: sendto returned %d", bytes)); - status=-30; goto on_error; - } else if (bytes == PJ_EPENDING) { - send_pending = 1; - PJ_LOG(3, (THIS_FILE, - "......ok: sendto returned pending")); - } else { - send_pending = 0; - PJ_LOG(3, (THIS_FILE, - "......ok: sendto returned immediate success")); - } - - // reset callback variables. - callback_read_size = callback_write_size = 0; - callback_accept_status = callback_connect_status = -2; - callback_read_key = callback_write_key = - callback_accept_key = callback_connect_key = NULL; - - // Poll if pending. - while (send_pending && recv_pending) { - int rc; - pj_time_val timeout = { 5, 0 }; - - TRACE_("poll..."); - rc = pj_ioqueue_poll(ioque, &timeout); - - if (rc == 0) { - PJ_LOG(1,(THIS_FILE, "...ERROR: timed out...")); - status=-45; goto on_error; - } else if (rc < 0) { - app_perror("...ERROR in ioqueue_poll()", rc); - status=-50; goto on_error; - } - - if (callback_read_key != NULL) { - if (callback_read_size != bufsize) { - status=-61; goto on_error; - } - - if (callback_read_key != skey) { - status=-65; goto on_error; - } - - if (memcmp(send_buf, recv_buf, bufsize) != 0) { - status=-70; goto on_error; - } - - - recv_pending = 0; - } - - if (callback_write_key != NULL) { - if (callback_write_size != bufsize) { - status=-73; goto on_error; - } - - if (callback_write_key != ckey) { - status=-75; goto on_error; - } - - send_pending = 0; - } - } - - // Success - status = 0; - -on_error: - if (status != 0) { - char errbuf[128]; - PJ_LOG(1, (THIS_FILE, - "...compliance test error: status=%d, os_err=%d (%s)", - status, pj_get_netos_error(), - pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)))); - } - if (ssock) - pj_sock_close(ssock); - if (csock) - pj_sock_close(csock); - if (ioque != NULL) - pj_ioqueue_destroy(ioque); - pj_pool_release(pool); - return status; - -} - -/* - * Testing with many handles. - * This will just test registering PJ_IOQUEUE_MAX_HANDLES count - * of sockets to the ioqueue. - */ -static int many_handles_test(void) -{ - enum { MAX = PJ_IOQUEUE_MAX_HANDLES }; - pj_pool_t *pool; - pj_ioqueue_t *ioqueue; - pj_sock_t *sock; - pj_ioqueue_key_t **key; - pj_status_t rc; - int count, i; - - PJ_LOG(3,(THIS_FILE,"...testing with so many handles")); - - pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); - if (!pool) - return PJ_ENOMEM; - - key = pj_pool_alloc(pool, MAX*sizeof(pj_ioqueue_key_t*)); - sock = pj_pool_alloc(pool, MAX*sizeof(pj_sock_t)); - - /* Create IOQueue */ - rc = pj_ioqueue_create(pool, MAX, - PJ_IOQUEUE_DEFAULT_THREADS, - &ioqueue); - if (rc != PJ_SUCCESS || ioqueue == NULL) { - app_perror("...error in pj_ioqueue_create", rc); - return -10; - } - - /* Register as many sockets. */ - for (count=0; count= 0 && callback_read_key != skey); - - // End time. - pj_get_timestamp(&t2); - t_elapsed.u64 += (t2.u64 - t1.u64); - - if (rc < 0) - break; - - // Compare recv buffer with send buffer. - if (callback_read_size != bufsize || - memcmp(send_buf, recv_buf, bufsize)) - { - rc = -1; - break; - } - - // Poll until all events are exhausted, before we start the next loop. - do { - pj_time_val timeout = { 0, 10 }; - rc = pj_ioqueue_poll(ioque, &timeout); - } while (rc>0); - - rc = 0; - } - - // Print results - if (rc == 0) { - pj_timestamp tzero; - pj_uint32_t usec_delay; - - tzero.u32.hi = tzero.u32.lo = 0; - usec_delay = pj_elapsed_usec( &tzero, &t_elapsed); - - PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d", - bufsize, inactive_sock_count, usec_delay)); - - } else { - PJ_LOG(2, (THIS_FILE, "...ERROR (buf:%d, fds:%d)", - bufsize, inactive_sock_count+2)); - } - - // Cleaning up. - for (i=0; ipjlib-test/ioq_udp.c + * + * \include pjlib-test/ioq_udp.c + */ + + +#if INCLUDE_UDP_IOQUEUE_TEST + +#include + +#include + +#define THIS_FILE "test_udp" +#define PORT 51233 +#define LOOP 100 +#define BUF_MIN_SIZE 32 +#define BUF_MAX_SIZE 2048 +#define SOCK_INACTIVE_MIN (1) +#define SOCK_INACTIVE_MAX (PJ_IOQUEUE_MAX_HANDLES - 2) +#define POOL_SIZE (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048) + +#undef TRACE_ +#define TRACE_(msg) PJ_LOG(3,(THIS_FILE,"....." msg)) + +static pj_ssize_t callback_read_size, + callback_write_size, + callback_accept_status, + callback_connect_status; +static pj_ioqueue_key_t *callback_read_key, + *callback_write_key, + *callback_accept_key, + *callback_connect_key; + +static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) +{ + callback_read_key = key; + callback_read_size = bytes_read; +} + +static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_written) +{ + callback_write_key = key; + callback_write_size = bytes_written; +} + +static void on_ioqueue_accept(pj_ioqueue_key_t *key, pj_sock_t sock, int status) +{ + PJ_UNUSED_ARG(sock); + callback_accept_key = key; + callback_accept_status = status; +} + +static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status) +{ + callback_connect_key = key; + callback_connect_status = status; +} + +static pj_ioqueue_callback test_cb = +{ + &on_ioqueue_read, + &on_ioqueue_write, + &on_ioqueue_accept, + &on_ioqueue_connect, +}; + +#ifdef PJ_WIN32 +# define S_ADDR S_un.S_addr +#else +# define S_ADDR s_addr +#endif + +/* + * native_format_test() + * This is just a simple test to verify that various structures in sock.h + * are really compatible with operating system's definitions. + */ +static int native_format_test(void) +{ + pj_status_t rc; + + // Test that PJ_INVALID_SOCKET is working. + { + pj_sock_t sock; + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_STREAM, -1, &sock); + if (rc == PJ_SUCCESS) + return -1020; + } + + // Previous func will set errno var. + pj_set_os_error(PJ_SUCCESS); + + return 0; +} + +/* + * compliance_test() + * To test that the basic IOQueue functionality works. It will just exchange + * data between two sockets. + */ +static int compliance_test(void) +{ + pj_sock_t ssock=-1, csock=-1; + pj_sockaddr_in addr; + int addrlen; + pj_pool_t *pool = NULL; + char *send_buf, *recv_buf; + pj_ioqueue_t *ioque = NULL; + pj_ioqueue_key_t *skey, *ckey; + int bufsize = BUF_MIN_SIZE; + pj_ssize_t bytes, status = -1; + pj_str_t temp; + pj_bool_t send_pending, recv_pending; + pj_status_t rc; + + pj_set_os_error(PJ_SUCCESS); + + // Create pool. + pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL); + + // Allocate buffers for send and receive. + send_buf = (char*)pj_pool_alloc(pool, bufsize); + recv_buf = (char*)pj_pool_alloc(pool, bufsize); + + // Allocate sockets for sending and receiving. + TRACE_("creating sockets..."); + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ssock); + if (rc==PJ_SUCCESS) + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &csock); + else + csock = PJ_INVALID_SOCKET; + if (rc != PJ_SUCCESS) { + app_perror("...ERROR in pj_sock_socket()", rc); + status=-1; goto on_error; + } + + // Bind server socket. + TRACE_("bind socket..."); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + addr.sin_port = pj_htons(PORT); + if (pj_sock_bind(ssock, &addr, sizeof(addr))) { + status=-10; goto on_error; + } + + // Create I/O Queue. + TRACE_("create ioqueue..."); + rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, + PJ_IOQUEUE_DEFAULT_THREADS, &ioque); + if (rc != PJ_SUCCESS) { + status=-20; goto on_error; + } + + // Register server and client socket. + // We put this after inactivity socket, hopefully this can represent the + // worst waiting time. + TRACE_("registering first sockets..."); + rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, + &test_cb, &skey); + if (rc != PJ_SUCCESS) { + app_perror("...error(10): ioqueue_register error", rc); + status=-25; goto on_error; + } + TRACE_("registering second sockets..."); + rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL, + &test_cb, &ckey); + if (rc != PJ_SUCCESS) { + app_perror("...error(11): ioqueue_register error", rc); + status=-26; goto on_error; + } + + // Set destination address to send the packet. + TRACE_("set destination address..."); + temp = pj_str("127.0.0.1"); + if ((rc=pj_sockaddr_in_init(&addr, &temp, PORT)) != 0) { + app_perror("...error: unable to resolve 127.0.0.1", rc); + status=-26; goto on_error; + } + + // Randomize send_buf. + pj_create_random_string(send_buf, bufsize); + + // Register reading from ioqueue. + TRACE_("start recvfrom..."); + addrlen = sizeof(addr); + bytes = pj_ioqueue_recvfrom(ioque, skey, recv_buf, bufsize, 0, + &addr, &addrlen); + if (bytes < 0 && bytes != PJ_EPENDING) { + status=-28; goto on_error; + } else if (bytes == PJ_EPENDING) { + recv_pending = 1; + PJ_LOG(3, (THIS_FILE, + "......ok: recvfrom returned pending")); + } else { + PJ_LOG(3, (THIS_FILE, + "......error: recvfrom returned immediate ok!")); + status=-29; goto on_error; + } + + // Write must return the number of bytes. + TRACE_("start sendto..."); + bytes = pj_ioqueue_sendto(ioque, ckey, send_buf, bufsize, 0, &addr, + sizeof(addr)); + if (bytes != bufsize && bytes != PJ_EPENDING) { + PJ_LOG(1,(THIS_FILE, + "......error: sendto returned %d", bytes)); + status=-30; goto on_error; + } else if (bytes == PJ_EPENDING) { + send_pending = 1; + PJ_LOG(3, (THIS_FILE, + "......ok: sendto returned pending")); + } else { + send_pending = 0; + PJ_LOG(3, (THIS_FILE, + "......ok: sendto returned immediate success")); + } + + // reset callback variables. + callback_read_size = callback_write_size = 0; + callback_accept_status = callback_connect_status = -2; + callback_read_key = callback_write_key = + callback_accept_key = callback_connect_key = NULL; + + // Poll if pending. + while (send_pending && recv_pending) { + int rc; + pj_time_val timeout = { 5, 0 }; + + TRACE_("poll..."); + rc = pj_ioqueue_poll(ioque, &timeout); + + if (rc == 0) { + PJ_LOG(1,(THIS_FILE, "...ERROR: timed out...")); + status=-45; goto on_error; + } else if (rc < 0) { + app_perror("...ERROR in ioqueue_poll()", rc); + status=-50; goto on_error; + } + + if (callback_read_key != NULL) { + if (callback_read_size != bufsize) { + status=-61; goto on_error; + } + + if (callback_read_key != skey) { + status=-65; goto on_error; + } + + if (memcmp(send_buf, recv_buf, bufsize) != 0) { + status=-70; goto on_error; + } + + + recv_pending = 0; + } + + if (callback_write_key != NULL) { + if (callback_write_size != bufsize) { + status=-73; goto on_error; + } + + if (callback_write_key != ckey) { + status=-75; goto on_error; + } + + send_pending = 0; + } + } + + // Success + status = 0; + +on_error: + if (status != 0) { + char errbuf[128]; + PJ_LOG(1, (THIS_FILE, + "...compliance test error: status=%d, os_err=%d (%s)", + status, pj_get_netos_error(), + pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)))); + } + if (ssock) + pj_sock_close(ssock); + if (csock) + pj_sock_close(csock); + if (ioque != NULL) + pj_ioqueue_destroy(ioque); + pj_pool_release(pool); + return status; + +} + +/* + * Testing with many handles. + * This will just test registering PJ_IOQUEUE_MAX_HANDLES count + * of sockets to the ioqueue. + */ +static int many_handles_test(void) +{ + enum { MAX = PJ_IOQUEUE_MAX_HANDLES }; + pj_pool_t *pool; + pj_ioqueue_t *ioqueue; + pj_sock_t *sock; + pj_ioqueue_key_t **key; + pj_status_t rc; + int count, i; + + PJ_LOG(3,(THIS_FILE,"...testing with so many handles")); + + pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); + if (!pool) + return PJ_ENOMEM; + + key = pj_pool_alloc(pool, MAX*sizeof(pj_ioqueue_key_t*)); + sock = pj_pool_alloc(pool, MAX*sizeof(pj_sock_t)); + + /* Create IOQueue */ + rc = pj_ioqueue_create(pool, MAX, + PJ_IOQUEUE_DEFAULT_THREADS, + &ioqueue); + if (rc != PJ_SUCCESS || ioqueue == NULL) { + app_perror("...error in pj_ioqueue_create", rc); + return -10; + } + + /* Register as many sockets. */ + for (count=0; count= 0 && callback_read_key != skey); + + // End time. + pj_get_timestamp(&t2); + t_elapsed.u64 += (t2.u64 - t1.u64); + + if (rc < 0) + break; + + // Compare recv buffer with send buffer. + if (callback_read_size != bufsize || + memcmp(send_buf, recv_buf, bufsize)) + { + rc = -1; + break; + } + + // Poll until all events are exhausted, before we start the next loop. + do { + pj_time_val timeout = { 0, 10 }; + rc = pj_ioqueue_poll(ioque, &timeout); + } while (rc>0); + + rc = 0; + } + + // Print results + if (rc == 0) { + pj_timestamp tzero; + pj_uint32_t usec_delay; + + tzero.u32.hi = tzero.u32.lo = 0; + usec_delay = pj_elapsed_usec( &tzero, &t_elapsed); + + PJ_LOG(3, (THIS_FILE, "...%10d %15d % 9d", + bufsize, inactive_sock_count, usec_delay)); + + } else { + PJ_LOG(2, (THIS_FILE, "...ERROR (buf:%d, fds:%d)", + bufsize, inactive_sock_count+2)); + } + + // Cleaning up. + for (i=0; ipjlib-test/list.c - * - * \include pjlib-test/list.c - */ - -#if INCLUDE_LIST_TEST - -#include - -typedef struct list_node -{ - PJ_DECL_LIST_MEMBER(struct list_node) - int value; -} list_node; - -static int compare_node(void *value, const pj_list_type *nd) -{ - list_node *node = (list_node*)nd; - return ((int)value == node->value) ? 0 : -1; -} - -#define PJ_SIGNED_ARRAY_SIZE(a) ((int)PJ_ARRAY_SIZE(a)) - -int list_test() -{ - list_node nodes[4]; // must be even number of nodes - list_node list; - list_node list2; - list_node *p; - int i; // don't change to unsigned! - - // - // Test insert_before(). - // - list.value = (unsigned)-1; - pj_list_init(&list); - for (i=0; inext) { - pj_assert(p->value == i); - if (p->value != i) { - return -1; - } - } - - // - // Test insert_after() - // - pj_list_init(&list); - for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) { - pj_list_insert_after(&list, &nodes[i]); - } - // check. - for (i=0, p=list.next; inext) { - pj_assert(p->value == i); - if (p->value != i) { - return -1; - } - } - - // - // Test merge_last() - // - // Init lists - pj_list_init(&list); - pj_list_init(&list2); - for (i=0; inext) { - pj_assert(p->value == i); - if (p->value != i) { - return -1; - } - } - // check list is empty - pj_assert( pj_list_empty(&list2) ); - if (!pj_list_empty(&list2)) { - return -1; - } - - // - // Check merge_first() - // - pj_list_init(&list); - pj_list_init(&list2); - for (i=0; inext) { - pj_assert(p->value == i); - if (p->value != i) { - return -1; - } - } - // check list is empty - pj_assert( pj_list_empty(&list) ); - if (!pj_list_empty(&list)) { - return -1; - } - - // - // Test insert_nodes_before() - // - // init list - pj_list_init(&list); - for (i=0; inext) { - pj_assert(p->value == i); - if (p->value != i) { - return -1; - } - } - - // erase test. - pj_list_init(&list); - for (i=0; i=0; --i) { - int j; - pj_list_erase(&nodes[i]); - for (j=0, p=list.next; jnext) { - pj_assert(p->value == j); - if (p->value != j) { - return -1; - } - } - } - - // find and search - pj_list_init(&list); - for (i=0; ipjlib-test/list.c + * + * \include pjlib-test/list.c + */ + +#if INCLUDE_LIST_TEST + +#include + +typedef struct list_node +{ + PJ_DECL_LIST_MEMBER(struct list_node) + int value; +} list_node; + +static int compare_node(void *value, const pj_list_type *nd) +{ + list_node *node = (list_node*)nd; + return ((int)value == node->value) ? 0 : -1; +} + +#define PJ_SIGNED_ARRAY_SIZE(a) ((int)PJ_ARRAY_SIZE(a)) + +int list_test() +{ + list_node nodes[4]; // must be even number of nodes + list_node list; + list_node list2; + list_node *p; + int i; // don't change to unsigned! + + // + // Test insert_before(). + // + list.value = (unsigned)-1; + pj_list_init(&list); + for (i=0; inext) { + pj_assert(p->value == i); + if (p->value != i) { + return -1; + } + } + + // + // Test insert_after() + // + pj_list_init(&list); + for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) { + pj_list_insert_after(&list, &nodes[i]); + } + // check. + for (i=0, p=list.next; inext) { + pj_assert(p->value == i); + if (p->value != i) { + return -1; + } + } + + // + // Test merge_last() + // + // Init lists + pj_list_init(&list); + pj_list_init(&list2); + for (i=0; inext) { + pj_assert(p->value == i); + if (p->value != i) { + return -1; + } + } + // check list is empty + pj_assert( pj_list_empty(&list2) ); + if (!pj_list_empty(&list2)) { + return -1; + } + + // + // Check merge_first() + // + pj_list_init(&list); + pj_list_init(&list2); + for (i=0; inext) { + pj_assert(p->value == i); + if (p->value != i) { + return -1; + } + } + // check list is empty + pj_assert( pj_list_empty(&list) ); + if (!pj_list_empty(&list)) { + return -1; + } + + // + // Test insert_nodes_before() + // + // init list + pj_list_init(&list); + for (i=0; inext) { + pj_assert(p->value == i); + if (p->value != i) { + return -1; + } + } + + // erase test. + pj_list_init(&list); + for (i=0; i=0; --i) { + int j; + pj_list_erase(&nodes[i]); + for (j=0, p=list.next; jnext) { + pj_assert(p->value == j); + if (p->value != j) { + return -1; + } + } + } + + // find and search + pj_list_init(&list); + for (i=0; i -#include -#include - -extern int param_echo_sock_type; -extern const char *param_echo_server; -extern int param_echo_port; - - -#if defined(PJ_WIN32) && PJ_WIN32!=0 -#include -static void boost(void) -{ - SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); -} -#else -#define boost() -#endif - -int main(int argc, char *argv[]) -{ - int rc; - - boost(); - - while (argc > 1) { - char *arg = argv[--argc]; - - if (*arg=='-' && *(arg+1)=='p') { - pj_str_t port = pj_str(argv[--argc]); - - param_echo_port = pj_strtoul(&port); - - } else if (*arg=='-' && *(arg+1)=='s') { - param_echo_server = argv[--argc]; - - } else if (*arg=='-' && *(arg+1)=='t') { - pj_str_t type = pj_str(argv[--argc]); - - if (pj_stricmp2(&type, "tcp")==0) - param_echo_sock_type = PJ_SOCK_STREAM; - else if (pj_stricmp2(&type, "udp")==0) - param_echo_sock_type = PJ_SOCK_DGRAM; - else { - PJ_LOG(3,("", "error: unknown socket type %s", type.ptr)); - return 1; - } - } - } - - rc = test_main(); - - return rc; -} - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/main.c 4 29/10/05 21:32 Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/main.c $ + * + * 4 29/10/05 21:32 Bennylp + * Boost process priority in Win32 + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + */ +#include "test.h" + +#include +#include +#include + +extern int param_echo_sock_type; +extern const char *param_echo_server; +extern int param_echo_port; + + +#if defined(PJ_WIN32) && PJ_WIN32!=0 +#include +static void boost(void) +{ + SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS); +} +#else +#define boost() +#endif + +#if defined(PJ_SUNOS) && PJ_SUNOS!=0 +#include +static void init_signals() +{ + struct sigaction act; + + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + + sigaction(SIGALRM, &act, NULL); +} + +#else +#define init_signals() +#endif + +int main(int argc, char *argv[]) +{ + int rc; + + boost(); + init_signals(); + + while (argc > 1) { + char *arg = argv[--argc]; + + if (*arg=='-' && *(arg+1)=='p') { + pj_str_t port = pj_str(argv[--argc]); + + param_echo_port = pj_strtoul(&port); + + } else if (*arg=='-' && *(arg+1)=='s') { + param_echo_server = argv[--argc]; + + } else if (*arg=='-' && *(arg+1)=='t') { + pj_str_t type = pj_str(argv[--argc]); + + if (pj_stricmp2(&type, "tcp")==0) + param_echo_sock_type = PJ_SOCK_STREAM; + else if (pj_stricmp2(&type, "udp")==0) + param_echo_sock_type = PJ_SOCK_DGRAM; + else { + PJ_LOG(3,("", "error: unknown socket type %s", type.ptr)); + return 1; + } + } + } + + rc = test_main(); + + return rc; +} + diff --git a/pjlib/src/pjlib-test/main_mod.c b/pjlib/src/pjlib-test/main_mod.c index 454101842..26f5295cc 100644 --- a/pjlib/src/pjlib-test/main_mod.c +++ b/pjlib/src/pjlib-test/main_mod.c @@ -1,33 +1,33 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/main_mod.c 2 10/29/05 11:51a Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/main_mod.c $ - * - * 2 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 1 10/05/05 5:12p Bennylp - * Created. - * - */ -#include "test.h" -#include -#include - -int init_module(void) -{ - printk(KERN_INFO "PJLIB test module loaded. Starting tests...\n"); - - test_main(); - - /* Prevent module from loading. We've finished test anyway.. */ - return 1; -} - -void cleanup_module(void) -{ - printk(KERN_INFO "PJLIB test module unloading...\n"); -} - -MODULE_LICENSE("GPL"); - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/main_mod.c 2 10/29/05 11:51a Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/main_mod.c $ + * + * 2 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 1 10/05/05 5:12p Bennylp + * Created. + * + */ +#include "test.h" +#include +#include + +int init_module(void) +{ + printk(KERN_INFO "PJLIB test module loaded. Starting tests...\n"); + + test_main(); + + /* Prevent module from loading. We've finished test anyway.. */ + return 1; +} + +void cleanup_module(void) +{ + printk(KERN_INFO "PJLIB test module unloading...\n"); +} + +MODULE_LICENSE("GPL"); + diff --git a/pjlib/src/pjlib-test/mutex.c b/pjlib/src/pjlib-test/mutex.c index b6609b8d3..a6fabb380 100644 --- a/pjlib/src/pjlib-test/mutex.c +++ b/pjlib/src/pjlib-test/mutex.c @@ -1,164 +1,164 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/mutex.c 1 10/23/05 12:52p Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/mutex.c $ - * - * 1 10/23/05 12:52p Bennylp - * Craeted. - * - */ -#include "test.h" -#include - -#if INCLUDE_MUTEX_TEST - -#undef TRACE_ -//#define TRACE_(x) PJ_LOG(3,x) -#define TRACE_(x) - -/* Test witn non-recursive mutex. */ -static int simple_mutex_test(pj_pool_t *pool) -{ - pj_status_t rc; - pj_mutex_t *mutex; - - PJ_LOG(3,("", "...testing simple mutex")); - - /* Create mutex. */ - TRACE_(("", "....create mutex")); - rc = pj_mutex_create( pool, "", PJ_MUTEX_SIMPLE, &mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_mutex_create", rc); - return -10; - } - - /* Normal lock/unlock cycle. */ - TRACE_(("", "....lock mutex")); - rc = pj_mutex_lock(mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_mutex_lock", rc); - return -20; - } - TRACE_(("", "....unlock mutex")); - rc = pj_mutex_unlock(mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_mutex_unlock", rc); - return -30; - } - - /* Lock again. */ - TRACE_(("", "....lock mutex")); - rc = pj_mutex_lock(mutex); - if (rc != PJ_SUCCESS) return -40; - - /* Try-lock should fail. It should not deadlocked. */ - TRACE_(("", "....trylock mutex")); - rc = pj_mutex_trylock(mutex); - if (rc == PJ_SUCCESS) - PJ_LOG(3,("", "...info: looks like simple mutex is recursive")); - - /* Unlock and done. */ - TRACE_(("", "....unlock mutex")); - rc = pj_mutex_unlock(mutex); - if (rc != PJ_SUCCESS) return -50; - - TRACE_(("", "....destroy mutex")); - rc = pj_mutex_destroy(mutex); - if (rc != PJ_SUCCESS) return -60; - - TRACE_(("", "....done")); - return PJ_SUCCESS; -} - - -/* Test with recursive mutex. */ -static int recursive_mutex_test(pj_pool_t *pool) -{ - pj_status_t rc; - pj_mutex_t *mutex; - - PJ_LOG(3,("", "...testing recursive mutex")); - - /* Create mutex. */ - TRACE_(("", "....create mutex")); - rc = pj_mutex_create( pool, "", PJ_MUTEX_RECURSE, &mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_mutex_create", rc); - return -10; - } - - /* Normal lock/unlock cycle. */ - TRACE_(("", "....lock mutex")); - rc = pj_mutex_lock(mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_mutex_lock", rc); - return -20; - } - TRACE_(("", "....unlock mutex")); - rc = pj_mutex_unlock(mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_mutex_unlock", rc); - return -30; - } - - /* Lock again. */ - TRACE_(("", "....lock mutex")); - rc = pj_mutex_lock(mutex); - if (rc != PJ_SUCCESS) return -40; - - /* Try-lock should NOT fail. . */ - TRACE_(("", "....trylock mutex")); - rc = pj_mutex_trylock(mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: recursive mutex is not recursive!", rc); - return -40; - } - - /* Locking again should not fail. */ - TRACE_(("", "....lock mutex")); - rc = pj_mutex_lock(mutex); - if (rc != PJ_SUCCESS) { - app_perror("...error: recursive mutex is not recursive!", rc); - return -45; - } - - /* Unlock several times and done. */ - TRACE_(("", "....unlock mutex 3x")); - rc = pj_mutex_unlock(mutex); - if (rc != PJ_SUCCESS) return -50; - rc = pj_mutex_unlock(mutex); - if (rc != PJ_SUCCESS) return -51; - rc = pj_mutex_unlock(mutex); - if (rc != PJ_SUCCESS) return -52; - - TRACE_(("", "....destroy mutex")); - rc = pj_mutex_destroy(mutex); - if (rc != PJ_SUCCESS) return -60; - - TRACE_(("", "....done")); - return PJ_SUCCESS; -} - -int mutex_test(void) -{ - pj_pool_t *pool; - int rc; - - pool = pj_pool_create(mem, "", 4000, 4000, NULL); - - rc = simple_mutex_test(pool); - if (rc != 0) - return rc; - - rc = recursive_mutex_test(pool); - if (rc != 0) - return rc; - - pj_pool_release(pool); - - return 0; -} - -#else -int dummy_mutex_test; -#endif - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/mutex.c 1 10/23/05 12:52p Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/mutex.c $ + * + * 1 10/23/05 12:52p Bennylp + * Craeted. + * + */ +#include "test.h" +#include + +#if INCLUDE_MUTEX_TEST + +#undef TRACE_ +//#define TRACE_(x) PJ_LOG(3,x) +#define TRACE_(x) + +/* Test witn non-recursive mutex. */ +static int simple_mutex_test(pj_pool_t *pool) +{ + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_LOG(3,("", "...testing simple mutex")); + + /* Create mutex. */ + TRACE_(("", "....create mutex")); + rc = pj_mutex_create( pool, "", PJ_MUTEX_SIMPLE, &mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_mutex_create", rc); + return -10; + } + + /* Normal lock/unlock cycle. */ + TRACE_(("", "....lock mutex")); + rc = pj_mutex_lock(mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_mutex_lock", rc); + return -20; + } + TRACE_(("", "....unlock mutex")); + rc = pj_mutex_unlock(mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_mutex_unlock", rc); + return -30; + } + + /* Lock again. */ + TRACE_(("", "....lock mutex")); + rc = pj_mutex_lock(mutex); + if (rc != PJ_SUCCESS) return -40; + + /* Try-lock should fail. It should not deadlocked. */ + TRACE_(("", "....trylock mutex")); + rc = pj_mutex_trylock(mutex); + if (rc == PJ_SUCCESS) + PJ_LOG(3,("", "...info: looks like simple mutex is recursive")); + + /* Unlock and done. */ + TRACE_(("", "....unlock mutex")); + rc = pj_mutex_unlock(mutex); + if (rc != PJ_SUCCESS) return -50; + + TRACE_(("", "....destroy mutex")); + rc = pj_mutex_destroy(mutex); + if (rc != PJ_SUCCESS) return -60; + + TRACE_(("", "....done")); + return PJ_SUCCESS; +} + + +/* Test with recursive mutex. */ +static int recursive_mutex_test(pj_pool_t *pool) +{ + pj_status_t rc; + pj_mutex_t *mutex; + + PJ_LOG(3,("", "...testing recursive mutex")); + + /* Create mutex. */ + TRACE_(("", "....create mutex")); + rc = pj_mutex_create( pool, "", PJ_MUTEX_RECURSE, &mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_mutex_create", rc); + return -10; + } + + /* Normal lock/unlock cycle. */ + TRACE_(("", "....lock mutex")); + rc = pj_mutex_lock(mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_mutex_lock", rc); + return -20; + } + TRACE_(("", "....unlock mutex")); + rc = pj_mutex_unlock(mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_mutex_unlock", rc); + return -30; + } + + /* Lock again. */ + TRACE_(("", "....lock mutex")); + rc = pj_mutex_lock(mutex); + if (rc != PJ_SUCCESS) return -40; + + /* Try-lock should NOT fail. . */ + TRACE_(("", "....trylock mutex")); + rc = pj_mutex_trylock(mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: recursive mutex is not recursive!", rc); + return -40; + } + + /* Locking again should not fail. */ + TRACE_(("", "....lock mutex")); + rc = pj_mutex_lock(mutex); + if (rc != PJ_SUCCESS) { + app_perror("...error: recursive mutex is not recursive!", rc); + return -45; + } + + /* Unlock several times and done. */ + TRACE_(("", "....unlock mutex 3x")); + rc = pj_mutex_unlock(mutex); + if (rc != PJ_SUCCESS) return -50; + rc = pj_mutex_unlock(mutex); + if (rc != PJ_SUCCESS) return -51; + rc = pj_mutex_unlock(mutex); + if (rc != PJ_SUCCESS) return -52; + + TRACE_(("", "....destroy mutex")); + rc = pj_mutex_destroy(mutex); + if (rc != PJ_SUCCESS) return -60; + + TRACE_(("", "....done")); + return PJ_SUCCESS; +} + +int mutex_test(void) +{ + pj_pool_t *pool; + int rc; + + pool = pj_pool_create(mem, "", 4000, 4000, NULL); + + rc = simple_mutex_test(pool); + if (rc != 0) + return rc; + + rc = recursive_mutex_test(pool); + if (rc != 0) + return rc; + + pj_pool_release(pool); + + return 0; +} + +#else +int dummy_mutex_test; +#endif + diff --git a/pjlib/src/pjlib-test/os.c b/pjlib/src/pjlib-test/os.c index 893cfc698..6f35c7def 100644 --- a/pjlib/src/pjlib-test/os.c +++ b/pjlib/src/pjlib-test/os.c @@ -1,10 +1,10 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/os.c 2 10/14/05 12:26a Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/os.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - */ +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/os.c 2 10/14/05 12:26a Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/os.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + */ diff --git a/pjlib/src/pjlib-test/pool.c b/pjlib/src/pjlib-test/pool.c index 8b9d1ff0b..26d8e01f0 100644 --- a/pjlib/src/pjlib-test/pool.c +++ b/pjlib/src/pjlib-test/pool.c @@ -1,164 +1,164 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/pool.c 2 10/14/05 12:26a Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/pool.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - */ -#include -#include -#include -#include "test.h" - -/** - * \page page_pjlib_pool_test Test: Pool - * - * This file provides implementation of \b pool_test(). It tests the - * functionality of the memory pool. - * - * - * This file is pjlib-test/pool.c - * - * \include pjlib-test/pool.c - */ - - -#if INCLUDE_POOL_TEST - -#define SIZE 4096 - -/* Normally we should throw exception when memory alloc fails. - * Here we do nothing so that the flow will go back to original caller, - * which will test the result using NULL comparison. Normally caller will - * catch the exception instead of checking for NULLs. - */ -static void null_callback(pj_pool_t *pool, pj_size_t size) -{ - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(size); -} - -#define GET_FREE(p) (pj_pool_get_capacity(p)-pj_pool_get_used_size(p)) - -/* Test that the capacity and used size reported by the pool is correct. - */ -static int capacity_test(void) -{ - pj_pool_t *pool = pj_pool_create(mem, NULL, SIZE, 0, &null_callback); - pj_size_t freesize; - - PJ_LOG(3,("test", "...capacity_test()")); - - if (!pool) - return -200; - - freesize = GET_FREE(pool); - - if (pj_pool_alloc(pool, freesize) == NULL) { - PJ_LOG(3,("test", "...error: wrong freesize %u reported", - freesize)); - pj_pool_release(pool); - return -210; - } - - pj_pool_release(pool); - return 0; -} - -/* Test function to drain the pool's space. - */ -static int drain_test(pj_size_t size, pj_size_t increment) -{ - pj_pool_t *pool = pj_pool_create(mem, NULL, size, increment, - &null_callback); - pj_size_t freesize; - void *p; - int status = 0; - - PJ_LOG(3,("test", "...drain_test(%d,%d)", size, increment)); - - if (!pool) - return -10; - - /* Get free size */ - freesize = GET_FREE(pool); - if (freesize < 1) { - status=-15; - goto on_error; - } - - /* Drain the pool until there's nothing left. */ - while (freesize > 0) { - int size; - - if (freesize > 255) - size = ((pj_rand() & 0x000000FF) + 4) & ~0x03L; - else - size = freesize; - - p = pj_pool_alloc(pool, size); - if (!p) { - status=-20; goto on_error; - } - - freesize -= size; - } - - /* Check that capacity is zero. */ - if (GET_FREE(pool) != 0) { - PJ_LOG(3,("test", "....error: returned free=%u (expecting 0)", - GET_FREE(pool))); - status=-30; goto on_error; - } - - /* Try to allocate once more */ - p = pj_pool_alloc(pool, 257); - if (!p) { - status=-40; goto on_error; - } - - /* Check that capacity is NOT zero. */ - if (GET_FREE(pool) == 0) { - status=-50; goto on_error; - } - - -on_error: - pj_pool_release(pool); - return status; -} - -int pool_test(void) -{ - enum { LOOP = 2 }; - int loop; - int rc; - - rc = capacity_test(); - if (rc) return rc; - - for (loop=0; loop +#include +#include +#include "test.h" + +/** + * \page page_pjlib_pool_test Test: Pool + * + * This file provides implementation of \b pool_test(). It tests the + * functionality of the memory pool. + * + * + * This file is pjlib-test/pool.c + * + * \include pjlib-test/pool.c + */ + + +#if INCLUDE_POOL_TEST + +#define SIZE 4096 + +/* Normally we should throw exception when memory alloc fails. + * Here we do nothing so that the flow will go back to original caller, + * which will test the result using NULL comparison. Normally caller will + * catch the exception instead of checking for NULLs. + */ +static void null_callback(pj_pool_t *pool, pj_size_t size) +{ + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(size); +} + +#define GET_FREE(p) (pj_pool_get_capacity(p)-pj_pool_get_used_size(p)) + +/* Test that the capacity and used size reported by the pool is correct. + */ +static int capacity_test(void) +{ + pj_pool_t *pool = pj_pool_create(mem, NULL, SIZE, 0, &null_callback); + pj_size_t freesize; + + PJ_LOG(3,("test", "...capacity_test()")); + + if (!pool) + return -200; + + freesize = GET_FREE(pool); + + if (pj_pool_alloc(pool, freesize) == NULL) { + PJ_LOG(3,("test", "...error: wrong freesize %u reported", + freesize)); + pj_pool_release(pool); + return -210; + } + + pj_pool_release(pool); + return 0; +} + +/* Test function to drain the pool's space. + */ +static int drain_test(pj_size_t size, pj_size_t increment) +{ + pj_pool_t *pool = pj_pool_create(mem, NULL, size, increment, + &null_callback); + pj_size_t freesize; + void *p; + int status = 0; + + PJ_LOG(3,("test", "...drain_test(%d,%d)", size, increment)); + + if (!pool) + return -10; + + /* Get free size */ + freesize = GET_FREE(pool); + if (freesize < 1) { + status=-15; + goto on_error; + } + + /* Drain the pool until there's nothing left. */ + while (freesize > 0) { + int size; + + if (freesize > 255) + size = ((pj_rand() & 0x000000FF) + 4) & ~0x03L; + else + size = freesize; + + p = pj_pool_alloc(pool, size); + if (!p) { + status=-20; goto on_error; + } + + freesize -= size; + } + + /* Check that capacity is zero. */ + if (GET_FREE(pool) != 0) { + PJ_LOG(3,("test", "....error: returned free=%u (expecting 0)", + GET_FREE(pool))); + status=-30; goto on_error; + } + + /* Try to allocate once more */ + p = pj_pool_alloc(pool, 257); + if (!p) { + status=-40; goto on_error; + } + + /* Check that capacity is NOT zero. */ + if (GET_FREE(pool) == 0) { + status=-50; goto on_error; + } + + +on_error: + pj_pool_release(pool); + return status; +} + +int pool_test(void) +{ + enum { LOOP = 2 }; + int loop; + int rc; + + rc = capacity_test(); + if (rc) return rc; + + for (loop=0; loop -#include - -#if !PJ_HAS_HIGH_RES_TIMER -# error Need high resolution timer for this test. -#endif - -#define THIS_FILE "test" - -#define LOOP 10 -#define COUNT 1024 -static unsigned sizes[COUNT]; -#define MIN_SIZE 4 -#define MAX_SIZE 512 -static unsigned total_size; - -static int pool_test_pool() -{ - int i; - pj_pool_t *pool = pj_pool_create(mem, NULL, total_size + 4*COUNT, 0, NULL); - if (!pool) - return -1; - - for (i=0; i +#include + +#if !PJ_HAS_HIGH_RES_TIMER +# error Need high resolution timer for this test. +#endif + +#define THIS_FILE "test" + +#define LOOP 10 +#define COUNT 1024 +static unsigned sizes[COUNT]; +#define MIN_SIZE 4 +#define MAX_SIZE 512 +static unsigned total_size; + +static int pool_test_pool() +{ + int i; + pj_pool_t *pool = pj_pool_create(mem, NULL, total_size + 4*COUNT, 0, NULL); + if (!pool) + return -1; + + for (i=0; i -#include -#include "test.h" - -#if INCLUDE_RAND_TEST - -#define COUNT 1024 -static int values[COUNT]; - -/* - * rand_test(), simply generates COUNT number of random number and - * check that there's no duplicate numbers. - */ -int rand_test(void) -{ - int i; - - for (i=0; i +#include +#include "test.h" + +#if INCLUDE_RAND_TEST + +#define COUNT 1024 +static int values[COUNT]; + +/* + * rand_test(), simply generates COUNT number of random number and + * check that there's no duplicate numbers. + */ +int rand_test(void) +{ + int i; + + for (i=0; i - -#define LOOP 32 -#define MIN_COUNT 64 -#define MAX_COUNT (LOOP * MIN_COUNT) -#define STRSIZE 16 -#define THIS_FILE "rbtree_test" - -typedef struct node_key -{ - pj_uint32_t hash; - char str[STRSIZE]; -} node_key; - -static int compare_node(const node_key *k1, const node_key *k2) -{ - if (k1->hash == k2->hash) { - return strcmp(k1->str, k2->str); - } else { - return k1->hash < k2->hash ? -1 : 1; - } -} - -void randomize_string(char *str, int len) -{ - int i; - for (i=0; ikey,(node_key*)it->key)>=0) { - ++err; - PJ_LOG(3, (THIS_FILE, "Error: %s >= %s", - (char*)prev->user_data, (char*)it->user_data)); - } - } - prev = it; - it = pj_rbtree_next(&rb, it); - } - - // Search. - for (j=0; j MAX_COUNT) - break; - } - - pj_pool_release(pool); - return err; -} - - -int rbtree_test() -{ - return test(); -} - -#endif /* INCLUDE_RBTREE_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/rbtree.c 2 10/14/05 12:26a Bennylp $ */ +#include "test.h" + +#if INCLUDE_RBTREE_TEST + +#include + +#define LOOP 32 +#define MIN_COUNT 64 +#define MAX_COUNT (LOOP * MIN_COUNT) +#define STRSIZE 16 +#define THIS_FILE "rbtree_test" + +typedef struct node_key +{ + pj_uint32_t hash; + char str[STRSIZE]; +} node_key; + +static int compare_node(const node_key *k1, const node_key *k2) +{ + if (k1->hash == k2->hash) { + return strcmp(k1->str, k2->str); + } else { + return k1->hash < k2->hash ? -1 : 1; + } +} + +void randomize_string(char *str, int len) +{ + int i; + for (i=0; ikey,(node_key*)it->key)>=0) { + ++err; + PJ_LOG(3, (THIS_FILE, "Error: %s >= %s", + (char*)prev->user_data, (char*)it->user_data)); + } + } + prev = it; + it = pj_rbtree_next(&rb, it); + } + + // Search. + for (j=0; j MAX_COUNT) + break; + } + + pj_pool_release(pool); + return err; +} + + +int rbtree_test() +{ + return test(); +} + +#endif /* INCLUDE_RBTREE_TEST */ + + diff --git a/pjlib/src/pjlib-test/select.c b/pjlib/src/pjlib-test/select.c index e6562d2ca..eaaffcc55 100644 --- a/pjlib/src/pjlib-test/select.c +++ b/pjlib/src/pjlib-test/select.c @@ -1,208 +1,208 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/select.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/select.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - */ -#include "test.h" - -/** - * \page page_pjlib_select_test Test: Socket Select() - * - * This file provides implementation of \b select_test(). It tests the - * functionality of the pj_sock_select() API. - * - * - * This file is pjlib-test/select.c - * - * \include pjlib-test/select.c - */ - - -#if INCLUDE_SELECT_TEST - -#include -#include -#include -#include -#include -#include -#include - -enum -{ - READ_FDS, - WRITE_FDS, - EXCEPT_FDS -}; - -#define UDP_PORT 51232 -#define THIS_FILE "select_test" - -/* - * do_select() - * - * Perform pj_sock_select() and find out which sockets - * are signalled. - */ -static int do_select( pj_sock_t sock1, pj_sock_t sock2, - int setcount[]) -{ - pj_fd_set_t fds[3]; - pj_time_val timeout; - int i, n; - - for (i=0; i<3; ++i) { - PJ_FD_ZERO(&fds[i]); - PJ_FD_SET(sock1, &fds[i]); - PJ_FD_SET(sock2, &fds[i]); - setcount[i] = 0; - } - - timeout.sec = 1; - timeout.msec = 0; - - n = pj_sock_select(FD_SETSIZE, &fds[0], &fds[1], &fds[2], - &timeout); - if (n < 0) - return n; - if (n == 0) - return 0; - - for (i=0; i<3; ++i) { - if (PJ_FD_ISSET(sock1, &fds[i])) - setcount[i]++; - if (PJ_FD_ISSET(sock2, &fds[i])) - setcount[i]++; - } - - return n; -} - -/* - * select_test() - * - * Test main entry. - */ -int select_test() -{ - pj_sock_t udp1=PJ_INVALID_SOCKET, udp2=PJ_INVALID_SOCKET; - pj_sockaddr_in udp_addr; - int status; - int setcount[3]; - pj_str_t s; - const char data[] = "hello"; - const int datalen = 5; - pj_ssize_t sent, received; - char buf[10]; - pj_status_t rc; - - PJ_LOG(3, (THIS_FILE, "...Testing simple UDP select()")); - - // Create two UDP sockets. - rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp1); - if (rc != PJ_SUCCESS) { - app_perror("...error: unable to create socket", rc); - status=-10; goto on_return; - } - rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp2); - if (udp2 == PJ_INVALID_SOCKET) { - app_perror("...error: unable to create socket", rc); - status=-20; goto on_return; - } - - // Bind one of the UDP socket. - pj_memset(&udp_addr, 0, sizeof(udp_addr)); - udp_addr.sin_family = PJ_AF_INET; - udp_addr.sin_port = UDP_PORT; - udp_addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); - - if (pj_sock_bind(udp2, &udp_addr, sizeof(udp_addr))) { - status=-30; goto on_return; - } - - // Send data. - sent = datalen; - rc = pj_sock_sendto(udp1, data, &sent, 0, &udp_addr, sizeof(udp_addr)); - if (rc != PJ_SUCCESS || sent != datalen) { - app_perror("...error: sendto() error", rc); - status=-40; goto on_return; - } - - // Check that socket is marked as reable. - // Note that select() may also report that sockets are writable. - status = do_select(udp1, udp2, setcount); - if (status < 0) { - char errbuf[128]; - pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)); - PJ_LOG(1,(THIS_FILE, "...error: %s", errbuf)); - status=-50; goto on_return; - } - if (status == 0) { - status=-60; goto on_return; - } - - if (setcount[READ_FDS] != 1) { - status=-70; goto on_return; - } - if (setcount[WRITE_FDS] != 0) { - if (setcount[WRITE_FDS] == 2) { - PJ_LOG(3,(THIS_FILE, "...info: system reports writable sockets")); - } else { - status=-80; goto on_return; - } - } else { - PJ_LOG(3,(THIS_FILE, - "...info: system doesn't report writable sockets")); - } - if (setcount[EXCEPT_FDS] != 0) { - status=-90; goto on_return; - } - - // Read the socket to clear readable sockets. - received = sizeof(buf); - rc = pj_sock_recv(udp2, buf, &received, 0); - if (rc != PJ_SUCCESS || received != 5) { - status=-100; goto on_return; - } - - status = 0; - - // Test timeout on the read part. - // This won't necessarily return zero, as select() may report that - // sockets are writable. - setcount[0] = setcount[1] = setcount[2] = 0; - status = do_select(udp1, udp2, setcount); - if (status != 0 && status != setcount[WRITE_FDS]) { - PJ_LOG(3,(THIS_FILE, "...error: expecting timeout but got %d sks set", - status)); - PJ_LOG(3,(THIS_FILE, " rdset: %d, wrset: %d, exset: %d", - setcount[0], setcount[1], setcount[2])); - status = -110; goto on_return; - } - if (setcount[READ_FDS] != 0) { - PJ_LOG(3,(THIS_FILE, "...error: readable socket not expected")); - status = -120; goto on_return; - } - - status = 0; - -on_return: - if (udp1 != PJ_INVALID_SOCKET) - pj_sock_close(udp1); - if (udp2 != PJ_INVALID_SOCKET) - pj_sock_close(udp2); - return status; -} - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_select_test; -#endif /* INCLUDE_SELECT_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/select.c 2 10/14/05 12:26a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/select.c $ + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + */ +#include "test.h" + +/** + * \page page_pjlib_select_test Test: Socket Select() + * + * This file provides implementation of \b select_test(). It tests the + * functionality of the pj_sock_select() API. + * + * + * This file is pjlib-test/select.c + * + * \include pjlib-test/select.c + */ + + +#if INCLUDE_SELECT_TEST + +#include +#include +#include +#include +#include +#include +#include + +enum +{ + READ_FDS, + WRITE_FDS, + EXCEPT_FDS +}; + +#define UDP_PORT 51232 +#define THIS_FILE "select_test" + +/* + * do_select() + * + * Perform pj_sock_select() and find out which sockets + * are signalled. + */ +static int do_select( pj_sock_t sock1, pj_sock_t sock2, + int setcount[]) +{ + pj_fd_set_t fds[3]; + pj_time_val timeout; + int i, n; + + for (i=0; i<3; ++i) { + PJ_FD_ZERO(&fds[i]); + PJ_FD_SET(sock1, &fds[i]); + PJ_FD_SET(sock2, &fds[i]); + setcount[i] = 0; + } + + timeout.sec = 1; + timeout.msec = 0; + + n = pj_sock_select(FD_SETSIZE, &fds[0], &fds[1], &fds[2], + &timeout); + if (n < 0) + return n; + if (n == 0) + return 0; + + for (i=0; i<3; ++i) { + if (PJ_FD_ISSET(sock1, &fds[i])) + setcount[i]++; + if (PJ_FD_ISSET(sock2, &fds[i])) + setcount[i]++; + } + + return n; +} + +/* + * select_test() + * + * Test main entry. + */ +int select_test() +{ + pj_sock_t udp1=PJ_INVALID_SOCKET, udp2=PJ_INVALID_SOCKET; + pj_sockaddr_in udp_addr; + int status; + int setcount[3]; + pj_str_t s; + const char data[] = "hello"; + const int datalen = 5; + pj_ssize_t sent, received; + char buf[10]; + pj_status_t rc; + + PJ_LOG(3, (THIS_FILE, "...Testing simple UDP select()")); + + // Create two UDP sockets. + rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp1); + if (rc != PJ_SUCCESS) { + app_perror("...error: unable to create socket", rc); + status=-10; goto on_return; + } + rc = pj_sock_socket( PJ_AF_INET, PJ_SOCK_DGRAM, 0, &udp2); + if (udp2 == PJ_INVALID_SOCKET) { + app_perror("...error: unable to create socket", rc); + status=-20; goto on_return; + } + + // Bind one of the UDP socket. + pj_memset(&udp_addr, 0, sizeof(udp_addr)); + udp_addr.sin_family = PJ_AF_INET; + udp_addr.sin_port = UDP_PORT; + udp_addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); + + if (pj_sock_bind(udp2, &udp_addr, sizeof(udp_addr))) { + status=-30; goto on_return; + } + + // Send data. + sent = datalen; + rc = pj_sock_sendto(udp1, data, &sent, 0, &udp_addr, sizeof(udp_addr)); + if (rc != PJ_SUCCESS || sent != datalen) { + app_perror("...error: sendto() error", rc); + status=-40; goto on_return; + } + + // Check that socket is marked as reable. + // Note that select() may also report that sockets are writable. + status = do_select(udp1, udp2, setcount); + if (status < 0) { + char errbuf[128]; + pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf)); + PJ_LOG(1,(THIS_FILE, "...error: %s", errbuf)); + status=-50; goto on_return; + } + if (status == 0) { + status=-60; goto on_return; + } + + if (setcount[READ_FDS] != 1) { + status=-70; goto on_return; + } + if (setcount[WRITE_FDS] != 0) { + if (setcount[WRITE_FDS] == 2) { + PJ_LOG(3,(THIS_FILE, "...info: system reports writable sockets")); + } else { + status=-80; goto on_return; + } + } else { + PJ_LOG(3,(THIS_FILE, + "...info: system doesn't report writable sockets")); + } + if (setcount[EXCEPT_FDS] != 0) { + status=-90; goto on_return; + } + + // Read the socket to clear readable sockets. + received = sizeof(buf); + rc = pj_sock_recv(udp2, buf, &received, 0); + if (rc != PJ_SUCCESS || received != 5) { + status=-100; goto on_return; + } + + status = 0; + + // Test timeout on the read part. + // This won't necessarily return zero, as select() may report that + // sockets are writable. + setcount[0] = setcount[1] = setcount[2] = 0; + status = do_select(udp1, udp2, setcount); + if (status != 0 && status != setcount[WRITE_FDS]) { + PJ_LOG(3,(THIS_FILE, "...error: expecting timeout but got %d sks set", + status)); + PJ_LOG(3,(THIS_FILE, " rdset: %d, wrset: %d, exset: %d", + setcount[0], setcount[1], setcount[2])); + status = -110; goto on_return; + } + if (setcount[READ_FDS] != 0) { + PJ_LOG(3,(THIS_FILE, "...error: readable socket not expected")); + status = -120; goto on_return; + } + + status = 0; + +on_return: + if (udp1 != PJ_INVALID_SOCKET) + pj_sock_close(udp1); + if (udp2 != PJ_INVALID_SOCKET) + pj_sock_close(udp2); + return status; +} + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_select_test; +#endif /* INCLUDE_SELECT_TEST */ + + diff --git a/pjlib/src/pjlib-test/sleep.c b/pjlib/src/pjlib-test/sleep.c index 95fa3bac5..cb33104f4 100644 --- a/pjlib/src/pjlib-test/sleep.c +++ b/pjlib/src/pjlib-test/sleep.c @@ -1,198 +1,198 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/sleep.c 3 10/29/05 11:51a Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/sleep.c $ - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/11/05 12:53a Bennylp - * Created. - * - */ -#include "test.h" - -/** - * \page page_pjlib_sleep_test Test: Sleep, Time, and Timestamp - * - * This file provides implementation of \b sleep_test(). - * - * \section sleep_test_sec Scope of the Test - * - * This tests: - * - whether pj_thread_sleep() works. - * - whether pj_gettimeofday() works. - * - whether pj_get_timestamp() and friends works. - * - * API tested: - * - pj_thread_sleep() - * - pj_gettimeofday() - * - PJ_TIME_VAL_SUB() - * - PJ_TIME_VAL_LTE() - * - pj_get_timestamp() - * - pj_get_timestamp_freq() (implicitly) - * - pj_elapsed_time() - * - pj_elapsed_usec() - * - * - * This file is pjlib-test/sleep.c - * - * \include pjlib-test/sleep.c - */ - -#if INCLUDE_SLEEP_TEST - -#include - -#define THIS_FILE "sleep_test" - -static int simple_sleep_test(void) -{ - enum { COUNT = 5 }; - int i; - pj_status_t rc; - - PJ_LOG(3,(THIS_FILE, "..will write messages every 1 second:")); - - for (i=0; i DURATION * (100+MIS)/100) - { - PJ_LOG(3,(THIS_FILE, - "...error: slept for %d ms instead of %d ms " - "(outside %d%% err window)", - msec, DURATION, MIS)); - return -30; - } - } - - - /* Test pj_thread_sleep() and pj_get_timestamp() and friends */ - { - pj_time_val t1, t2; - pj_timestamp start, stop; - pj_time_val elapsed; - pj_uint32_t msec; - - /* Mark start of test. */ - rc = pj_get_timestamp(&start); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_get_timestamp()", rc); - return -60; - } - - /* ..also with gettimeofday() */ - pj_gettimeofday(&t1); - - /* Sleep */ - rc = pj_thread_sleep(DURATION2); - if (rc != PJ_SUCCESS) { - app_perror("...error: pj_thread_sleep()", rc); - return -70; - } - - /* Mark end of test. */ - pj_get_timestamp(&stop); - - /* ..also with gettimeofday() */ - pj_gettimeofday(&t2); - - /* Compare t1 and t2. */ - if (PJ_TIME_VAL_LTE(t2, t1)) { - PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!")); - return -75; - } - - /* Get elapsed time in time_val */ - elapsed = pj_elapsed_time(&start, &stop); - - msec = PJ_TIME_VAL_MSEC(elapsed); - - /* Check if it's within range. */ - if (msec < DURATION2 * (100-MIS)/100 || - msec > DURATION2 * (100+MIS)/100) - { - PJ_LOG(3,(THIS_FILE, - "...error: slept for %d ms instead of %d ms " - "(outside %d%% err window)", - msec, DURATION2, MIS)); - return -30; - } - } - - /* All done. */ - return 0; -} - -int sleep_test() -{ - int rc; - - rc = simple_sleep_test(); - if (rc != PJ_SUCCESS) - return rc; - - rc = sleep_duration_test(); - if (rc != PJ_SUCCESS) - return rc; - - return 0; -} - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_sleep_test; -#endif /* INCLUDE_SLEEP_TEST */ +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/sleep.c 3 10/29/05 11:51a Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/sleep.c $ + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/11/05 12:53a Bennylp + * Created. + * + */ +#include "test.h" + +/** + * \page page_pjlib_sleep_test Test: Sleep, Time, and Timestamp + * + * This file provides implementation of \b sleep_test(). + * + * \section sleep_test_sec Scope of the Test + * + * This tests: + * - whether pj_thread_sleep() works. + * - whether pj_gettimeofday() works. + * - whether pj_get_timestamp() and friends works. + * + * API tested: + * - pj_thread_sleep() + * - pj_gettimeofday() + * - PJ_TIME_VAL_SUB() + * - PJ_TIME_VAL_LTE() + * - pj_get_timestamp() + * - pj_get_timestamp_freq() (implicitly) + * - pj_elapsed_time() + * - pj_elapsed_usec() + * + * + * This file is pjlib-test/sleep.c + * + * \include pjlib-test/sleep.c + */ + +#if INCLUDE_SLEEP_TEST + +#include + +#define THIS_FILE "sleep_test" + +static int simple_sleep_test(void) +{ + enum { COUNT = 5 }; + int i; + pj_status_t rc; + + PJ_LOG(3,(THIS_FILE, "..will write messages every 1 second:")); + + for (i=0; i DURATION * (100+MIS)/100) + { + PJ_LOG(3,(THIS_FILE, + "...error: slept for %d ms instead of %d ms " + "(outside %d%% err window)", + msec, DURATION, MIS)); + return -30; + } + } + + + /* Test pj_thread_sleep() and pj_get_timestamp() and friends */ + { + pj_time_val t1, t2; + pj_timestamp start, stop; + pj_time_val elapsed; + pj_uint32_t msec; + + /* Mark start of test. */ + rc = pj_get_timestamp(&start); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_get_timestamp()", rc); + return -60; + } + + /* ..also with gettimeofday() */ + pj_gettimeofday(&t1); + + /* Sleep */ + rc = pj_thread_sleep(DURATION2); + if (rc != PJ_SUCCESS) { + app_perror("...error: pj_thread_sleep()", rc); + return -70; + } + + /* Mark end of test. */ + pj_get_timestamp(&stop); + + /* ..also with gettimeofday() */ + pj_gettimeofday(&t2); + + /* Compare t1 and t2. */ + if (PJ_TIME_VAL_LTE(t2, t1)) { + PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!")); + return -75; + } + + /* Get elapsed time in time_val */ + elapsed = pj_elapsed_time(&start, &stop); + + msec = PJ_TIME_VAL_MSEC(elapsed); + + /* Check if it's within range. */ + if (msec < DURATION2 * (100-MIS)/100 || + msec > DURATION2 * (100+MIS)/100) + { + PJ_LOG(3,(THIS_FILE, + "...error: slept for %d ms instead of %d ms " + "(outside %d%% err window)", + msec, DURATION2, MIS)); + return -30; + } + } + + /* All done. */ + return 0; +} + +int sleep_test() +{ + int rc; + + rc = simple_sleep_test(); + if (rc != PJ_SUCCESS) + return rc; + + rc = sleep_duration_test(); + if (rc != PJ_SUCCESS) + return rc; + + return 0; +} + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_sleep_test; +#endif /* INCLUDE_SLEEP_TEST */ diff --git a/pjlib/src/pjlib-test/sock.c b/pjlib/src/pjlib-test/sock.c index 9135a8bb7..c50c15cba 100644 --- a/pjlib/src/pjlib-test/sock.c +++ b/pjlib/src/pjlib-test/sock.c @@ -1,459 +1,459 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/sock.c 4 10/29/05 11:51a Bennylp $ */ -/* $Log: /pjproject-0.3/pjlib/src/pjlib-test/sock.c $ - * - * 4 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 3 14/10/05 11:31 Bennylp - * Fixed bug when TCP data is received in chunks. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 9/21/05 1:38p Bennylp - * Renamed from *.cpp - * - * 2 9/17/05 10:37a Bennylp - * Major reorganization towards version 0.3. - * - * 1 9/15/05 8:41p Bennylp - */ -#include -#include "test.h" - - -/** - * \page page_pjlib_sock_test Test: Socket - * - * This file provides implementation of \b sock_test(). It tests the - * various aspects of the socket API. - * - * \section sock_test_scope_sec Scope of the Test - * - * The scope of the test: - * - verify the validity of the address structs. - * - verify that address manipulation API works. - * - simple socket creation and destruction. - * - simple socket send/recv and sendto/recvfrom. - * - UDP connect() - * - send/recv big data. - * - all for both UDP and TCP. - * - * The APIs tested in this test: - * - pj_inet_aton() - * - pj_inet_ntoa() - * - pj_gethostname() - * - pj_sock_socket() - * - pj_sock_close() - * - pj_sock_send() - * - pj_sock_sendto() - * - pj_sock_recv() - * - pj_sock_recvfrom() - * - pj_sock_bind() - * - pj_sock_connect() - * - pj_sock_listen() - * - pj_sock_accept() - * - * - * This file is pjlib-test/sock.c - * - * \include pjlib-test/sock.c - */ - -#if INCLUDE_SOCK_TEST - -#define UDP_PORT 51234 -#define TCP_PORT (UDP_PORT+10) -#define BIG_DATA_LEN 9000 - -static char bigdata[BIG_DATA_LEN]; -static char bigbuffer[BIG_DATA_LEN]; - -static int format_test(void) -{ - pj_str_t s = pj_str("127.0.0.1"); - char *p; - pj_in_addr addr; - const pj_str_t *hostname; - - PJ_LOG(3,("test", "...format_test()")); - - /* pj_inet_aton() */ - if (pj_inet_aton(&s, &addr) != 1) - return -10; - - /* Check the result. */ - p = (char*)&addr; - if (p[0]!=127 || p[1]!=0 || p[2]!=0 || p[3]!=1) - return -15; - - /* pj_inet_ntoa() */ - p = pj_inet_ntoa(addr); - if (!p) - return -20; - - if (pj_strcmp2(&s, p) != 0) - return -30; - - /* pj_gethostname() */ - hostname = pj_gethostname(); - if (!hostname || !hostname->ptr || !hostname->slen) - return -40; - - /* pj_gethostaddr() */ - - return 0; -} - -static int simple_sock_test(void) -{ - int types[2]; - pj_sock_t sock; - int i; - pj_status_t rc = PJ_SUCCESS; - - types[0] = PJ_SOCK_STREAM; - types[1] = PJ_SOCK_DGRAM; - - PJ_LOG(3,("test", "...simple_sock_test()")); - - for (i=0; isin_addr)); - strcpy(addr_str, pj_inet_ntoa(addr.sin_addr)); - PJ_LOG(3,("test", "...error: src address mismatch (original=%s, " - "recvfrom addr=%s)", - srcaddr_str, addr_str)); - return -152; - } - - } else { - /* Repeat recv() until all data is received. - * This applies only for non-UDP of course, since for UDP - * we would expect all data to be received in one packet. - */ - total_received = 0; - do { - received = DATA_LEN-total_received; - rc = pj_sock_recv(ss, recvdata+total_received, &received, 0); - if (rc != PJ_SUCCESS) { - app_perror("...recv error", rc); - rc = -155; goto on_error; - } - if (received <= 0) { - PJ_LOG(3,("", "...error: socket has closed! (received=%d)", - received)); - rc = -156; goto on_error; - } - if (received != DATA_LEN-total_received) { - if (sock_type != PJ_SOCK_STREAM) { - PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", - DATA_LEN-total_received, received)); - rc = -157; goto on_error; - } - } - total_received += received; - } while (total_received < DATA_LEN); - } - - TRACE_(("test", "....memcmp()")); - if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) { - PJ_LOG(3,("","...error: received data mismatch " - "(got:'%s' expecting:'%s'", - recvdata, senddata)); - rc = -160; goto on_error; - } - - /* - * Test send/recv big data. - */ - TRACE_(("test", "....sendto()")); - if (dstaddr) { - sent = BIG_DATA_LEN; - rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen); - if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { - app_perror("...sendto error", rc); - rc = -161; goto on_error; - } - } else { - sent = BIG_DATA_LEN; - rc = pj_sock_send(cs, bigdata, &sent, 0); - if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { - app_perror("...send error", rc); - rc = -165; goto on_error; - } - } - - TRACE_(("test", "....recv()")); - - /* Repeat recv() until all data is received. - * This applies only for non-UDP of course, since for UDP - * we would expect all data to be received in one packet. - */ - total_received = 0; - do { - received = BIG_DATA_LEN-total_received; - rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0); - if (rc != PJ_SUCCESS) { - app_perror("...recv error", rc); - rc = -170; goto on_error; - } - if (received <= 0) { - PJ_LOG(3,("", "...error: socket has closed! (received=%d)", - received)); - rc = -173; goto on_error; - } - if (received != BIG_DATA_LEN-total_received) { - if (sock_type != PJ_SOCK_STREAM) { - PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", - BIG_DATA_LEN-total_received, received)); - rc = -176; goto on_error; - } - } - total_received += received; - } while (total_received < BIG_DATA_LEN); - - TRACE_(("test", "....memcmp()")); - if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) { - PJ_LOG(3,("", "...error: received data has been altered!")); - rc = -180; goto on_error; - } - - rc = 0; - -on_error: - return rc; -} - -static int udp_test(void) -{ - pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET; - pj_sockaddr_in dstaddr, srcaddr; - pj_str_t s; - pj_status_t rc = 0, retval; - - PJ_LOG(3,("test", "...udp_test()")); - - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ss); - if (rc != 0) { - app_perror("...error: unable to create socket", rc); - return -100; - } - - rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &cs); - if (rc != 0) - return -110; - - /* Bind server socket. */ - pj_memset(&dstaddr, 0, sizeof(dstaddr)); - dstaddr.sin_family = PJ_AF_INET; - dstaddr.sin_port = pj_htons(UDP_PORT); - dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); - - if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) { - app_perror("...bind error", rc); - rc = -120; goto on_error; - } - - /* Bind client socket. */ - pj_memset(&srcaddr, 0, sizeof(srcaddr)); - srcaddr.sin_family = PJ_AF_INET; - srcaddr.sin_port = pj_htons(UDP_PORT-1); - srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); - - if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) { - app_perror("...bind error", rc); - rc = -121; goto on_error; - } - - /* Test send/recv, with sendto */ - rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, NULL, - sizeof(dstaddr)); - if (rc != 0) - goto on_error; - - /* Test send/recv, with sendto and recvfrom */ - rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, - &srcaddr, sizeof(dstaddr)); - if (rc != 0) - goto on_error; - - /* connect() the sockets. */ - rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr)); - if (rc != 0) { - app_perror("...connect() error", rc); - rc = -122; goto on_error; - } - - /* Test send/recv with send() */ - rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, NULL, 0); - if (rc != 0) - goto on_error; - - /* Test send/recv with send() and recvfrom */ - rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, &srcaddr, - sizeof(srcaddr)); - if (rc != 0) - goto on_error; - -on_error: - retval = rc; - if (cs != PJ_INVALID_SOCKET) { - rc = pj_sock_close(cs); - if (rc != PJ_SUCCESS) { - app_perror("...error in closing socket", rc); - return -1000; - } - } - if (ss != PJ_INVALID_SOCKET) { - rc = pj_sock_close(ss); - if (rc != PJ_SUCCESS) { - app_perror("...error in closing socket", rc); - return -1010; - } - } - - return retval; -} - -static int tcp_test(void) -{ - pj_sock_t cs, ss; - pj_status_t rc = 0, retval; - - PJ_LOG(3,("test", "...tcp_test()")); - - rc = app_socketpair(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ss, &cs); - if (rc != PJ_SUCCESS) { - app_perror("...error: app_socketpair():", rc); - return -2000; - } - - /* Test send/recv with send() and recv() */ - retval = send_recv_test(PJ_SOCK_STREAM, ss, cs, NULL, NULL, 0); - - rc = pj_sock_close(cs); - if (rc != PJ_SUCCESS) { - app_perror("...error in closing socket", rc); - return -2000; - } - - rc = pj_sock_close(ss); - if (rc != PJ_SUCCESS) { - app_perror("...error in closing socket", rc); - return -2010; - } - - return retval; -} - -static int ioctl_test(void) -{ - return 0; -} - -int sock_test() -{ - int rc; - - pj_create_random_string(bigdata, BIG_DATA_LEN); - - rc = format_test(); - if (rc != 0) - return rc; - - rc = simple_sock_test(); - if (rc != 0) - return rc; - - rc = ioctl_test(); - if (rc != 0) - return rc; - - rc = udp_test(); - if (rc != 0) - return rc; - - rc = tcp_test(); - if (rc != 0) - return rc; - - return 0; -} - - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_sock_test; -#endif /* INCLUDE_SOCK_TEST */ - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/sock.c 4 10/29/05 11:51a Bennylp $ */ +/* $Log: /pjproject-0.3/pjlib/src/pjlib-test/sock.c $ + * + * 4 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 3 14/10/05 11:31 Bennylp + * Fixed bug when TCP data is received in chunks. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 9/21/05 1:38p Bennylp + * Renamed from *.cpp + * + * 2 9/17/05 10:37a Bennylp + * Major reorganization towards version 0.3. + * + * 1 9/15/05 8:41p Bennylp + */ +#include +#include "test.h" + + +/** + * \page page_pjlib_sock_test Test: Socket + * + * This file provides implementation of \b sock_test(). It tests the + * various aspects of the socket API. + * + * \section sock_test_scope_sec Scope of the Test + * + * The scope of the test: + * - verify the validity of the address structs. + * - verify that address manipulation API works. + * - simple socket creation and destruction. + * - simple socket send/recv and sendto/recvfrom. + * - UDP connect() + * - send/recv big data. + * - all for both UDP and TCP. + * + * The APIs tested in this test: + * - pj_inet_aton() + * - pj_inet_ntoa() + * - pj_gethostname() + * - pj_sock_socket() + * - pj_sock_close() + * - pj_sock_send() + * - pj_sock_sendto() + * - pj_sock_recv() + * - pj_sock_recvfrom() + * - pj_sock_bind() + * - pj_sock_connect() + * - pj_sock_listen() + * - pj_sock_accept() + * + * + * This file is pjlib-test/sock.c + * + * \include pjlib-test/sock.c + */ + +#if INCLUDE_SOCK_TEST + +#define UDP_PORT 51234 +#define TCP_PORT (UDP_PORT+10) +#define BIG_DATA_LEN 9000 + +static char bigdata[BIG_DATA_LEN]; +static char bigbuffer[BIG_DATA_LEN]; + +static int format_test(void) +{ + pj_str_t s = pj_str("127.0.0.1"); + char *p; + pj_in_addr addr; + const pj_str_t *hostname; + + PJ_LOG(3,("test", "...format_test()")); + + /* pj_inet_aton() */ + if (pj_inet_aton(&s, &addr) != 1) + return -10; + + /* Check the result. */ + p = (char*)&addr; + if (p[0]!=127 || p[1]!=0 || p[2]!=0 || p[3]!=1) + return -15; + + /* pj_inet_ntoa() */ + p = pj_inet_ntoa(addr); + if (!p) + return -20; + + if (pj_strcmp2(&s, p) != 0) + return -30; + + /* pj_gethostname() */ + hostname = pj_gethostname(); + if (!hostname || !hostname->ptr || !hostname->slen) + return -40; + + /* pj_gethostaddr() */ + + return 0; +} + +static int simple_sock_test(void) +{ + int types[2]; + pj_sock_t sock; + int i; + pj_status_t rc = PJ_SUCCESS; + + types[0] = PJ_SOCK_STREAM; + types[1] = PJ_SOCK_DGRAM; + + PJ_LOG(3,("test", "...simple_sock_test()")); + + for (i=0; isin_addr)); + strcpy(addr_str, pj_inet_ntoa(addr.sin_addr)); + PJ_LOG(3,("test", "...error: src address mismatch (original=%s, " + "recvfrom addr=%s)", + srcaddr_str, addr_str)); + return -152; + } + + } else { + /* Repeat recv() until all data is received. + * This applies only for non-UDP of course, since for UDP + * we would expect all data to be received in one packet. + */ + total_received = 0; + do { + received = DATA_LEN-total_received; + rc = pj_sock_recv(ss, recvdata+total_received, &received, 0); + if (rc != PJ_SUCCESS) { + app_perror("...recv error", rc); + rc = -155; goto on_error; + } + if (received <= 0) { + PJ_LOG(3,("", "...error: socket has closed! (received=%d)", + received)); + rc = -156; goto on_error; + } + if (received != DATA_LEN-total_received) { + if (sock_type != PJ_SOCK_STREAM) { + PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", + DATA_LEN-total_received, received)); + rc = -157; goto on_error; + } + } + total_received += received; + } while (total_received < DATA_LEN); + } + + TRACE_(("test", "....memcmp()")); + if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) { + PJ_LOG(3,("","...error: received data mismatch " + "(got:'%s' expecting:'%s'", + recvdata, senddata)); + rc = -160; goto on_error; + } + + /* + * Test send/recv big data. + */ + TRACE_(("test", "....sendto()")); + if (dstaddr) { + sent = BIG_DATA_LEN; + rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen); + if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { + app_perror("...sendto error", rc); + rc = -161; goto on_error; + } + } else { + sent = BIG_DATA_LEN; + rc = pj_sock_send(cs, bigdata, &sent, 0); + if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) { + app_perror("...send error", rc); + rc = -165; goto on_error; + } + } + + TRACE_(("test", "....recv()")); + + /* Repeat recv() until all data is received. + * This applies only for non-UDP of course, since for UDP + * we would expect all data to be received in one packet. + */ + total_received = 0; + do { + received = BIG_DATA_LEN-total_received; + rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0); + if (rc != PJ_SUCCESS) { + app_perror("...recv error", rc); + rc = -170; goto on_error; + } + if (received <= 0) { + PJ_LOG(3,("", "...error: socket has closed! (received=%d)", + received)); + rc = -173; goto on_error; + } + if (received != BIG_DATA_LEN-total_received) { + if (sock_type != PJ_SOCK_STREAM) { + PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes", + BIG_DATA_LEN-total_received, received)); + rc = -176; goto on_error; + } + } + total_received += received; + } while (total_received < BIG_DATA_LEN); + + TRACE_(("test", "....memcmp()")); + if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) { + PJ_LOG(3,("", "...error: received data has been altered!")); + rc = -180; goto on_error; + } + + rc = 0; + +on_error: + return rc; +} + +static int udp_test(void) +{ + pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET; + pj_sockaddr_in dstaddr, srcaddr; + pj_str_t s; + pj_status_t rc = 0, retval; + + PJ_LOG(3,("test", "...udp_test()")); + + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &ss); + if (rc != 0) { + app_perror("...error: unable to create socket", rc); + return -100; + } + + rc = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &cs); + if (rc != 0) + return -110; + + /* Bind server socket. */ + pj_memset(&dstaddr, 0, sizeof(dstaddr)); + dstaddr.sin_family = PJ_AF_INET; + dstaddr.sin_port = pj_htons(UDP_PORT); + dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); + + if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) { + app_perror("...bind error", rc); + rc = -120; goto on_error; + } + + /* Bind client socket. */ + pj_memset(&srcaddr, 0, sizeof(srcaddr)); + srcaddr.sin_family = PJ_AF_INET; + srcaddr.sin_port = pj_htons(UDP_PORT-1); + srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); + + if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) { + app_perror("...bind error", rc); + rc = -121; goto on_error; + } + + /* Test send/recv, with sendto */ + rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, NULL, + sizeof(dstaddr)); + if (rc != 0) + goto on_error; + + /* Test send/recv, with sendto and recvfrom */ + rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, &dstaddr, + &srcaddr, sizeof(dstaddr)); + if (rc != 0) + goto on_error; + + /* connect() the sockets. */ + rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr)); + if (rc != 0) { + app_perror("...connect() error", rc); + rc = -122; goto on_error; + } + + /* Test send/recv with send() */ + rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, NULL, 0); + if (rc != 0) + goto on_error; + + /* Test send/recv with send() and recvfrom */ + rc = send_recv_test(PJ_SOCK_DGRAM, ss, cs, NULL, &srcaddr, + sizeof(srcaddr)); + if (rc != 0) + goto on_error; + +on_error: + retval = rc; + if (cs != PJ_INVALID_SOCKET) { + rc = pj_sock_close(cs); + if (rc != PJ_SUCCESS) { + app_perror("...error in closing socket", rc); + return -1000; + } + } + if (ss != PJ_INVALID_SOCKET) { + rc = pj_sock_close(ss); + if (rc != PJ_SUCCESS) { + app_perror("...error in closing socket", rc); + return -1010; + } + } + + return retval; +} + +static int tcp_test(void) +{ + pj_sock_t cs, ss; + pj_status_t rc = 0, retval; + + PJ_LOG(3,("test", "...tcp_test()")); + + rc = app_socketpair(PJ_AF_INET, PJ_SOCK_STREAM, 0, &ss, &cs); + if (rc != PJ_SUCCESS) { + app_perror("...error: app_socketpair():", rc); + return -2000; + } + + /* Test send/recv with send() and recv() */ + retval = send_recv_test(PJ_SOCK_STREAM, ss, cs, NULL, NULL, 0); + + rc = pj_sock_close(cs); + if (rc != PJ_SUCCESS) { + app_perror("...error in closing socket", rc); + return -2000; + } + + rc = pj_sock_close(ss); + if (rc != PJ_SUCCESS) { + app_perror("...error in closing socket", rc); + return -2010; + } + + return retval; +} + +static int ioctl_test(void) +{ + return 0; +} + +int sock_test() +{ + int rc; + + pj_create_random_string(bigdata, BIG_DATA_LEN); + + rc = format_test(); + if (rc != 0) + return rc; + + rc = simple_sock_test(); + if (rc != 0) + return rc; + + rc = ioctl_test(); + if (rc != 0) + return rc; + + rc = udp_test(); + if (rc != 0) + return rc; + + rc = tcp_test(); + if (rc != 0) + return rc; + + return 0; +} + + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_sock_test; +#endif /* INCLUDE_SOCK_TEST */ + diff --git a/pjlib/src/pjlib-test/sock_perf.c b/pjlib/src/pjlib-test/sock_perf.c index 9e800432b..c20016d66 100644 --- a/pjlib/src/pjlib-test/sock_perf.c +++ b/pjlib/src/pjlib-test/sock_perf.c @@ -1,183 +1,183 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/sock_perf.c 4 10/29/05 11:51a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/sock_perf.c $ - * - * 4 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 3 14/10/05 11:31 Bennylp - * Fixed bug when TCP data is part_received in chunks. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/11/05 11:18p Bennylp - * Created. - * - */ -#include "test.h" -#include -#include - - -/** - * \page page_pjlib_sock_perf_test Test: Socket Performance - * - * Test the performance of the socket communication. This will perform - * simple producer-consumer type of test, where we calculate how long - * does it take to send certain number of packets from producer to - * consumer. - * - * This file is pjlib-test/sock_perf.c - * - * \include pjlib-test/sock_perf.c - */ - -#if INCLUDE_SOCK_PERF_TEST - -/* - * sock_producer_consumer() - * - * Simple producer-consumer benchmarking. Send loop number of - * buf_size size packets as fast as possible. - */ -static int sock_producer_consumer(int sock_type, - unsigned buf_size, - unsigned loop, - unsigned *p_bandwidth) -{ - pj_sock_t consumer, producer; - pj_pool_t *pool; - char *outgoing_buffer, *incoming_buffer; - pj_timestamp start, stop; - unsigned i; - pj_highprec_t elapsed, bandwidth; - pj_size_t total_received; - pj_status_t rc; - - /* Create pool. */ - pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); - if (!pool) - return -10; - - /* Create producer-consumer pair. */ - rc = app_socketpair(PJ_AF_INET, sock_type, 0, &consumer, &producer); - if (rc != PJ_SUCCESS) { - app_perror("...error: create socket pair", rc); - return -20; - } - - /* Create buffers. */ - outgoing_buffer = pj_pool_alloc(pool, buf_size); - incoming_buffer = pj_pool_alloc(pool, buf_size); - - /* Start loop. */ - pj_get_timestamp(&start); - total_received = 0; - for (i=0; i 10) - break; - } - - /* Stop timer. */ - pj_get_timestamp(&stop); - - elapsed = pj_elapsed_usec(&start, &stop); - - /* bandwidth = total_received * 1000 / elapsed */ - bandwidth = total_received; - pj_highprec_mul(bandwidth, 1000); - pj_highprec_div(bandwidth, elapsed); - - *p_bandwidth = (pj_uint32_t)bandwidth; - - /* Close sockets. */ - pj_sock_close(consumer); - pj_sock_close(producer); - - /* Done */ - pj_pool_release(pool); - - return 0; -} - -/* - * sock_perf_test() - * - * Main test entry. - */ -int sock_perf_test(void) -{ - enum { LOOP = 64 * 1024 }; - int rc; - unsigned bandwidth; - - PJ_LOG(3,("", "...benchmarking socket " - "(2 sockets, packet=512, single threaded):")); - - /* Benchmarking UDP */ - rc = sock_producer_consumer(PJ_SOCK_DGRAM, 512, LOOP, &bandwidth); - if (rc != 0) return rc; - PJ_LOG(3,("", "....bandwidth UDP = %d KB/s", bandwidth)); - - /* Benchmarking TCP */ - rc = sock_producer_consumer(PJ_SOCK_STREAM, 512, LOOP, &bandwidth); - if (rc != 0) return rc; - PJ_LOG(3,("", "....bandwidth TCP = %d KB/s", bandwidth)); - - return rc; -} - - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_sock_perf_test; -#endif /* INCLUDE_SOCK_PERF_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/sock_perf.c 4 10/29/05 11:51a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/sock_perf.c $ + * + * 4 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 3 14/10/05 11:31 Bennylp + * Fixed bug when TCP data is part_received in chunks. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/11/05 11:18p Bennylp + * Created. + * + */ +#include "test.h" +#include +#include + + +/** + * \page page_pjlib_sock_perf_test Test: Socket Performance + * + * Test the performance of the socket communication. This will perform + * simple producer-consumer type of test, where we calculate how long + * does it take to send certain number of packets from producer to + * consumer. + * + * This file is pjlib-test/sock_perf.c + * + * \include pjlib-test/sock_perf.c + */ + +#if INCLUDE_SOCK_PERF_TEST + +/* + * sock_producer_consumer() + * + * Simple producer-consumer benchmarking. Send loop number of + * buf_size size packets as fast as possible. + */ +static int sock_producer_consumer(int sock_type, + unsigned buf_size, + unsigned loop, + unsigned *p_bandwidth) +{ + pj_sock_t consumer, producer; + pj_pool_t *pool; + char *outgoing_buffer, *incoming_buffer; + pj_timestamp start, stop; + unsigned i; + pj_highprec_t elapsed, bandwidth; + pj_size_t total_received; + pj_status_t rc; + + /* Create pool. */ + pool = pj_pool_create(mem, NULL, 4096, 4096, NULL); + if (!pool) + return -10; + + /* Create producer-consumer pair. */ + rc = app_socketpair(PJ_AF_INET, sock_type, 0, &consumer, &producer); + if (rc != PJ_SUCCESS) { + app_perror("...error: create socket pair", rc); + return -20; + } + + /* Create buffers. */ + outgoing_buffer = pj_pool_alloc(pool, buf_size); + incoming_buffer = pj_pool_alloc(pool, buf_size); + + /* Start loop. */ + pj_get_timestamp(&start); + total_received = 0; + for (i=0; i 10) + break; + } + + /* Stop timer. */ + pj_get_timestamp(&stop); + + elapsed = pj_elapsed_usec(&start, &stop); + + /* bandwidth = total_received * 1000 / elapsed */ + bandwidth = total_received; + pj_highprec_mul(bandwidth, 1000); + pj_highprec_div(bandwidth, elapsed); + + *p_bandwidth = (pj_uint32_t)bandwidth; + + /* Close sockets. */ + pj_sock_close(consumer); + pj_sock_close(producer); + + /* Done */ + pj_pool_release(pool); + + return 0; +} + +/* + * sock_perf_test() + * + * Main test entry. + */ +int sock_perf_test(void) +{ + enum { LOOP = 64 * 1024 }; + int rc; + unsigned bandwidth; + + PJ_LOG(3,("", "...benchmarking socket " + "(2 sockets, packet=512, single threaded):")); + + /* Benchmarking UDP */ + rc = sock_producer_consumer(PJ_SOCK_DGRAM, 512, LOOP, &bandwidth); + if (rc != 0) return rc; + PJ_LOG(3,("", "....bandwidth UDP = %d KB/s", bandwidth)); + + /* Benchmarking TCP */ + rc = sock_producer_consumer(PJ_SOCK_STREAM, 512, LOOP, &bandwidth); + if (rc != 0) return rc; + PJ_LOG(3,("", "....bandwidth TCP = %d KB/s", bandwidth)); + + return rc; +} + + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_sock_perf_test; +#endif /* INCLUDE_SOCK_PERF_TEST */ + + diff --git a/pjlib/src/pjlib-test/string.c b/pjlib/src/pjlib-test/string.c index 1a3de3256..3714ecaa6 100644 --- a/pjlib/src/pjlib-test/string.c +++ b/pjlib/src/pjlib-test/string.c @@ -1,168 +1,168 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/string.c 2 10/14/05 12:26a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/string.c $ - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/05/05 5:13p Bennylp - * Created. - * - */ -#include -#include -#include -#include "test.h" - -/** - * \page page_pjlib_string_test Test: String - * - * This file provides implementation of \b string_test(). It tests the - * functionality of the string API. - * - * \section sleep_test_sec Scope of the Test - * - * API tested: - * - pj_str() - * - pj_strcmp() - * - pj_strcmp2() - * - pj_stricmp() - * - pj_strlen() - * - pj_strncmp() - * - pj_strnicmp() - * - pj_strchr() - * - pj_strdup() - * - pj_strdup2() - * - pj_strcpy() - * - pj_strcat() - * - pj_strtrim() - * - pj_utoa() - * - pj_strtoul() - * - pj_create_random_string() - * - * - * This file is pjlib-test/string.c - * - * \include pjlib-test/string.c - */ - -#if INCLUDE_STRING_TEST - -#ifdef _MSC_VER -# pragma warning(disable: 4204) -#endif - -#define HELLO_WORLD "Hello World" -#define JUST_HELLO "Hello" -#define UL_VALUE 3456789012UL - -int string_test(void) -{ - const pj_str_t hello_world = { HELLO_WORLD, strlen(HELLO_WORLD) }; - const pj_str_t just_hello = { JUST_HELLO, strlen(JUST_HELLO) }; - pj_str_t s1, s2, s3, s4, s5; - enum { RCOUNT = 10, RLEN = 16 }; - pj_str_t random[RCOUNT]; - pj_pool_t *pool; - int i; - - pool = pj_pool_create(mem, NULL, 4096, 0, NULL); - if (!pool) return -5; - - /* - * pj_str(), pj_strcmp(), pj_stricmp(), pj_strlen(), - * pj_strncmp(), pj_strchr() - */ - s1 = pj_str(HELLO_WORLD); - if (pj_strcmp(&s1, &hello_world) != 0) - return -10; - if (pj_stricmp(&s1, &hello_world) != 0) - return -20; - if (pj_strcmp(&s1, &just_hello) <= 0) - return -30; - if (pj_stricmp(&s1, &just_hello) <= 0) - return -40; - if (pj_strlen(&s1) != strlen(HELLO_WORLD)) - return -50; - if (pj_strncmp(&s1, &hello_world, 5) != 0) - return -60; - if (pj_strnicmp(&s1, &hello_world, 5) != 0) - return -70; - if (pj_strchr(&s1, HELLO_WORLD[1]) != s1.ptr+1) - return -80; - - /* - * pj_strdup() - */ - if (!pj_strdup(pool, &s2, &s1)) - return -100; - if (pj_strcmp(&s1, &s2) != 0) - return -110; - - /* - * pj_strcpy(), pj_strcat() - */ - s3.ptr = pj_pool_alloc(pool, 256); - if (!s3.ptr) - return -200; - pj_strcpy(&s3, &s2); - pj_strcat(&s3, &just_hello); - - if (pj_strcmp2(&s3, HELLO_WORLD JUST_HELLO) != 0) - return -210; - - /* - * pj_strdup2(), pj_strtrim(). - */ - pj_strdup2(pool, &s4, " " HELLO_WORLD "\t "); - pj_strtrim(&s4); - if (pj_strcmp2(&s4, HELLO_WORLD) != 0) - return -250; - - /* - * pj_utoa() - */ - s5.ptr = pj_pool_alloc(pool, 16); - if (!s5.ptr) - return -270; - s5.slen = pj_utoa(UL_VALUE, s5.ptr); - - /* - * pj_strtoul() - */ - if (pj_strtoul(&s5) != UL_VALUE) - return -280; - - /* - * pj_create_random_string() - * Check that no duplicate strings are returned. - */ - for (i=0; i +#include +#include +#include "test.h" + +/** + * \page page_pjlib_string_test Test: String + * + * This file provides implementation of \b string_test(). It tests the + * functionality of the string API. + * + * \section sleep_test_sec Scope of the Test + * + * API tested: + * - pj_str() + * - pj_strcmp() + * - pj_strcmp2() + * - pj_stricmp() + * - pj_strlen() + * - pj_strncmp() + * - pj_strnicmp() + * - pj_strchr() + * - pj_strdup() + * - pj_strdup2() + * - pj_strcpy() + * - pj_strcat() + * - pj_strtrim() + * - pj_utoa() + * - pj_strtoul() + * - pj_create_random_string() + * + * + * This file is pjlib-test/string.c + * + * \include pjlib-test/string.c + */ + +#if INCLUDE_STRING_TEST + +#ifdef _MSC_VER +# pragma warning(disable: 4204) +#endif + +#define HELLO_WORLD "Hello World" +#define JUST_HELLO "Hello" +#define UL_VALUE 3456789012UL + +int string_test(void) +{ + const pj_str_t hello_world = { HELLO_WORLD, strlen(HELLO_WORLD) }; + const pj_str_t just_hello = { JUST_HELLO, strlen(JUST_HELLO) }; + pj_str_t s1, s2, s3, s4, s5; + enum { RCOUNT = 10, RLEN = 16 }; + pj_str_t random[RCOUNT]; + pj_pool_t *pool; + int i; + + pool = pj_pool_create(mem, NULL, 4096, 0, NULL); + if (!pool) return -5; + + /* + * pj_str(), pj_strcmp(), pj_stricmp(), pj_strlen(), + * pj_strncmp(), pj_strchr() + */ + s1 = pj_str(HELLO_WORLD); + if (pj_strcmp(&s1, &hello_world) != 0) + return -10; + if (pj_stricmp(&s1, &hello_world) != 0) + return -20; + if (pj_strcmp(&s1, &just_hello) <= 0) + return -30; + if (pj_stricmp(&s1, &just_hello) <= 0) + return -40; + if (pj_strlen(&s1) != strlen(HELLO_WORLD)) + return -50; + if (pj_strncmp(&s1, &hello_world, 5) != 0) + return -60; + if (pj_strnicmp(&s1, &hello_world, 5) != 0) + return -70; + if (pj_strchr(&s1, HELLO_WORLD[1]) != s1.ptr+1) + return -80; + + /* + * pj_strdup() + */ + if (!pj_strdup(pool, &s2, &s1)) + return -100; + if (pj_strcmp(&s1, &s2) != 0) + return -110; + + /* + * pj_strcpy(), pj_strcat() + */ + s3.ptr = pj_pool_alloc(pool, 256); + if (!s3.ptr) + return -200; + pj_strcpy(&s3, &s2); + pj_strcat(&s3, &just_hello); + + if (pj_strcmp2(&s3, HELLO_WORLD JUST_HELLO) != 0) + return -210; + + /* + * pj_strdup2(), pj_strtrim(). + */ + pj_strdup2(pool, &s4, " " HELLO_WORLD "\t "); + pj_strtrim(&s4); + if (pj_strcmp2(&s4, HELLO_WORLD) != 0) + return -250; + + /* + * pj_utoa() + */ + s5.ptr = pj_pool_alloc(pool, 16); + if (!s5.ptr) + return -270; + s5.slen = pj_utoa(UL_VALUE, s5.ptr); + + /* + * pj_strtoul() + */ + if (pj_strtoul(&s5) != UL_VALUE) + return -280; + + /* + * pj_create_random_string() + * Check that no duplicate strings are returned. + */ + for (i=0; i -#ifdef _MSC_VER -# pragma warning(disable:4127) -#endif - -#define DO_TEST(test) do { \ - PJ_LOG(3, ("test", "Running %s...", #test)); \ - rc = test; \ - PJ_LOG(3, ("test", \ - "%s(%d)", \ - (rc ? "..ERROR" : "..success"), rc)); \ - if (rc!=0) goto on_return; \ - } while (0) - - -pj_pool_factory *mem; - -int param_echo_sock_type; -const char *param_echo_server = ECHO_SERVER_ADDRESS; -int param_echo_port = ECHO_SERVER_START_PORT; - -int test_inner(void) -{ - pj_caching_pool caching_pool; - const char *filename; - int line; - int rc = 0; - - mem = &caching_pool.factory; - - rc = pj_init(); - if (rc != 0) { - app_perror("pj_init() error!!", rc); - return rc; - } - - pj_log_set_level(3); - pj_log_set_decor(PJ_LOG_HAS_NEWLINE); - pj_dump_config(); - pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); - -#if INCLUDE_ERRNO_TEST - DO_TEST( errno_test() ); -#endif - -#if INCLUDE_TIMESTAMP_TEST - DO_TEST( timestamp_test() ); -#endif - -#if INCLUDE_EXCEPTION_TEST - DO_TEST( exception_test() ); -#endif - -#if INCLUDE_RAND_TEST - DO_TEST( rand_test() ); -#endif - -#if INCLUDE_LIST_TEST - DO_TEST( list_test() ); -#endif - -#if INCLUDE_POOL_TEST - DO_TEST( pool_test() ); -#endif - -#if INCLUDE_POOL_PERF_TEST - DO_TEST( pool_perf_test() ); -#endif - -#if INCLUDE_STRING_TEST - DO_TEST( string_test() ); -#endif - -#if INCLUDE_FIFOBUF_TEST - DO_TEST( fifobuf_test() ); -#endif - -#if INCLUDE_RBTREE_TEST - DO_TEST( rbtree_test() ); -#endif - -#if INCLUDE_ATOMIC_TEST - DO_TEST( atomic_test() ); -#endif - -#if INCLUDE_MUTEX_TEST - DO_TEST( mutex_test() ); -#endif - -#if INCLUDE_TIMER_TEST - DO_TEST( timer_test() ); -#endif - -#if INCLUDE_SLEEP_TEST - DO_TEST( sleep_test() ); -#endif - -#if INCLUDE_THREAD_TEST - DO_TEST( thread_test() ); -#endif - -#if INCLUDE_SOCK_TEST - DO_TEST( sock_test() ); -#endif - -#if INCLUDE_SOCK_PERF_TEST - DO_TEST( sock_perf_test() ); -#endif - -#if INCLUDE_SELECT_TEST - DO_TEST( select_test() ); -#endif - -#if INCLUDE_UDP_IOQUEUE_TEST - DO_TEST( udp_ioqueue_test() ); -#endif - -#if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST - DO_TEST( tcp_ioqueue_test() ); -#endif - -#if INCLUDE_IOQUEUE_PERF_TEST - DO_TEST( ioqueue_perf_test() ); -#endif - -#if INCLUDE_XML_TEST - DO_TEST( xml_test() ); -#endif - -#if INCLUDE_ECHO_SERVER - //echo_server(); - echo_srv_sync(); -#elif INCLUDE_ECHO_CLIENT - if (param_echo_sock_type == 0) - param_echo_sock_type = PJ_SOCK_DGRAM; - - echo_client( param_echo_sock_type, - param_echo_server, - param_echo_port); -#endif - - goto on_return; - -on_return: - - pj_caching_pool_destroy( &caching_pool ); - - PJ_LOG(3,("test", "")); - - pj_thread_get_stack_info(pj_thread_this(), &filename, &line); - PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u", - pj_thread_get_stack_max_usage(pj_thread_this()), - filename, line)); - if (rc == 0) - PJ_LOG(3,("test", "Looks like everything is okay!..")); - else - PJ_LOG(3,("test", "Test completed with error(s)")); - return 0; -} - -int test_main(void) -{ - PJ_USE_EXCEPTION; - - PJ_TRY { - return test_inner(); - } - PJ_DEFAULT { - int id = PJ_GET_EXCEPTION(); - PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)", - id, pj_exception_id_name(id))); - } - PJ_END; - - return -1; -} +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/test.c 4 29/10/05 21:33 Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/test.c $ + * + * 4 29/10/05 21:33 Bennylp + * Changed echo_server() to echo_srv_sync() + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/05/05 5:13p Bennylp + * Created. + * + */ +#include "test.h" +#include +#ifdef _MSC_VER +# pragma warning(disable:4127) +#endif + +#define DO_TEST(test) do { \ + PJ_LOG(3, ("test", "Running %s...", #test)); \ + rc = test; \ + PJ_LOG(3, ("test", \ + "%s(%d)", \ + (rc ? "..ERROR" : "..success"), rc)); \ + if (rc!=0) goto on_return; \ + } while (0) + + +pj_pool_factory *mem; + +int param_echo_sock_type; +const char *param_echo_server = ECHO_SERVER_ADDRESS; +int param_echo_port = ECHO_SERVER_START_PORT; + +int test_inner(void) +{ + pj_caching_pool caching_pool; + const char *filename; + int line; + int rc = 0; + + mem = &caching_pool.factory; + + rc = pj_init(); + if (rc != 0) { + app_perror("pj_init() error!!", rc); + return rc; + } + + pj_log_set_level(3); + pj_log_set_decor(PJ_LOG_HAS_NEWLINE); + pj_dump_config(); + pj_caching_pool_init( &caching_pool, &pj_pool_factory_default_policy, 0 ); + +#if INCLUDE_ERRNO_TEST + DO_TEST( errno_test() ); +#endif + +#if INCLUDE_TIMESTAMP_TEST + DO_TEST( timestamp_test() ); +#endif + +#if INCLUDE_EXCEPTION_TEST + DO_TEST( exception_test() ); +#endif + +#if INCLUDE_RAND_TEST + DO_TEST( rand_test() ); +#endif + +#if INCLUDE_LIST_TEST + DO_TEST( list_test() ); +#endif + +#if INCLUDE_POOL_TEST + DO_TEST( pool_test() ); +#endif + +#if INCLUDE_POOL_PERF_TEST + DO_TEST( pool_perf_test() ); +#endif + +#if INCLUDE_STRING_TEST + DO_TEST( string_test() ); +#endif + +#if INCLUDE_FIFOBUF_TEST + DO_TEST( fifobuf_test() ); +#endif + +#if INCLUDE_RBTREE_TEST + DO_TEST( rbtree_test() ); +#endif + +#if INCLUDE_ATOMIC_TEST + DO_TEST( atomic_test() ); +#endif + +#if INCLUDE_MUTEX_TEST + DO_TEST( mutex_test() ); +#endif + +#if INCLUDE_TIMER_TEST + DO_TEST( timer_test() ); +#endif + +#if INCLUDE_SLEEP_TEST + DO_TEST( sleep_test() ); +#endif + +#if INCLUDE_THREAD_TEST + DO_TEST( thread_test() ); +#endif + +#if INCLUDE_SOCK_TEST + DO_TEST( sock_test() ); +#endif + +#if INCLUDE_SOCK_PERF_TEST + DO_TEST( sock_perf_test() ); +#endif + +#if INCLUDE_SELECT_TEST + DO_TEST( select_test() ); +#endif + +#if INCLUDE_UDP_IOQUEUE_TEST + DO_TEST( udp_ioqueue_test() ); +#endif + +#if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST + DO_TEST( tcp_ioqueue_test() ); +#endif + +#if INCLUDE_IOQUEUE_PERF_TEST + DO_TEST( ioqueue_perf_test() ); +#endif + +#if INCLUDE_XML_TEST + DO_TEST( xml_test() ); +#endif + +#if INCLUDE_ECHO_SERVER + //echo_server(); + echo_srv_sync(); +#elif INCLUDE_ECHO_CLIENT + if (param_echo_sock_type == 0) + param_echo_sock_type = PJ_SOCK_DGRAM; + + echo_client( param_echo_sock_type, + param_echo_server, + param_echo_port); +#endif + + goto on_return; + +on_return: + + pj_caching_pool_destroy( &caching_pool ); + + PJ_LOG(3,("test", "")); + + pj_thread_get_stack_info(pj_thread_this(), &filename, &line); + PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u", + pj_thread_get_stack_max_usage(pj_thread_this()), + filename, line)); + if (rc == 0) + PJ_LOG(3,("test", "Looks like everything is okay!..")); + else + PJ_LOG(3,("test", "Test completed with error(s)")); + return 0; +} + +int test_main(void) +{ + PJ_USE_EXCEPTION; + + PJ_TRY { + return test_inner(); + } + PJ_DEFAULT { + int id = PJ_GET_EXCEPTION(); + PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)", + id, pj_exception_id_name(id))); + } + PJ_END; + + return -1; +} diff --git a/pjlib/src/pjlib-test/test.h b/pjlib/src/pjlib-test/test.h index 475cdff64..f02cdd1dc 100644 --- a/pjlib/src/pjlib-test/test.h +++ b/pjlib/src/pjlib-test/test.h @@ -1,90 +1,90 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/test.h 4 10/29/05 10:28p Bennylp $ */ -#ifndef __PJLIB_TEST_H__ -#define __PJLIB_TEST_H__ - -#include - -#define GROUP_LIBC 1 -#define GROUP_OS 1 -#define GROUP_DATA_STRUCTURE 1 -#define GROUP_NETWORK 1 -#define GROUP_EXTRA 1 - -#define INCLUDE_ERRNO_TEST GROUP_LIBC -#define INCLUDE_TIMESTAMP_TEST GROUP_OS -#define INCLUDE_EXCEPTION_TEST GROUP_LIBC -#define INCLUDE_RAND_TEST GROUP_LIBC -#define INCLUDE_LIST_TEST GROUP_DATA_STRUCTURE -#define INCLUDE_POOL_TEST GROUP_LIBC -#define INCLUDE_POOL_PERF_TEST (PJ_HAS_MALLOC && GROUP_LIBC) -#define INCLUDE_STRING_TEST GROUP_DATA_STRUCTURE -#define INCLUDE_FIFOBUF_TEST GROUP_DATA_STRUCTURE -#define INCLUDE_RBTREE_TEST GROUP_DATA_STRUCTURE -#define INCLUDE_TIMER_TEST GROUP_DATA_STRUCTURE -#define INCLUDE_ATOMIC_TEST GROUP_OS -#define INCLUDE_MUTEX_TEST GROUP_OS -#define INCLUDE_SLEEP_TEST GROUP_OS -#define INCLUDE_THREAD_TEST GROUP_OS -#define INCLUDE_SOCK_TEST GROUP_NETWORK -#define INCLUDE_SOCK_PERF_TEST GROUP_NETWORK -#define INCLUDE_SELECT_TEST GROUP_NETWORK -#define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK -#define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK -#define INCLUDE_IOQUEUE_PERF_TEST GROUP_NETWORK -#define INCLUDE_XML_TEST GROUP_EXTRA - - -#define INCLUDE_ECHO_SERVER 0 -#define INCLUDE_ECHO_CLIENT 0 - -#define ECHO_SERVER_MAX_THREADS 4 -#define ECHO_SERVER_START_PORT 65000 -#define ECHO_SERVER_ADDRESS "compaq.home" -#define ECHO_SERVER_DURATION_MSEC (60*60*1000) - -#define ECHO_CLIENT_MAX_THREADS 10 - -PJ_BEGIN_DECL - -extern int errno_test(void); -extern int timestamp_test(void); -extern int exception_test(void); -extern int rand_test(void); -extern int list_test(void); -extern int pool_test(void); -extern int pool_perf_test(void); -extern int string_test(void); -extern int fifobuf_test(void); -extern int timer_test(void); -extern int rbtree_test(void); -extern int atomic_test(void); -extern int mutex_test(void); -extern int sleep_test(void); -extern int thread_test(void); -extern int sock_test(void); -extern int sock_perf_test(void); -extern int select_test(void); -extern int udp_ioqueue_test(void); -extern int tcp_ioqueue_test(void); -extern int ioqueue_perf_test(void); -extern int xml_test(void); - -extern int echo_server(void); -extern int echo_client(int sock_type, const char *server, int port); - -extern pj_pool_factory *mem; - -extern int test_main(void); -extern void app_perror(const char *msg, pj_status_t err); -extern pj_status_t app_socket(int family, int type, int proto, int port, - pj_sock_t *ptr_sock); -extern pj_status_t app_socketpair(int family, int type, int protocol, - pj_sock_t *server, pj_sock_t *client); - -//#define TRACE_(expr) PJ_LOG(3,expr) -#define TRACE_(expr) - -PJ_END_DECL - -#endif /* __PJLIB_TEST_H__ */ - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/test.h 4 10/29/05 10:28p Bennylp $ */ +#ifndef __PJLIB_TEST_H__ +#define __PJLIB_TEST_H__ + +#include + +#define GROUP_LIBC 0 +#define GROUP_OS 0 +#define GROUP_DATA_STRUCTURE 0 +#define GROUP_NETWORK 0 +#define GROUP_EXTRA 0 + +#define INCLUDE_ERRNO_TEST GROUP_LIBC +#define INCLUDE_TIMESTAMP_TEST GROUP_OS +#define INCLUDE_EXCEPTION_TEST GROUP_LIBC +#define INCLUDE_RAND_TEST GROUP_LIBC +#define INCLUDE_LIST_TEST GROUP_DATA_STRUCTURE +#define INCLUDE_POOL_TEST GROUP_LIBC +#define INCLUDE_POOL_PERF_TEST (PJ_HAS_MALLOC && GROUP_LIBC) +#define INCLUDE_STRING_TEST GROUP_DATA_STRUCTURE +#define INCLUDE_FIFOBUF_TEST 0 // GROUP_DATA_STRUCTURE +#define INCLUDE_RBTREE_TEST GROUP_DATA_STRUCTURE +#define INCLUDE_TIMER_TEST GROUP_DATA_STRUCTURE +#define INCLUDE_ATOMIC_TEST GROUP_OS +#define INCLUDE_MUTEX_TEST GROUP_OS +#define INCLUDE_SLEEP_TEST GROUP_OS +#define INCLUDE_THREAD_TEST GROUP_OS +#define INCLUDE_SOCK_TEST GROUP_NETWORK +#define INCLUDE_SOCK_PERF_TEST GROUP_NETWORK +#define INCLUDE_SELECT_TEST GROUP_NETWORK +#define INCLUDE_UDP_IOQUEUE_TEST GROUP_NETWORK +#define INCLUDE_TCP_IOQUEUE_TEST GROUP_NETWORK +#define INCLUDE_IOQUEUE_PERF_TEST GROUP_NETWORK +#define INCLUDE_XML_TEST GROUP_EXTRA + + +#define INCLUDE_ECHO_SERVER 0 +#define INCLUDE_ECHO_CLIENT 1 + +#define ECHO_SERVER_MAX_THREADS 4 +#define ECHO_SERVER_START_PORT 65000 +#define ECHO_SERVER_ADDRESS "compaq.home" +#define ECHO_SERVER_DURATION_MSEC (60*60*1000) + +#define ECHO_CLIENT_MAX_THREADS 2 + +PJ_BEGIN_DECL + +extern int errno_test(void); +extern int timestamp_test(void); +extern int exception_test(void); +extern int rand_test(void); +extern int list_test(void); +extern int pool_test(void); +extern int pool_perf_test(void); +extern int string_test(void); +extern int fifobuf_test(void); +extern int timer_test(void); +extern int rbtree_test(void); +extern int atomic_test(void); +extern int mutex_test(void); +extern int sleep_test(void); +extern int thread_test(void); +extern int sock_test(void); +extern int sock_perf_test(void); +extern int select_test(void); +extern int udp_ioqueue_test(void); +extern int tcp_ioqueue_test(void); +extern int ioqueue_perf_test(void); +extern int xml_test(void); + +extern int echo_server(void); +extern int echo_client(int sock_type, const char *server, int port); + +extern pj_pool_factory *mem; + +extern int test_main(void); +extern void app_perror(const char *msg, pj_status_t err); +extern pj_status_t app_socket(int family, int type, int proto, int port, + pj_sock_t *ptr_sock); +extern pj_status_t app_socketpair(int family, int type, int protocol, + pj_sock_t *server, pj_sock_t *client); + +//#define TRACE_(expr) PJ_LOG(3,expr) +#define TRACE_(expr) + +PJ_END_DECL + +#endif /* __PJLIB_TEST_H__ */ + diff --git a/pjlib/src/pjlib-test/thread.c b/pjlib/src/pjlib-test/thread.c index f41ec16e0..a99253838 100644 --- a/pjlib/src/pjlib-test/thread.c +++ b/pjlib/src/pjlib-test/thread.c @@ -1,290 +1,290 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/thread.c 4 10/29/05 11:51a Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/thread.c $ - * - * 4 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 3 14/10/05 11:32 Bennylp - * More lenient with timeslice difference. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/11/05 12:39a Bennylp - * Created. - * - */ -#include "test.h" - -/** - * \page page_pjlib_thread_test Test: Thread Test - * - * This file contains \a thread_test() definition. - * - * \section thread_test_scope_sec Scope of Test - * This tests: - * - whether PJ_THREAD_SUSPENDED flag works. - * - whether multithreading works. - * - whether thread timeslicing works, and threads have equal - * time-slice proportion. - * - * APIs tested: - * - pj_thread_create() - * - pj_thread_register() - * - pj_thread_this() - * - pj_thread_get_name() - * - pj_thread_destroy() - * - pj_thread_resume() - * - pj_thread_sleep() - * - pj_thread_join() - * - pj_thread_destroy() - * - * - * This file is pjlib-test/thread.c - * - * \include pjlib-test/thread.c - */ -#if INCLUDE_THREAD_TEST - -#include - -#define THIS_FILE "thread_test" - -static int quit_flag=0; - -/* - * The thread's entry point. - * - * Each of the thread mainly will just execute the loop which - * increments a variable. - */ -static void* thread_proc(pj_uint32_t *pcounter) -{ - /* Test that pj_thread_register() works. */ - pj_thread_desc desc; - pj_thread_t *this_thread; - pj_status_t rc; - - rc = pj_thread_register("thread", desc, &this_thread); - if (rc != PJ_SUCCESS) { - app_perror("...error in pj_thread_register", rc); - return NULL; - } - - /* Test that pj_thread_this() works */ - this_thread = pj_thread_this(); - if (this_thread == NULL) { - PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!")); - return NULL; - } - - /* Test that pj_thread_get_name() works */ - if (pj_thread_get_name(this_thread) == NULL) { - PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!")); - return NULL; - } - - /* Main loop */ - for (;!quit_flag;) { - (*pcounter)++; - //Must sleep if platform doesn't do time-slicing. - pj_thread_sleep(0); - } - - return NULL; -} - -/* - * simple_thread() - */ -static int simple_thread(const char *title, unsigned flags) -{ - pj_pool_t *pool; - pj_thread_t *thread; - pj_status_t rc; - pj_uint32_t counter = 0; - - PJ_LOG(3,(THIS_FILE, "..%s", title)); - - pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); - if (!pool) - return -1000; - - quit_flag = 0; - - rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc, - &counter, - PJ_THREAD_DEFAULT_STACK_SIZE, - flags, - &thread); - - if (rc != PJ_SUCCESS) { - app_perror("...error: unable to create thread", rc); - return -1010; - } - - if (flags & PJ_THREAD_SUSPENDED) { - rc = pj_thread_resume(thread); - if (rc != PJ_SUCCESS) { - app_perror("...error: resume thread error", rc); - return -1020; - } - } - - PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit..")); - - quit_flag = 1; - pj_thread_join(thread); - - pj_pool_release(pool); - - PJ_LOG(3,(THIS_FILE, "...%s success", title)); - return PJ_SUCCESS; -} - - -/* - * timeslice_test() - */ -static int timeslice_test(void) -{ - enum { NUM_THREADS = 4 }; - pj_pool_t *pool; - pj_uint32_t counter[NUM_THREADS], lowest, highest, diff; - pj_thread_t *thread[NUM_THREADS]; - int i; - pj_status_t rc; - - quit_flag = 0; - - pool = pj_pool_create(mem, NULL, 4096, 0, NULL); - if (!pool) - return -10; - - PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS)); - - /* Create all threads in suspended mode. */ - for (i=0; i highest) - highest = counter[i]; - } - - /* Check that all threads are running. */ - if (lowest < 2) { - PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!")); - return -70; - } - - /* The difference between lowest and higest should be lower than 50%. - */ - diff = (highest-lowest)*100 / ((highest+lowest)/2); - if ( diff >= 50) { - PJ_LOG(3,(THIS_FILE, "...ERROR: thread didn't have equal timeslice!")); - PJ_LOG(3,(THIS_FILE, ".....lowest counter=%u, highest counter=%u, diff=%u%%", - lowest, highest, diff)); - return -80; - } else { - PJ_LOG(3,(THIS_FILE, - "...info: timeslice diff between lowest & highest=%u%%", - diff)); - } - - return 0; -} - -int thread_test(void) -{ - int rc; - - rc = simple_thread("simple thread test", 0); - if (rc != PJ_SUCCESS) - return rc; - - rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED); - if (rc != PJ_SUCCESS) - return rc; - - rc = timeslice_test(); - if (rc != PJ_SUCCESS) - return rc; - - return rc; -} - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_thread_test; -#endif /* INCLUDE_THREAD_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/thread.c 4 10/29/05 11:51a Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/thread.c $ + * + * 4 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 3 14/10/05 11:32 Bennylp + * More lenient with timeslice difference. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/11/05 12:39a Bennylp + * Created. + * + */ +#include "test.h" + +/** + * \page page_pjlib_thread_test Test: Thread Test + * + * This file contains \a thread_test() definition. + * + * \section thread_test_scope_sec Scope of Test + * This tests: + * - whether PJ_THREAD_SUSPENDED flag works. + * - whether multithreading works. + * - whether thread timeslicing works, and threads have equal + * time-slice proportion. + * + * APIs tested: + * - pj_thread_create() + * - pj_thread_register() + * - pj_thread_this() + * - pj_thread_get_name() + * - pj_thread_destroy() + * - pj_thread_resume() + * - pj_thread_sleep() + * - pj_thread_join() + * - pj_thread_destroy() + * + * + * This file is pjlib-test/thread.c + * + * \include pjlib-test/thread.c + */ +#if INCLUDE_THREAD_TEST + +#include + +#define THIS_FILE "thread_test" + +static int quit_flag=0; + +/* + * The thread's entry point. + * + * Each of the thread mainly will just execute the loop which + * increments a variable. + */ +static void* thread_proc(pj_uint32_t *pcounter) +{ + /* Test that pj_thread_register() works. */ + pj_thread_desc desc; + pj_thread_t *this_thread; + pj_status_t rc; + + rc = pj_thread_register("thread", desc, &this_thread); + if (rc != PJ_SUCCESS) { + app_perror("...error in pj_thread_register", rc); + return NULL; + } + + /* Test that pj_thread_this() works */ + this_thread = pj_thread_this(); + if (this_thread == NULL) { + PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!")); + return NULL; + } + + /* Test that pj_thread_get_name() works */ + if (pj_thread_get_name(this_thread) == NULL) { + PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!")); + return NULL; + } + + /* Main loop */ + for (;!quit_flag;) { + (*pcounter)++; + //Must sleep if platform doesn't do time-slicing. + pj_thread_sleep(0); + } + + return NULL; +} + +/* + * simple_thread() + */ +static int simple_thread(const char *title, unsigned flags) +{ + pj_pool_t *pool; + pj_thread_t *thread; + pj_status_t rc; + pj_uint32_t counter = 0; + + PJ_LOG(3,(THIS_FILE, "..%s", title)); + + pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); + if (!pool) + return -1000; + + quit_flag = 0; + + rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc, + &counter, + PJ_THREAD_DEFAULT_STACK_SIZE, + flags, + &thread); + + if (rc != PJ_SUCCESS) { + app_perror("...error: unable to create thread", rc); + return -1010; + } + + if (flags & PJ_THREAD_SUSPENDED) { + rc = pj_thread_resume(thread); + if (rc != PJ_SUCCESS) { + app_perror("...error: resume thread error", rc); + return -1020; + } + } + + PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit..")); + + quit_flag = 1; + pj_thread_join(thread); + + pj_pool_release(pool); + + PJ_LOG(3,(THIS_FILE, "...%s success", title)); + return PJ_SUCCESS; +} + + +/* + * timeslice_test() + */ +static int timeslice_test(void) +{ + enum { NUM_THREADS = 4 }; + pj_pool_t *pool; + pj_uint32_t counter[NUM_THREADS], lowest, highest, diff; + pj_thread_t *thread[NUM_THREADS]; + int i; + pj_status_t rc; + + quit_flag = 0; + + pool = pj_pool_create(mem, NULL, 4096, 0, NULL); + if (!pool) + return -10; + + PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS)); + + /* Create all threads in suspended mode. */ + for (i=0; i highest) + highest = counter[i]; + } + + /* Check that all threads are running. */ + if (lowest < 2) { + PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!")); + return -70; + } + + /* The difference between lowest and higest should be lower than 50%. + */ + diff = (highest-lowest)*100 / ((highest+lowest)/2); + if ( diff >= 50) { + PJ_LOG(3,(THIS_FILE, "...ERROR: thread didn't have equal timeslice!")); + PJ_LOG(3,(THIS_FILE, ".....lowest counter=%u, highest counter=%u, diff=%u%%", + lowest, highest, diff)); + return -80; + } else { + PJ_LOG(3,(THIS_FILE, + "...info: timeslice diff between lowest & highest=%u%%", + diff)); + } + + return 0; +} + +int thread_test(void) +{ + int rc; + + rc = simple_thread("simple thread test", 0); + if (rc != PJ_SUCCESS) + return rc; + + rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED); + if (rc != PJ_SUCCESS) + return rc; + + rc = timeslice_test(); + if (rc != PJ_SUCCESS) + return rc; + + return rc; +} + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_thread_test; +#endif /* INCLUDE_THREAD_TEST */ + + diff --git a/pjlib/src/pjlib-test/timer.c b/pjlib/src/pjlib-test/timer.c index 1aaa208d5..f06b5a012 100644 --- a/pjlib/src/pjlib-test/timer.c +++ b/pjlib/src/pjlib-test/timer.c @@ -1,169 +1,169 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/timer.c 3 10/29/05 10:23p Bennylp $ */ -#include "test.h" - -/** - * \page page_pjlib_timer_test Test: Timer - * - * This file provides implementation of \b timer_test(). It tests the - * functionality of the timer heap. - * - * - * This file is pjlib-test/timer.c - * - * \include pjlib-test/timer.c - */ - - -#if INCLUDE_TIMER_TEST - -#include - -#define LOOP 16 -#define MIN_COUNT 250 -#define MAX_COUNT (LOOP * MIN_COUNT) -#define MIN_DELAY 2 -#define D (MAX_COUNT / 32000) -#define DELAY (D < MIN_DELAY ? MIN_DELAY : D) -#define THIS_FILE "timer_test" - - -static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e) -{ - PJ_UNUSED_ARG(ht); - PJ_UNUSED_ARG(e); -} - -static int test_timer_heap(void) -{ - int i, j; - pj_timer_entry *entry; - pj_pool_t *pool; - pj_timer_heap_t *timer; - pj_time_val delay; - pj_status_t rc; int err=0; - unsigned size, count; - - size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry); - pool = pj_pool_create( mem, NULL, size, 4000, NULL); - if (!pool) { - PJ_LOG(3,("test", "...error: unable to create pool of %u bytes", - size)); - return -10; - } - - entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry)); - if (!entry) - return -20; - - for (i=0; i 0) { - t_poll.u32.lo += (t2.u32.lo - t1.u32.lo); - early += rc; - } - } - - // Set the time where all timers should finish - pj_gettimeofday(&expire); - delay.sec = DELAY; - delay.msec = 0; - PJ_TIME_VAL_ADD(expire, delay); - - // Wait unfil all timers finish, cancel some of them. - do { - int index = pj_rand() % count; - pj_get_timestamp(&t1); - rc = pj_timer_heap_cancel(timer, &entry[index]); - pj_get_timestamp(&t2); - if (rc > 0) { - cancelled += rc; - t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo); - } - - pj_gettimeofday(&now); - - pj_get_timestamp(&t1); - rc = pj_timer_heap_poll(timer, NULL); - pj_get_timestamp(&t2); - if (rc > 0) { - done += rc; - t_poll.u32.lo += (t2.u32.lo - t1.u32.lo); - } - - } while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0); - - if (pj_timer_heap_count(timer)) { - PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left", - pj_timer_heap_count(timer))); - ++err; - } - t_sched.u32.lo /= count; - t_cancel.u32.lo /= count; - t_poll.u32.lo /= count; - PJ_LOG(4, (THIS_FILE, - "...ok (count:%d, early:%d, cancelled:%d, " - "sched:%d, cancel:%d poll:%d)", - count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo, - t_poll.u32.lo)); - - count = count * 2; - if (count > MAX_COUNT) - break; - } - - pj_pool_release(pool); - return err; -} - - -int timer_test() -{ - return test_timer_heap(); -} - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_timer_test; -#endif /* INCLUDE_TIMER_TEST */ - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/timer.c 3 10/29/05 10:23p Bennylp $ */ +#include "test.h" + +/** + * \page page_pjlib_timer_test Test: Timer + * + * This file provides implementation of \b timer_test(). It tests the + * functionality of the timer heap. + * + * + * This file is pjlib-test/timer.c + * + * \include pjlib-test/timer.c + */ + + +#if INCLUDE_TIMER_TEST + +#include + +#define LOOP 16 +#define MIN_COUNT 250 +#define MAX_COUNT (LOOP * MIN_COUNT) +#define MIN_DELAY 2 +#define D (MAX_COUNT / 32000) +#define DELAY (D < MIN_DELAY ? MIN_DELAY : D) +#define THIS_FILE "timer_test" + + +static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e) +{ + PJ_UNUSED_ARG(ht); + PJ_UNUSED_ARG(e); +} + +static int test_timer_heap(void) +{ + int i, j; + pj_timer_entry *entry; + pj_pool_t *pool; + pj_timer_heap_t *timer; + pj_time_val delay; + pj_status_t rc; int err=0; + unsigned size, count; + + size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry); + pool = pj_pool_create( mem, NULL, size, 4000, NULL); + if (!pool) { + PJ_LOG(3,("test", "...error: unable to create pool of %u bytes", + size)); + return -10; + } + + entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry)); + if (!entry) + return -20; + + for (i=0; i 0) { + t_poll.u32.lo += (t2.u32.lo - t1.u32.lo); + early += rc; + } + } + + // Set the time where all timers should finish + pj_gettimeofday(&expire); + delay.sec = DELAY; + delay.msec = 0; + PJ_TIME_VAL_ADD(expire, delay); + + // Wait unfil all timers finish, cancel some of them. + do { + int index = pj_rand() % count; + pj_get_timestamp(&t1); + rc = pj_timer_heap_cancel(timer, &entry[index]); + pj_get_timestamp(&t2); + if (rc > 0) { + cancelled += rc; + t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo); + } + + pj_gettimeofday(&now); + + pj_get_timestamp(&t1); + rc = pj_timer_heap_poll(timer, NULL); + pj_get_timestamp(&t2); + if (rc > 0) { + done += rc; + t_poll.u32.lo += (t2.u32.lo - t1.u32.lo); + } + + } while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0); + + if (pj_timer_heap_count(timer)) { + PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left", + pj_timer_heap_count(timer))); + ++err; + } + t_sched.u32.lo /= count; + t_cancel.u32.lo /= count; + t_poll.u32.lo /= count; + PJ_LOG(4, (THIS_FILE, + "...ok (count:%d, early:%d, cancelled:%d, " + "sched:%d, cancel:%d poll:%d)", + count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo, + t_poll.u32.lo)); + + count = count * 2; + if (count > MAX_COUNT) + break; + } + + pj_pool_release(pool); + return err; +} + + +int timer_test() +{ + return test_timer_heap(); +} + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_timer_test; +#endif /* INCLUDE_TIMER_TEST */ + + diff --git a/pjlib/src/pjlib-test/timestamp.c b/pjlib/src/pjlib-test/timestamp.c index 3d4d9f8e9..4dac0db17 100644 --- a/pjlib/src/pjlib-test/timestamp.c +++ b/pjlib/src/pjlib-test/timestamp.c @@ -1,140 +1,143 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/timestamp.c 4 10/29/05 11:51a Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/timestamp.c $ - * - * 4 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 3 14/10/05 11:32 Bennylp - * Longer test, to check if timestamp is running backwards. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/09/05 9:39p Bennylp - * Created. - * - */ -#include "test.h" -#include -#include - - -/** - * \page page_pjlib_timestamp_test Test: Timestamp - * - * This file provides implementation of timestamp_test() - * - * \section timestamp_test_sec Scope of the Test - * - * This tests whether timestamp API works. - * - * API tested: - * - pj_get_timestamp_freq() - * - pj_get_timestamp() - * - pj_elapsed_usec() - * - PJ_LOG() - * - * - * This file is pjlib-test/timestamp.c - * - * \include pjlib-test/timestamp.c - */ - -#if INCLUDE_TIMESTAMP_TEST - -#define THIS_FILE "timestamp" - -int timestamp_test(void) -{ - enum { CONSECUTIVE_LOOP = 1000 }; - volatile unsigned i; - pj_timestamp freq, t1, t2; - unsigned elapsed; - pj_status_t rc; - - PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)")); - - /* Get and display timestamp frequency. */ - if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) { - app_perror("...ERROR: get timestamp freq", rc); - return -1000; - } - - PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu", - freq.u32.hi, freq.u32.lo)); - - PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait)..")); - - /* - * Check if consecutive readings should yield timestamp value - * that is bigger than previous value. - * First we get the first timestamp. - */ - rc = pj_get_timestamp(&t1); - if (rc != PJ_SUCCESS) { - app_perror("...ERROR: get timestamp", rc); - return -1001; - } - for (i=0; i= t1. */ - if (t2.u32.hi < t1.u32.hi || - (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo)) - { - PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp runs backwards!")); - return -1003; - } - } - - /* - * Simple test to time some loop. - */ - PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop")); - - - /* Mark start time. */ - if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) { - app_perror("....error: cat't get timestamp", rc); - return -1010; - } - - /* Loop.. */ - for (i=0; i<1000000; ++i) - ; - - /* Mark end time. */ - pj_get_timestamp(&t2); - - /* Get elapsed time in usec. */ - elapsed = pj_elapsed_usec(&t1, &t2); - PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed)); - - /* See if elapsed time is reasonable. */ - if (elapsed < 1 || elapsed > 100000) { - PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u)", - elapsed)); - return -1030; - } - return 0; -} - - -#else -/* To prevent warning about "translation unit is empty" - * when this test is disabled. - */ -int dummy_timestamp_test; -#endif /* INCLUDE_TIMESTAMP_TEST */ - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/timestamp.c 4 10/29/05 11:51a Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/timestamp.c $ + * + * 4 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 3 14/10/05 11:32 Bennylp + * Longer test, to check if timestamp is running backwards. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/09/05 9:39p Bennylp + * Created. + * + */ +#include "test.h" +#include +#include + + +/** + * \page page_pjlib_timestamp_test Test: Timestamp + * + * This file provides implementation of timestamp_test() + * + * \section timestamp_test_sec Scope of the Test + * + * This tests whether timestamp API works. + * + * API tested: + * - pj_get_timestamp_freq() + * - pj_get_timestamp() + * - pj_elapsed_usec() + * - PJ_LOG() + * + * + * This file is pjlib-test/timestamp.c + * + * \include pjlib-test/timestamp.c + */ + +#if INCLUDE_TIMESTAMP_TEST + +#define THIS_FILE "timestamp" + +int timestamp_test(void) +{ + enum { CONSECUTIVE_LOOP = 1000 }; + volatile unsigned i; + pj_timestamp freq, t1, t2; + unsigned elapsed; + pj_status_t rc; + + PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)")); + + /* Get and display timestamp frequency. */ + if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) { + app_perror("...ERROR: get timestamp freq", rc); + return -1000; + } + + PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu", + freq.u32.hi, freq.u32.lo)); + + PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait)..")); + + /* + * Check if consecutive readings should yield timestamp value + * that is bigger than previous value. + * First we get the first timestamp. + */ + rc = pj_get_timestamp(&t1); + if (rc != PJ_SUCCESS) { + app_perror("...ERROR: get timestamp", rc); + return -1001; + } + for (i=0; i= t1. */ + if (t2.u32.hi < t1.u32.hi || + (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo)) + { + PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp runs backwards!")); + return -1003; + } + } + + /* + * Simple test to time some loop. + */ + PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop")); + + + /* Mark start time. */ + if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) { + app_perror("....error: cat't get timestamp", rc); + return -1010; + } + + /* Loop.. */ + for (i=0; i<1000000; ++i) + ; + + /* Mark end time. */ + pj_get_timestamp(&t2); + + /* Get elapsed time in usec. */ + elapsed = pj_elapsed_usec(&t1, &t2); + PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed)); + + /* See if elapsed time is reasonable. */ + if (elapsed < 1 || elapsed > 100000) { + PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u, " + "t1.u32.hi=%u, t1.u32.lo=%u, " + "t2.u32.hi=%u, t2.u32.lo=%u)", + elapsed, + t1.u32.hi, t1.u32.lo, t2.u32.hi, t2.u32.lo)); + return -1030; + } + return 0; +} + + +#else +/* To prevent warning about "translation unit is empty" + * when this test is disabled. + */ +int dummy_timestamp_test; +#endif /* INCLUDE_TIMESTAMP_TEST */ + diff --git a/pjlib/src/pjlib-test/udp_echo_srv_sync.c b/pjlib/src/pjlib-test/udp_echo_srv_sync.c index b513498b5..b95eb61e5 100644 --- a/pjlib/src/pjlib-test/udp_echo_srv_sync.c +++ b/pjlib/src/pjlib-test/udp_echo_srv_sync.c @@ -1,168 +1,168 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/udp_echo_srv_sync.c 2 29/10/05 21:34 Bennylp $ */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/udp_echo_srv_sync.c $ - * - * 2 29/10/05 21:34 Bennylp - * Tested on Win32 - * - * 1 10/29/05 9:56a Bennylp - * Created. - * - */ -#include "test.h" -#include - -static pj_sem_t *sem; -static pj_mutex_t *mutex; -static pj_size_t total_bw; - -static int worker_thread(void *arg) -{ - pj_sock_t sock = (pj_sock_t)arg; - char buf[1516]; - pj_size_t received; - pj_time_val last_print; - pj_status_t last_recv_err = PJ_SUCCESS, last_write_err = PJ_SUCCESS; - - received = 0; - pj_gettimeofday(&last_print); - - for (;;) { - pj_ssize_t len; - pj_uint32_t delay_msec; - pj_time_val now; - pj_highprec_t bw; - pj_status_t rc; - pj_sockaddr_in addr; - int addrlen; - - len = sizeof(buf); - addrlen = sizeof(addr); - rc = pj_sock_recvfrom(sock, buf, &len, 0, &addr, &addrlen); - if (rc != 0) { - if (rc != last_recv_err) { - app_perror("...recv error", rc); - last_recv_err = rc; - } - continue; - } - - received += len; - - rc = pj_sock_sendto(sock, buf, &len, 0, &addr, addrlen); - if (rc != PJ_SUCCESS) { - if (rc != last_write_err) { - app_perror("...send error", rc); - last_write_err = rc; - } - continue; - } - - pj_gettimeofday(&now); - PJ_TIME_VAL_SUB(now, last_print); - delay_msec = PJ_TIME_VAL_MSEC(now); - - if (delay_msec < 1000) - continue; - - bw = received; - pj_highprec_mul(bw, 1000); - pj_highprec_div(bw, delay_msec); - - pj_mutex_lock(mutex); - total_bw = total_bw + (pj_size_t)bw; - pj_mutex_unlock(mutex); - - pj_gettimeofday(&last_print); - received = 0; - pj_sem_post(sem); - pj_thread_sleep(0); - } -} - - -int echo_srv_sync(void) -{ - pj_pool_t *pool; - pj_sock_t sock; - pj_thread_t *thread[ECHO_SERVER_MAX_THREADS]; - pj_status_t rc; - pj_highprec_t abs_total; - unsigned count; - int i; - - pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); - if (!pool) - return -5; - - rc = pj_sem_create(pool, NULL, 0, ECHO_SERVER_MAX_THREADS, &sem); - if (rc != PJ_SUCCESS) { - app_perror("...unable to create semaphore", rc); - return -6; - } - - rc = pj_mutex_create_simple(pool, NULL, &mutex); - if (rc != PJ_SUCCESS) { - app_perror("...unable to create mutex", rc); - return -7; - } - - rc = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, ECHO_SERVER_START_PORT, &sock); - if (rc != PJ_SUCCESS) { - app_perror("...socket error", rc); - return -10; - } - - for (i=0; i" : ""))); - - total_bw = 0; - - if (count==20) { - count = 0; - abs_total = 0; - } - - while (pj_sem_trywait(sem) == PJ_SUCCESS) - ; - } -} - - +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/udp_echo_srv_sync.c 2 29/10/05 21:34 Bennylp $ */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/udp_echo_srv_sync.c $ + * + * 2 29/10/05 21:34 Bennylp + * Tested on Win32 + * + * 1 10/29/05 9:56a Bennylp + * Created. + * + */ +#include "test.h" +#include + +static pj_sem_t *sem; +static pj_mutex_t *mutex; +static pj_size_t total_bw; + +static int worker_thread(void *arg) +{ + pj_sock_t sock = (pj_sock_t)arg; + char buf[1516]; + pj_size_t received; + pj_time_val last_print; + pj_status_t last_recv_err = PJ_SUCCESS, last_write_err = PJ_SUCCESS; + + received = 0; + pj_gettimeofday(&last_print); + + for (;;) { + pj_ssize_t len; + pj_uint32_t delay_msec; + pj_time_val now; + pj_highprec_t bw; + pj_status_t rc; + pj_sockaddr_in addr; + int addrlen; + + len = sizeof(buf); + addrlen = sizeof(addr); + rc = pj_sock_recvfrom(sock, buf, &len, 0, &addr, &addrlen); + if (rc != 0) { + if (rc != last_recv_err) { + app_perror("...recv error", rc); + last_recv_err = rc; + } + continue; + } + + received += len; + + rc = pj_sock_sendto(sock, buf, &len, 0, &addr, addrlen); + if (rc != PJ_SUCCESS) { + if (rc != last_write_err) { + app_perror("...send error", rc); + last_write_err = rc; + } + continue; + } + + pj_gettimeofday(&now); + PJ_TIME_VAL_SUB(now, last_print); + delay_msec = PJ_TIME_VAL_MSEC(now); + + if (delay_msec < 1000) + continue; + + bw = received; + pj_highprec_mul(bw, 1000); + pj_highprec_div(bw, delay_msec); + + pj_mutex_lock(mutex); + total_bw = total_bw + (pj_size_t)bw; + pj_mutex_unlock(mutex); + + pj_gettimeofday(&last_print); + received = 0; + pj_sem_post(sem); + pj_thread_sleep(0); + } +} + + +int echo_srv_sync(void) +{ + pj_pool_t *pool; + pj_sock_t sock; + pj_thread_t *thread[ECHO_SERVER_MAX_THREADS]; + pj_status_t rc; + pj_highprec_t abs_total; + unsigned count; + int i; + + pool = pj_pool_create(mem, NULL, 4000, 4000, NULL); + if (!pool) + return -5; + + rc = pj_sem_create(pool, NULL, 0, ECHO_SERVER_MAX_THREADS, &sem); + if (rc != PJ_SUCCESS) { + app_perror("...unable to create semaphore", rc); + return -6; + } + + rc = pj_mutex_create_simple(pool, NULL, &mutex); + if (rc != PJ_SUCCESS) { + app_perror("...unable to create mutex", rc); + return -7; + } + + rc = app_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, ECHO_SERVER_START_PORT, &sock); + if (rc != PJ_SUCCESS) { + app_perror("...socket error", rc); + return -10; + } + + for (i=0; i" : ""))); + + total_bw = 0; + + if (count==20) { + count = 0; + abs_total = 0; + } + + while (pj_sem_trywait(sem) == PJ_SUCCESS) + ; + } +} + + diff --git a/pjlib/src/pjlib-test/util.c b/pjlib/src/pjlib-test/util.c index c698cff49..991d8ddbe 100644 --- a/pjlib/src/pjlib-test/util.c +++ b/pjlib/src/pjlib-test/util.c @@ -1,129 +1,129 @@ -/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/util.c 3 10/29/05 11:51a Bennylp $ - */ -/* - * $Log: /pjproject-0.3/pjlib/src/pjlib-test/util.c $ - * - * 3 10/29/05 11:51a Bennylp - * Version 0.3-pre2. - * - * 2 10/14/05 12:26a Bennylp - * Finished error code framework, some fixes in ioqueue, etc. Pretty - * major. - * - * 1 10/12/05 10:00a Bennylp - * Created. - * - */ -#include "test.h" -#include - -void app_perror(const char *msg, pj_status_t rc) -{ - char errbuf[256]; - - PJ_CHECK_STACK(); - - pj_strerror(rc, errbuf, sizeof(errbuf)); - PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf)); -} - -#define SERVER 0 -#define CLIENT 1 - -pj_status_t app_socket(int family, int type, int proto, int port, - pj_sock_t *ptr_sock) -{ - pj_sockaddr_in addr; - pj_sock_t sock; - pj_status_t rc; - - rc = pj_sock_socket(family, type, proto, &sock); - if (rc != PJ_SUCCESS) - return rc; - - pj_memset(&addr, 0, sizeof(addr)); - addr.sin_family = (pj_uint16_t)family; - addr.sin_port = (short)(port!=-1 ? pj_htons((pj_uint16_t)port) : 0); - rc = pj_sock_bind(sock, &addr, sizeof(addr)); - if (rc != PJ_SUCCESS) - return rc; - - if (type == PJ_SOCK_STREAM) { - rc = pj_sock_listen(sock, 5); - if (rc != PJ_SUCCESS) - return rc; - } - - *ptr_sock = sock; - return PJ_SUCCESS; -} - -pj_status_t app_socketpair(int family, int type, int protocol, - pj_sock_t *serverfd, pj_sock_t *clientfd) -{ - int i; - static unsigned short port = 11000; - pj_sockaddr_in addr; - pj_str_t s; - pj_status_t rc = 0; - pj_sock_t sock[2]; - - /* Create both sockets. */ - for (i=0; i<2; ++i) { - rc = pj_sock_socket(family, type, protocol, &sock[i]); - if (rc != PJ_SUCCESS) { - if (i==1) - pj_sock_close(sock[0]); - return rc; - } - } - - /* Retry bind */ - pj_memset(&addr, 0, sizeof(addr)); - addr.sin_family = PJ_AF_INET; - for (i=0; i<5; ++i) { - addr.sin_port = pj_htons(port++); - rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr)); - if (rc == PJ_SUCCESS) - break; - } - - if (rc != PJ_SUCCESS) - goto on_error; - - /* For TCP, listen the socket. */ - if (type == PJ_SOCK_STREAM) { - rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN); - if (rc != PJ_SUCCESS) - goto on_error; - } - - /* Connect client socket. */ - addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); - rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr)); - if (rc != PJ_SUCCESS) - goto on_error; - - /* For TCP, must accept(), and get the new socket. */ - if (type == PJ_SOCK_STREAM) { - pj_sock_t newserver; - - rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL); - if (rc != PJ_SUCCESS) - goto on_error; - - /* Replace server socket with new socket. */ - pj_sock_close(sock[SERVER]); - sock[SERVER] = newserver; - } - - *serverfd = sock[SERVER]; - *clientfd = sock[CLIENT]; - - return rc; - -on_error: - for (i=0; i<2; ++i) - pj_sock_close(sock[i]); - return rc; -} +/* $Header: /pjproject-0.3/pjlib/src/pjlib-test/util.c 3 10/29/05 11:51a Bennylp $ + */ +/* + * $Log: /pjproject-0.3/pjlib/src/pjlib-test/util.c $ + * + * 3 10/29/05 11:51a Bennylp + * Version 0.3-pre2. + * + * 2 10/14/05 12:26a Bennylp + * Finished error code framework, some fixes in ioqueue, etc. Pretty + * major. + * + * 1 10/12/05 10:00a Bennylp + * Created. + * + */ +#include "test.h" +#include + +void app_perror(const char *msg, pj_status_t rc) +{ + char errbuf[256]; + + PJ_CHECK_STACK(); + + pj_strerror(rc, errbuf, sizeof(errbuf)); + PJ_LOG(1,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf)); +} + +#define SERVER 0 +#define CLIENT 1 + +pj_status_t app_socket(int family, int type, int proto, int port, + pj_sock_t *ptr_sock) +{ + pj_sockaddr_in addr; + pj_sock_t sock; + pj_status_t rc; + + rc = pj_sock_socket(family, type, proto, &sock); + if (rc != PJ_SUCCESS) + return rc; + + pj_memset(&addr, 0, sizeof(addr)); + addr.sin_family = (pj_uint16_t)family; + addr.sin_port = (short)(port!=-1 ? pj_htons((pj_uint16_t)port) : 0); + rc = pj_sock_bind(sock, &addr, sizeof(addr)); + if (rc != PJ_SUCCESS) + return rc; + + if (type == PJ_SOCK_STREAM) { + rc = pj_sock_listen(sock, 5); + if (rc != PJ_SUCCESS) + return rc; + } + + *ptr_sock = sock; + return PJ_SUCCESS; +} + +pj_status_t app_socketpair(int family, int type, int protocol, + pj_sock_t *serverfd, pj_sock_t *clientfd) +{ + int i; + static unsigned short port = 11000; + pj_sockaddr_in addr; + pj_str_t s; + pj_status_t rc = 0; + pj_sock_t sock[2]; + + /* Create both sockets. */ + for (i=0; i<2; ++i) { + rc = pj_sock_socket(family, type, protocol, &sock[i]); + if (rc != PJ_SUCCESS) { + if (i==1) + pj_sock_close(sock[0]); + return rc; + } + } + + /* Retry bind */ + pj_memset(&addr, 0, sizeof(addr)); + addr.sin_family = PJ_AF_INET; + for (i=0; i<5; ++i) { + addr.sin_port = pj_htons(port++); + rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr)); + if (rc == PJ_SUCCESS) + break; + } + + if (rc != PJ_SUCCESS) + goto on_error; + + /* For TCP, listen the socket. */ + if (type == PJ_SOCK_STREAM) { + rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN); + if (rc != PJ_SUCCESS) + goto on_error; + } + + /* Connect client socket. */ + addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1")); + rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr)); + if (rc != PJ_SUCCESS) + goto on_error; + + /* For TCP, must accept(), and get the new socket. */ + if (type == PJ_SOCK_STREAM) { + pj_sock_t newserver; + + rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL); + if (rc != PJ_SUCCESS) + goto on_error; + + /* Replace server socket with new socket. */ + pj_sock_close(sock[SERVER]); + sock[SERVER] = newserver; + } + + *serverfd = sock[SERVER]; + *clientfd = sock[CLIENT]; + + return rc; + +on_error: + for (i=0; i<2; ++i) + pj_sock_close(sock[i]); + return rc; +} diff --git a/pjlib/src/pjlib-test/xml.c b/pjlib/src/pjlib-test/xml.c index 9a7c0a1ea..da9544897 100644 --- a/pjlib/src/pjlib-test/xml.c +++ b/pjlib/src/pjlib-test/xml.c @@ -1,127 +1,127 @@ -#include "test.h" - - -#if INCLUDE_XML_TEST - -#include -#include - -#define THIS_FILE "xml_test" - -static const char *xml_doc[] = -{ -" \n" -" \n" -"\n" -" \n" -" \n" -" open\n" -" assistant\n" -" \n" -" \n" -" true\n" -" false\n" -" true\n" -" \n" -" tel:09012345678\n" -" \n" -"\n" -" \n" -" \n" -" open\n" -" \n" -" im:pep@example.com\n" -" \n" -"\n" -" \n" -" \n" -" closed\n" -" meeting\n" -" \n" -" http://example.com/~pep/\n" -" http://example.com/~pep/icon.gif\n" -" http://example.com/~pep/card.vcd\n" -" sip:pep@example.com\n" -" \n" -"\n" -" Full state presence document\n" -"\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"\n" -" \n" -} -; - -static int xml_parse_print_test(const char *doc) -{ - pj_str_t msg; - pj_pool_t *pool; - pj_xml_node *root; - char *output; - int output_len; - - pool = pj_pool_create(mem, "xml", 4096, 1024, NULL); - pj_strdup2(pool, &msg, doc); - root = pj_xml_parse(pool, msg.ptr, msg.slen); - if (!root) { - PJ_LOG(1, (THIS_FILE, " Error: unable to parse XML")); - return -10; - } - - output = (char*)pj_pool_alloc(pool, msg.slen + 512); - pj_memset(output, 0, msg.slen+512); - output_len = pj_xml_print(root, output, msg.slen+512, PJ_TRUE); - if (output_len < 1) { - PJ_LOG(1, (THIS_FILE, " Error: buffer too small to print XML file")); - return -20; - } - output[output_len] = '\0'; - - - pj_pool_release(pool); - return 0; -} - -int xml_test() -{ - unsigned i; - for (i=0; i +#include + +#define THIS_FILE "xml_test" + +static const char *xml_doc[] = +{ +" \n" +" \n" +"\n" +" \n" +" \n" +" open\n" +" assistant\n" +" \n" +" \n" +" true\n" +" false\n" +" true\n" +" \n" +" tel:09012345678\n" +" \n" +"\n" +" \n" +" \n" +" open\n" +" \n" +" im:pep@example.com\n" +" \n" +"\n" +" \n" +" \n" +" closed\n" +" meeting\n" +" \n" +" http://example.com/~pep/\n" +" http://example.com/~pep/icon.gif\n" +" http://example.com/~pep/card.vcd\n" +" sip:pep@example.com\n" +" \n" +"\n" +" Full state presence document\n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +" \n" +"\n" +" \n" +} +; + +static int xml_parse_print_test(const char *doc) +{ + pj_str_t msg; + pj_pool_t *pool; + pj_xml_node *root; + char *output; + int output_len; + + pool = pj_pool_create(mem, "xml", 4096, 1024, NULL); + pj_strdup2(pool, &msg, doc); + root = pj_xml_parse(pool, msg.ptr, msg.slen); + if (!root) { + PJ_LOG(1, (THIS_FILE, " Error: unable to parse XML")); + return -10; + } + + output = (char*)pj_pool_alloc(pool, msg.slen + 512); + pj_memset(output, 0, msg.slen+512); + output_len = pj_xml_print(root, output, msg.slen+512, PJ_TRUE); + if (output_len < 1) { + PJ_LOG(1, (THIS_FILE, " Error: buffer too small to print XML file")); + return -20; + } + output[output_len] = '\0'; + + + pj_pool_release(pool); + return 0; +} + +int xml_test() +{ + unsigned i; + for (i=0; i> .$(app).depend; \ - if gcc -MM $(DEPFLAGS) $$F >> .$(app).depend; then \ - true; \ - else \ - echo 'err:' >> .$(app).depend; \ - exit 1; \ - fi; \ - done - -dep: depend - --include .$(app).depend - +LIBDIR = ../lib +BINDIR = ../bin + +# +# The full path of output lib file (e.g. ../lib/libapp.a). +# +LIB = $($(APP)_LIB) + +# +# The full path of output executable file (e.g. ../bin/app.exe). +# +EXE = $($(APP)_EXE) + +# +# Source directory +# +SRCDIR = $($(APP)_SRCDIR) + +# +# SRCEXT is .c +# SRCS is file.c +# FULL_SRCS is ../src/app/file.c +# +SRCEXT = $($(APP)_SRCEXT) +SRCS = $($(APP)_SRCS) +FULL_SRCS = $(foreach file, $(SRCS), $(SRCDIR)/$(file)) + + +# +# Output directory for object files (i.e. output/target) +# +OBJDIR = ./output/$(app)-$(TARGET) + +# +# OBJS1 is ./output/target/file.c +# OBJS is ./output/target/file.o +# +OBJS1 = $(foreach file, $(SRCS), $(OBJDIR)/$(file)) +OBJS = $(OBJS1:%$(SRCEXT)=%$(OBJEXT)) +OBJDIRS := $(sort $(foreach file, $(SRCS), $(dir $(OBJDIR)/$(file)))) + + +# +# When generating dependency (gcc -MM), ideally we use only either +# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work. +# +DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS) + +print_common: + @echo "###" + @echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):" + @echo "###" + @echo APP=$(APP) + @echo SRCEXT=$(SRCEXT) + @echo OBJDIR=$(OBJDIR) + @echo OBJS=$(OBJS) + @echo SRCDIR=$(SRCDIR) + @echo FULL_SRCS=$(FULL_SRCS) + @echo $(APP)_CFLAGS=$($(APP)_CFLAGS) + @echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS) + @echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS) + @echo DEPFLAGS=$(DEPFLAGS) + +print_bin: print_common + @echo EXE=$(EXE) + @echo BINDIR=$(BINDIR) + +print_lib: print_common + @echo LIB=$(LIB) + @echo LIBDIR=$(LIBDIR) + +$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) + $(AR) $(LIB) $(OBJS) + $(RANLIB) $(LIB) + +$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) + $(LD) $(LDOUT) $(EXE) $(OBJS) $($(APP)_LDFLAGS) + +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c + $(CC) $($(APP)_CFLAGS) $< $(CCOUT) $@ + +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp + $(CC) $($(APP)_CXXFLAGS) $< $(CCOUT) $@ + +#$(OBJDIR): +# $(MKDIR) $(OBJDIR) +$(OBJDIRS): + $(MKDIR) $@ + +$(LIBDIR): + $(MKDIR) $(LIBDIR) + +$(BINDIR): + $(MKDIR) $(BINDIR) + +clean: + $(RM) -r $(OBJDIR)/* + $(RMDIR) $(OBJDIR) + +realclean: clean + $(RM) $(LIB) $(EXE) + $(RM) .$(app).depend + +depend: + $(RM) .$(app).depend + for F in $(FULL_SRCS); do \ + echo -n $(OBJDIR)/ >> .$(app).depend; \ + if gcc -MM $(DEPFLAGS) $$F >> .$(app).depend; then \ + true; \ + else \ + echo 'err:' >> .$(app).depend; \ + exit 1; \ + fi; \ + done + +dep: depend + +-include .$(app).depend + diff --git a/pjmedia/src/pjmedia.h b/pjmedia/src/pjmedia.h index 50d4a6add..3f43670a1 100644 --- a/pjmedia/src/pjmedia.h +++ b/pjmedia/src/pjmedia.h @@ -1,15 +1,15 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia.h 2 5/15/05 11:51a Bennylp $ */ -#ifndef __PJMEDIA_H__ -#define __PJMEDIA_H__ - -#include -#include -#include -#include -#include -#include -#include -#include - -#endif /* __PJMEDIA_H__ */ - +/* $Header: /pjproject/pjmedia/src/pjmedia.h 2 5/15/05 11:51a Bennylp $ */ +#ifndef __PJMEDIA_H__ +#define __PJMEDIA_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __PJMEDIA_H__ */ + diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c index 13409d3e3..c814562aa 100644 --- a/pjmedia/src/pjmedia/codec.c +++ b/pjmedia/src/pjmedia/codec.c @@ -1,89 +1,89 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/codec.c 3 4/17/05 11:59a Bennylp $ */ -#include -#include -#include -#include - -#define THIS_FILE "codec.c" - -static void enum_all_codecs (pj_codec_mgr *cm) -{ - pj_codec_factory *cf; - - cf = cm->factory_list.next; - cm->codec_cnt = 0; - while (cf != &cm->factory_list) { - pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS]; - int i, cnt; - - cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp); - if (cnt > PJ_CODEC_MGR_MAX_CODECS) { - pj_assert(0); - PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory")); - cnt = PJ_CODEC_MGR_MAX_CODECS; - } - - for (i=0; icodec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) { - cm->codecs[cm->codec_cnt++] = temp[i]; - } - - cf = cf->next; - } -} - -PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr) -{ - pj_list_init (&mgr->factory_list); - mgr->codec_cnt = 0; - return 0; -} - -PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr, - pj_codec_factory *factory) -{ - pj_list_insert_before (&mgr->factory_list, factory); - enum_all_codecs (mgr); - return 0; -} - -PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory) -{ - PJ_UNUSED_ARG(mgr) - pj_list_erase(factory); - enum_all_codecs (mgr); -} - -PJ_DEF(unsigned) -pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]) -{ - unsigned i; - - if (count > mgr->codec_cnt) - count = mgr->codec_cnt; - - for (i=0; icodecs[i]; - - return mgr->codec_cnt; -} - -PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id) -{ - pj_codec_factory *factory = mgr->factory_list.next; - while (factory != &mgr->factory_list) { - if ( (*factory->op->match_id)(factory, id) == 0 ) { - pj_codec *codec = (*factory->op->alloc_codec)(factory, id); - if (codec != NULL) - return codec; - } - factory = factory->next; - } - return NULL; -} - -PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec) -{ - PJ_UNUSED_ARG(mgr) - (*codec->factory->op->dealloc_codec)(codec->factory, codec); -} - +/* $Header: /pjproject/pjmedia/src/pjmedia/codec.c 3 4/17/05 11:59a Bennylp $ */ +#include +#include +#include +#include + +#define THIS_FILE "codec.c" + +static void enum_all_codecs (pj_codec_mgr *cm) +{ + pj_codec_factory *cf; + + cf = cm->factory_list.next; + cm->codec_cnt = 0; + while (cf != &cm->factory_list) { + pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS]; + int i, cnt; + + cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp); + if (cnt > PJ_CODEC_MGR_MAX_CODECS) { + pj_assert(0); + PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory")); + cnt = PJ_CODEC_MGR_MAX_CODECS; + } + + for (i=0; icodec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) { + cm->codecs[cm->codec_cnt++] = temp[i]; + } + + cf = cf->next; + } +} + +PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr) +{ + pj_list_init (&mgr->factory_list); + mgr->codec_cnt = 0; + return 0; +} + +PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr, + pj_codec_factory *factory) +{ + pj_list_insert_before (&mgr->factory_list, factory); + enum_all_codecs (mgr); + return 0; +} + +PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory) +{ + PJ_UNUSED_ARG(mgr) + pj_list_erase(factory); + enum_all_codecs (mgr); +} + +PJ_DEF(unsigned) +pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]) +{ + unsigned i; + + if (count > mgr->codec_cnt) + count = mgr->codec_cnt; + + for (i=0; icodecs[i]; + + return mgr->codec_cnt; +} + +PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id) +{ + pj_codec_factory *factory = mgr->factory_list.next; + while (factory != &mgr->factory_list) { + if ( (*factory->op->match_id)(factory, id) == 0 ) { + pj_codec *codec = (*factory->op->alloc_codec)(factory, id); + if (codec != NULL) + return codec; + } + factory = factory->next; + } + return NULL; +} + +PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec) +{ + PJ_UNUSED_ARG(mgr) + (*codec->factory->op->dealloc_codec)(codec->factory, codec); +} + diff --git a/pjmedia/src/pjmedia/codec.h b/pjmedia/src/pjmedia/codec.h index 131c1a4d2..80ae9fea4 100644 --- a/pjmedia/src/pjmedia/codec.h +++ b/pjmedia/src/pjmedia/codec.h @@ -1,337 +1,337 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/codec.h 7 8/24/05 10:29a Bennylp $ */ - -#ifndef __PJMEDIA_CODEC_H__ -#define __PJMEDIA_CODEC_H__ - - -/** - * @file codec.h - * @brief Codec framework. - */ - -#include - -PJ_BEGIN_DECL - - -/** - * @defgroup PJMED_CODEC Codec framework. - * @ingroup PJMEDIA - * @{ - */ - -/** Top most media type. */ -typedef enum pj_media_type -{ - /** No type. */ - PJ_MEDIA_TYPE_NONE = 0, - - /** The media is audio */ - PJ_MEDIA_TYPE_AUDIO = 1, - - /** The media is video. */ - PJ_MEDIA_TYPE_VIDEO = 2, - - /** Unknown media type, in this case the name will be specified in - * encoding_name. - */ - PJ_MEDIA_TYPE_UNKNOWN = 3, - -} pj_media_type; - - -/** Media direction. */ -typedef enum pj_media_dir_t -{ - /** None */ - PJ_MEDIA_DIR_NONE = 0, - - /** Encoding (outgoing to network) stream */ - PJ_MEDIA_DIR_ENCODING = 1, - - /** Decoding (incoming from network) stream. */ - PJ_MEDIA_DIR_DECODING = 2, - - /** Incoming and outgoing stream. */ - PJ_MEDIA_DIR_ENCODING_DECODING = 3, - -} pj_media_dir_t; - - -/** Standard RTP paylist types. */ -typedef enum pj_rtp_pt -{ - PJ_RTP_PT_PCMU = 0, /* audio PCMU */ - PJ_RTP_PT_GSM = 3, /* audio GSM */ - PJ_RTP_PT_G723 = 4, /* audio G723 */ - PJ_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */ - PJ_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */ - PJ_RTP_PT_LPC = 7, /* audio LPC */ - PJ_RTP_PT_PCMA = 8, /* audio PCMA */ - PJ_RTP_PT_G722 = 9, /* audio G722 */ - PJ_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */ - PJ_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */ - PJ_RTP_PT_QCELP = 12, /* audio QCELP */ - PJ_RTP_PT_CN = 13, /* audio Comfort Noise */ - PJ_RTP_PT_MPA = 14, /* audio MPEG1 or MPEG2 as elementary streams */ - PJ_RTP_PT_G728 = 15, /* audio G728 */ - PJ_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */ - PJ_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */ - PJ_RTP_PT_G729 = 18, /* audio G729 */ - PJ_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun Microsystems (RFC 2029) */ - PJ_RTP_PT_JPEG = 26, /* video JPEG */ - PJ_RTP_PT_NV = 28, /* video NV implemented by nv program by Xerox */ - PJ_RTP_PT_H261 = 31, /* video H261 */ - PJ_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary streams */ - PJ_RTP_PT_MP2T = 33, /* video MPEG2 transport */ - PJ_RTP_PT_H263 = 34, /* video H263 */ - - PJ_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */ -} pj_rtp_pt; - - -/** Identification used to search for codec factory that supports specific - * codec specification. - */ -typedef struct pj_codec_id -{ - /** Media type. */ - pj_media_type type; - - /** Payload type (can be dynamic). */ - unsigned pt; - - /** Encoding name, must be present if the payload type is dynamic. */ - pj_str_t encoding_name; - - /** Sampling rate. */ - unsigned sample_rate; -} pj_codec_id; - - -/** Detailed codec attributes used both to configure a codec and to query - * the capability of codec factories. - */ -typedef struct pj_codec_attr -{ - pj_uint32_t sample_rate; /* Sampling rate in Hz */ - pj_uint32_t avg_bps; /* Average bandwidth in bits per second */ - - pj_uint8_t pcm_bits_per_sample;/* Bits per sample in the PCM side */ - pj_uint16_t ptime; /* Packet time in miliseconds */ - - unsigned pt:8; /* Payload type. */ - unsigned vad_enabled:1; /* Voice Activity Detector. */ - unsigned cng_enabled:1; /* Comfort Noise Generator. */ - unsigned lpf_enabled:1; /* Low pass filter */ - unsigned hpf_enabled:1; /* High pass filter */ - unsigned penh_enabled:1; /* Perceptual Enhancement */ - unsigned concl_enabled:1; /* Packet loss concealment */ - unsigned reserved_bit:1; - -} pj_codec_attr; - -/** Types of audio frame. */ -typedef enum pj_audio_frame_type -{ - /** The frame is a silence audio frame. */ - PJ_AUDIO_FRAME_SILENCE, - - /** The frame is a non-silence audio frame. */ - PJ_AUDIO_FRAME_AUDIO, - -} pj_audio_frame_type; - -typedef struct pj_codec pj_codec; -typedef struct pj_codec_factory pj_codec_factory; - - -/** This structure describes an audio frame. */ -struct pj_audio_frame -{ - /** Type: silence or non-silence. */ - pj_audio_frame_type type; - - /** Pointer to buffer. */ - void *buf; - - /** Frame size in bytes. */ - unsigned size; -}; - -/** - * Operations that must be supported by the codec. - */ -typedef struct pj_codec_op -{ - /** Get default attributes. */ - pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr); - - /** Open and initialize codec using the specified attribute. - * @return zero on success. - */ - pj_status_t (*init)( pj_codec *codec, pj_pool_t *pool ); - - /** Close and shutdown codec. - */ - pj_status_t (*open)( pj_codec *codec, pj_codec_attr *attr ); - - /** Close and shutdown codec. - */ - pj_status_t (*close)( pj_codec *codec ); - - /** Encode frame. - */ - pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); - - /** Decode frame. - */ - pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); - -} pj_codec_op; - -/** - * A codec describes an instance to encode or decode media frames. - */ -struct pj_codec -{ - /** Entries to put this codec instance in codec factory's list. */ - PJ_DECL_LIST_MEMBER(struct pj_codec) - - /** Codec's private data. */ - void *codec_data; - - /** Codec factory where this codec was allocated. */ - pj_codec_factory *factory; - - /** Operations to codec. */ - pj_codec_op *op; -}; - -/** - * This structure describes operations that must be supported by codec factories. - */ -typedef struct pj_codec_factory_op -{ - /** Check whether the factory can create codec with the specified ID. - * @param factory The codec factory. - * @param id The codec ID. - * @return zero it matches. - */ - pj_status_t (*match_id)( pj_codec_factory *factory, const pj_codec_id *id ); - - /** Create default attributes for the specified codec ID. This function can - * be called by application to get the capability of the codec. - * @param factory The codec factory. - * @param id The codec ID. - * @param attr The attribute to be initialized. - * @return zero if success. - */ - pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id, - pj_codec_attr *attr ); - - /** Enumerate supported codecs. - * @param factory The codec factory. - * @param count Number of entries in the array. - * @param codecs The codec array. - * @return the total number of supported codecs, which can be less or - * greater than requested. - */ - unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]); - - /** This function is called by codec manager to instantiate one codec - * instance. - * @param factory The codec factory. - * @param id The codec ID. - * @return the instance of the codec, or NULL if codec can not be created. - */ - pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id); - - /** This function is called by codec manager to return a particular instance - * of codec back to the codec factory. - * @param factory The codec factory. - * @param codec The codec instance to be returned. - */ - void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec ); - -} pj_codec_factory_op; - -/** - * Codec factory describes a module that is able to create codec with specific - * capabilities. These capabilities can be queried by codec manager to create - * instances of codec. - */ -struct pj_codec_factory -{ - /** Entries to put this structure in the codec manager list. */ - PJ_DECL_LIST_MEMBER(struct pj_codec_factory) - - /** The factory's private data. */ - void *factory_data; - - /** Operations to the factory. */ - pj_codec_factory_op *op; - -}; - -/** - * Declare maximum codecs - */ -#define PJ_CODEC_MGR_MAX_CODECS 32 - -/** - * Codec manager maintains codec factory etc. - */ -typedef struct pj_codec_mgr -{ - pj_codec_factory factory_list; - unsigned codec_cnt; - pj_codec_id codecs[PJ_CODEC_MGR_MAX_CODECS]; -} pj_codec_mgr; - -/** - * Init codec manager. - */ -PJ_DECL(pj_status_t) -pj_codec_mgr_init (pj_codec_mgr *mgr); - -/** - * Register codec to codec manager. - */ -PJ_DECL(pj_status_t) -pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory); - -/** - * Unregister codec. - */ -PJ_DECL(void) -pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory); - -/** - * Enumerate codecs. - */ -PJ_DECL(unsigned) -pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]); - -/** - * Open codec. - */ -PJ_DECL(pj_codec*) -pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id); - -/** - * Close codec. - */ -PJ_DECL(void) -pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec); - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJMEDIA_CODEC_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/codec.h 7 8/24/05 10:29a Bennylp $ */ + +#ifndef __PJMEDIA_CODEC_H__ +#define __PJMEDIA_CODEC_H__ + + +/** + * @file codec.h + * @brief Codec framework. + */ + +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_CODEC Codec framework. + * @ingroup PJMEDIA + * @{ + */ + +/** Top most media type. */ +typedef enum pj_media_type +{ + /** No type. */ + PJ_MEDIA_TYPE_NONE = 0, + + /** The media is audio */ + PJ_MEDIA_TYPE_AUDIO = 1, + + /** The media is video. */ + PJ_MEDIA_TYPE_VIDEO = 2, + + /** Unknown media type, in this case the name will be specified in + * encoding_name. + */ + PJ_MEDIA_TYPE_UNKNOWN = 3, + +} pj_media_type; + + +/** Media direction. */ +typedef enum pj_media_dir_t +{ + /** None */ + PJ_MEDIA_DIR_NONE = 0, + + /** Encoding (outgoing to network) stream */ + PJ_MEDIA_DIR_ENCODING = 1, + + /** Decoding (incoming from network) stream. */ + PJ_MEDIA_DIR_DECODING = 2, + + /** Incoming and outgoing stream. */ + PJ_MEDIA_DIR_ENCODING_DECODING = 3, + +} pj_media_dir_t; + + +/** Standard RTP paylist types. */ +typedef enum pj_rtp_pt +{ + PJ_RTP_PT_PCMU = 0, /* audio PCMU */ + PJ_RTP_PT_GSM = 3, /* audio GSM */ + PJ_RTP_PT_G723 = 4, /* audio G723 */ + PJ_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */ + PJ_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */ + PJ_RTP_PT_LPC = 7, /* audio LPC */ + PJ_RTP_PT_PCMA = 8, /* audio PCMA */ + PJ_RTP_PT_G722 = 9, /* audio G722 */ + PJ_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */ + PJ_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */ + PJ_RTP_PT_QCELP = 12, /* audio QCELP */ + PJ_RTP_PT_CN = 13, /* audio Comfort Noise */ + PJ_RTP_PT_MPA = 14, /* audio MPEG1 or MPEG2 as elementary streams */ + PJ_RTP_PT_G728 = 15, /* audio G728 */ + PJ_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */ + PJ_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */ + PJ_RTP_PT_G729 = 18, /* audio G729 */ + PJ_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun Microsystems (RFC 2029) */ + PJ_RTP_PT_JPEG = 26, /* video JPEG */ + PJ_RTP_PT_NV = 28, /* video NV implemented by nv program by Xerox */ + PJ_RTP_PT_H261 = 31, /* video H261 */ + PJ_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary streams */ + PJ_RTP_PT_MP2T = 33, /* video MPEG2 transport */ + PJ_RTP_PT_H263 = 34, /* video H263 */ + + PJ_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */ +} pj_rtp_pt; + + +/** Identification used to search for codec factory that supports specific + * codec specification. + */ +typedef struct pj_codec_id +{ + /** Media type. */ + pj_media_type type; + + /** Payload type (can be dynamic). */ + unsigned pt; + + /** Encoding name, must be present if the payload type is dynamic. */ + pj_str_t encoding_name; + + /** Sampling rate. */ + unsigned sample_rate; +} pj_codec_id; + + +/** Detailed codec attributes used both to configure a codec and to query + * the capability of codec factories. + */ +typedef struct pj_codec_attr +{ + pj_uint32_t sample_rate; /* Sampling rate in Hz */ + pj_uint32_t avg_bps; /* Average bandwidth in bits per second */ + + pj_uint8_t pcm_bits_per_sample;/* Bits per sample in the PCM side */ + pj_uint16_t ptime; /* Packet time in miliseconds */ + + unsigned pt:8; /* Payload type. */ + unsigned vad_enabled:1; /* Voice Activity Detector. */ + unsigned cng_enabled:1; /* Comfort Noise Generator. */ + unsigned lpf_enabled:1; /* Low pass filter */ + unsigned hpf_enabled:1; /* High pass filter */ + unsigned penh_enabled:1; /* Perceptual Enhancement */ + unsigned concl_enabled:1; /* Packet loss concealment */ + unsigned reserved_bit:1; + +} pj_codec_attr; + +/** Types of audio frame. */ +typedef enum pj_audio_frame_type +{ + /** The frame is a silence audio frame. */ + PJ_AUDIO_FRAME_SILENCE, + + /** The frame is a non-silence audio frame. */ + PJ_AUDIO_FRAME_AUDIO, + +} pj_audio_frame_type; + +typedef struct pj_codec pj_codec; +typedef struct pj_codec_factory pj_codec_factory; + + +/** This structure describes an audio frame. */ +struct pj_audio_frame +{ + /** Type: silence or non-silence. */ + pj_audio_frame_type type; + + /** Pointer to buffer. */ + void *buf; + + /** Frame size in bytes. */ + unsigned size; +}; + +/** + * Operations that must be supported by the codec. + */ +typedef struct pj_codec_op +{ + /** Get default attributes. */ + pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr); + + /** Open and initialize codec using the specified attribute. + * @return zero on success. + */ + pj_status_t (*init)( pj_codec *codec, pj_pool_t *pool ); + + /** Close and shutdown codec. + */ + pj_status_t (*open)( pj_codec *codec, pj_codec_attr *attr ); + + /** Close and shutdown codec. + */ + pj_status_t (*close)( pj_codec *codec ); + + /** Encode frame. + */ + pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input, + unsigned output_buf_len, struct pj_audio_frame *output); + + /** Decode frame. + */ + pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input, + unsigned output_buf_len, struct pj_audio_frame *output); + +} pj_codec_op; + +/** + * A codec describes an instance to encode or decode media frames. + */ +struct pj_codec +{ + /** Entries to put this codec instance in codec factory's list. */ + PJ_DECL_LIST_MEMBER(struct pj_codec) + + /** Codec's private data. */ + void *codec_data; + + /** Codec factory where this codec was allocated. */ + pj_codec_factory *factory; + + /** Operations to codec. */ + pj_codec_op *op; +}; + +/** + * This structure describes operations that must be supported by codec factories. + */ +typedef struct pj_codec_factory_op +{ + /** Check whether the factory can create codec with the specified ID. + * @param factory The codec factory. + * @param id The codec ID. + * @return zero it matches. + */ + pj_status_t (*match_id)( pj_codec_factory *factory, const pj_codec_id *id ); + + /** Create default attributes for the specified codec ID. This function can + * be called by application to get the capability of the codec. + * @param factory The codec factory. + * @param id The codec ID. + * @param attr The attribute to be initialized. + * @return zero if success. + */ + pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id, + pj_codec_attr *attr ); + + /** Enumerate supported codecs. + * @param factory The codec factory. + * @param count Number of entries in the array. + * @param codecs The codec array. + * @return the total number of supported codecs, which can be less or + * greater than requested. + */ + unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]); + + /** This function is called by codec manager to instantiate one codec + * instance. + * @param factory The codec factory. + * @param id The codec ID. + * @return the instance of the codec, or NULL if codec can not be created. + */ + pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id); + + /** This function is called by codec manager to return a particular instance + * of codec back to the codec factory. + * @param factory The codec factory. + * @param codec The codec instance to be returned. + */ + void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec ); + +} pj_codec_factory_op; + +/** + * Codec factory describes a module that is able to create codec with specific + * capabilities. These capabilities can be queried by codec manager to create + * instances of codec. + */ +struct pj_codec_factory +{ + /** Entries to put this structure in the codec manager list. */ + PJ_DECL_LIST_MEMBER(struct pj_codec_factory) + + /** The factory's private data. */ + void *factory_data; + + /** Operations to the factory. */ + pj_codec_factory_op *op; + +}; + +/** + * Declare maximum codecs + */ +#define PJ_CODEC_MGR_MAX_CODECS 32 + +/** + * Codec manager maintains codec factory etc. + */ +typedef struct pj_codec_mgr +{ + pj_codec_factory factory_list; + unsigned codec_cnt; + pj_codec_id codecs[PJ_CODEC_MGR_MAX_CODECS]; +} pj_codec_mgr; + +/** + * Init codec manager. + */ +PJ_DECL(pj_status_t) +pj_codec_mgr_init (pj_codec_mgr *mgr); + +/** + * Register codec to codec manager. + */ +PJ_DECL(pj_status_t) +pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory); + +/** + * Unregister codec. + */ +PJ_DECL(void) +pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory); + +/** + * Enumerate codecs. + */ +PJ_DECL(unsigned) +pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]); + +/** + * Open codec. + */ +PJ_DECL(pj_codec*) +pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id); + +/** + * Close codec. + */ +PJ_DECL(void) +pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_CODEC_H__ */ diff --git a/pjmedia/src/pjmedia/config.h b/pjmedia/src/pjmedia/config.h index b0dd7ea91..0956e9580 100644 --- a/pjmedia/src/pjmedia/config.h +++ b/pjmedia/src/pjmedia/config.h @@ -1,11 +1,11 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/config.h 3 2/24/05 10:40a Bennylp $ */ - -#ifndef __PJMED_CONFIG_H__ -#define __PJMED_CONFIG_H__ - -/** - * @defgroup PJMEDIA Media Stack - */ - - -#endif /* __PJMED_CONFIG_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/config.h 3 2/24/05 10:40a Bennylp $ */ + +#ifndef __PJMED_CONFIG_H__ +#define __PJMED_CONFIG_H__ + +/** + * @defgroup PJMEDIA Media Stack + */ + + +#endif /* __PJMED_CONFIG_H__ */ diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c index 042447178..650fdab77 100644 --- a/pjmedia/src/pjmedia/dsound.c +++ b/pjmedia/src/pjmedia/dsound.c @@ -1,861 +1,861 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/dsound.c 6 6/14/05 12:54a Bennylp $ */ - -#ifdef _MSC_VER -//# pragma warning(disable: 4201) // non-standard extension: nameless struct/union -# pragma warning(push, 3) -#endif -#include -#include -#include - -#include -#include -#include -#include - -#define THIS_FILE "dsound.c" - -/* - * Constants - */ -#define PACKET_BUFFER_COUNT 4 - -typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device; - - -/* - * DirectSound Factory Operations - */ -static pj_status_t dsound_init(void); -static const char *dsound_get_name(void); -static pj_status_t dsound_destroy(void); -static pj_status_t dsound_enum_devices(int *count, char *dev_names[]); -static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev); -static pj_status_t dsound_destroy_dev(pj_snd_dev *dev); - - -/* - * DirectSound Device Operations - */ -static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ); -static pj_status_t dsound_dev_close( pj_snd_dev *dev ); -static pj_status_t dsound_dev_play( pj_snd_dev *dev ); -static pj_status_t dsound_dev_record( pj_snd_dev *dev ); - -/* - * Utils. - */ -static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev ); - - -static pj_snd_dev_factory dsound_factory = -{ - &dsound_init, - &dsound_get_name, - &dsound_destroy, - &dsound_enum_devices, - &dsound_create_dev, - &dsound_destroy_dev -}; - -static struct pj_snd_dev_op dsound_dev_op = -{ - &dsound_dev_open, - &dsound_dev_close, - &dsound_dev_play, - &dsound_dev_record -}; - -#define DSOUND_TYPE_PLAYER 1 -#define DSOUND_TYPE_RECORDER 2 - -typedef struct Direct_Sound_Descriptor -{ - int type; - - LPDIRECTSOUND lpDsPlay; - LPDIRECTSOUNDBUFFER lpDsPlayBuffer; - - LPDIRECTSOUNDCAPTURE lpDsCapture; - LPDIRECTSOUNDCAPTUREBUFFER lpDsCaptureBuffer; - - LPDIRECTSOUNDNOTIFY lpDsNotify; - HANDLE hEvent; - HANDLE hThread; - HANDLE hStartEvent; - DWORD dwThreadQuitFlag; - pj_thread_desc thread_desc; - pj_thread_t *thread; -} Direct_Sound_Descriptor; - -struct PJ_Direct_Sound_Device -{ - Direct_Sound_Descriptor playDesc; - Direct_Sound_Descriptor recDesc; -}; - -struct Thread_Param -{ - pj_snd_dev *dev; - Direct_Sound_Descriptor *desc; -}; - -PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory() -{ - return &dsound_factory; -} - -/* - * Init DirectSound. - */ -static pj_status_t dsound_init(void) -{ - /* Nothing to do. */ - return 0; -} - -/* - * Get the name of the factory. - */ -static const char *dsound_get_name(void) -{ - return "DirectSound"; -} - -/* - * Destroy DirectSound. - */ -static pj_status_t dsound_destroy(void) -{ - /* TODO: clean up devices in case application haven't done it. */ - return 0; -} - -/* - * Enum devices in the system. - */ -static pj_status_t dsound_enum_devices(int *count, char *dev_names[]) -{ - dev_names[0] = "DirectSound Default Device"; - *count = 1; - return 0; -} - -/* - * Create DirectSound device. - */ -static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev) -{ - PJ_Direct_Sound_Device *dsDev; - - /* TODO: create based on the name. */ - PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME); - - /* Create DirectSound structure. */ - dsDev = malloc(sizeof(*dsDev)); - if (!dsDev) { - PJ_LOG(1,(THIS_FILE, "No memory to allocate device!")); - return -1; - } - memset(dsDev, 0, sizeof(*dsDev)); - - /* Associate DirectSound device with device. */ - dev->device = dsDev; - dev->op = &dsound_dev_op; - - return 0; -} - -/* - * Destroy DirectSound device. - */ -static pj_status_t dsound_destroy_dev( pj_snd_dev *dev ) -{ - if (dev->device) { - free(dev->device); - dev->device = NULL; - } - return 0; -} - -static void dsound_release_descriptor(Direct_Sound_Descriptor *desc) -{ - if (desc->lpDsNotify) - IDirectSoundNotify_Release( desc->lpDsNotify ); - - if (desc->hEvent) - CloseHandle(desc->hEvent); - - if (desc->lpDsPlayBuffer) - IDirectSoundBuffer_Release( desc->lpDsPlayBuffer ); - - if (desc->lpDsPlay) - IDirectSound_Release( desc->lpDsPlay ); - - if (desc->lpDsCaptureBuffer) - IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer); - - if (desc->lpDsCapture) - IDirectSoundCapture_Release(desc->lpDsCapture); -} - -/* - * Destroy DirectSound resources. - */ -static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev ) -{ - dsound_release_descriptor( &dsDev->playDesc ); - dsound_release_descriptor( &dsDev->recDesc ); - memset(dsDev, 0, sizeof(*dsDev)); - return 0; -} - -static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev) -{ - memset(pcmwf, 0, sizeof(PCMWAVEFORMAT)); - pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM; - pcmwf->wf.nChannels = 1; - pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec; - pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame; - pcmwf->wf.nAvgBytesPerSec = - dev->param.samples_per_sec * dev->param.bytes_per_frame; - pcmwf->wBitsPerSample = dev->param.bits_per_sample; -} - -/* - * Initialize DirectSound player device. - */ -static pj_status_t dsound_init_player (pj_snd_dev *dev) -{ - HRESULT hr; - HWND hwnd; - PCMWAVEFORMAT pcmwf; - DSBUFFERDESC dsbdesc; - DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT]; - unsigned i; - PJ_Direct_Sound_Device *dsDev = dev->device; - - /* - * Check parameters. - */ - if (dev->play_cb == NULL) { - assert(0); - return -1; - } - if (dev->device == NULL) { - assert(0); - return -1; - } - - PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device")); - - /* - * Create DirectSound device. - */ - hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL); - if (FAILED(hr)) - goto on_error; - - hwnd = GetForegroundWindow(); - if (hwnd == NULL) { - hwnd = GetDesktopWindow(); - } - hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd, - DSSCL_PRIORITY); - if FAILED(hr) - goto on_error; - - /* - * Create DirectSound play buffer. - */ - // Set up wave format structure. - init_waveformatex (&pcmwf, dev); - - // Set up DSBUFFERDESC structure. - memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); - dsbdesc.dwSize = sizeof(DSBUFFERDESC); - dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | - DSBCAPS_GETCURRENTPOSITION2; - - dsbdesc.dwBufferBytes = - (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * - dev->param.frames_per_packet); - dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; - - // Create buffer. - hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc, - &dsDev->playDesc.lpDsPlayBuffer, NULL); - if (FAILED(hr) ) - goto on_error; - - /* - * Create event for play notification. - */ - dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); - if (dsDev->playDesc.hEvent == NULL) - goto on_error; - - /* - * Setup notification for play. - */ - hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer, - &IID_IDirectSoundNotify, - (LPVOID *)&dsDev->playDesc.lpDsNotify); - if (FAILED(hr)) - goto on_error; - - - for (i=0; iparam.bytes_per_frame * - dev->param.frames_per_packet; - dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent; - } - - hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify, - PACKET_BUFFER_COUNT, - dsPosNotify); - if (FAILED(hr)) - goto on_error; - - /* Done setting up play device. */ - PJ_LOG(4,(THIS_FILE, "DirectSound player device created")); - - return 0; - -on_error: - PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr)); - dsound_destroy_dsound_dev(dsDev); - return -1; -} - -/* - * Initialize DirectSound recorder device - */ -static pj_status_t dsound_init_recorder (pj_snd_dev *dev) -{ - HRESULT hr; - PCMWAVEFORMAT pcmwf; - DSCBUFFERDESC dscbdesc; - DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT]; - unsigned i; - PJ_Direct_Sound_Device *dsDev = dev->device; - - /* - * Check parameters. - */ - if (dev->rec_cb == NULL) { - assert(0); - return -1; - } - if (dev->device == NULL) { - assert(0); - return -1; - } - - PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device")); - - /* - * Creating recorder device. - */ - hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL); - if (FAILED(hr)) - goto on_error; - - /* Init wave format */ - init_waveformatex (&pcmwf, dev); - - /* - * Setup capture buffer using sound buffer structure that was passed - * to play buffer creation earlier. - */ - memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC)); - dscbdesc.dwSize = sizeof(DSCBUFFERDESC); - dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ; - dscbdesc.dwBufferBytes = - (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * - dev->param.frames_per_packet); - dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; - - hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture, - &dscbdesc, - &dsDev->recDesc.lpDsCaptureBuffer, - NULL); - if (FAILED(hr)) - goto on_error; - - /* - * Create event for play notification. - */ - dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); - if (dsDev->recDesc.hEvent == NULL) - goto on_error; - - /* - * Setup notifications for recording. - */ - hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer, - &IID_IDirectSoundNotify, - (LPVOID *)&dsDev->recDesc.lpDsNotify); - if (FAILED(hr)) - goto on_error; - - - for (i=0; iparam.bytes_per_frame * - dev->param.frames_per_packet; - dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent; - } - - hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify, - PACKET_BUFFER_COUNT, - dsPosNotify); - if (FAILED(hr)) - goto on_error; - - /* Done setting up recorder device. */ - PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created")); - - return 0; - -on_error: - PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr)); - dsound_destroy_dsound_dev(dsDev); - return -1; -} - -/* - * Initialize DirectSound device. - */ -static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ) -{ - PJ_Direct_Sound_Device *dsDev = dev->device; - pj_status_t status; - - dsDev->playDesc.type = DSOUND_TYPE_PLAYER; - dsDev->recDesc.type = DSOUND_TYPE_RECORDER; - - if (role & PJ_SOUND_PLAYER) { - status = dsound_init_player (dev); - if (status != 0) - return status; - } - - if (role & PJ_SOUND_RECORDER) { - status = dsound_init_recorder (dev); - if (status != 0) - return status; - } - - return 0; -} - -/* - * Close DirectSound device. - */ -static pj_status_t dsound_dev_close( pj_snd_dev *dev ) -{ - PJ_LOG(4,(THIS_FILE, "Closing DirectSound device")); - - if (dev->device) { - PJ_Direct_Sound_Device *dsDev = dev->device; - - if (dsDev->playDesc.hThread) { - PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player")); - dsDev->playDesc.dwThreadQuitFlag = 1; - SetEvent(dsDev->playDesc.hEvent); - if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) { - PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit")); - TerminateThread(dsDev->playDesc.hThread, -1); - IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer ); - } - - pj_thread_destroy (dsDev->playDesc.thread); - } - - if (dsDev->recDesc.hThread) { - PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder")); - dsDev->recDesc.dwThreadQuitFlag = 1; - SetEvent(dsDev->recDesc.hEvent); - if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) { - PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit")); - TerminateThread(dsDev->recDesc.hThread, -1); - IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer ); - } - - pj_thread_destroy (dsDev->recDesc.thread); - } - - dsound_destroy_dsound_dev( dev->device ); - dev->op = NULL; - } - - PJ_LOG(4,(THIS_FILE, "DirectSound device closed")); - return 0; -} - -static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer. - DWORD dwOffset, // Our own write cursor. - LPBYTE lpbSoundData, // Start of our data. - DWORD dwSoundBytes) // Size of block to copy. -{ - LPVOID lpvPtr1; - DWORD dwBytes1; - LPVOID lpvPtr2; - DWORD dwBytes2; - HRESULT hr; - - // Obtain memory address of write block. This will be in two parts - // if the block wraps around. - - hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1, - &dwBytes1, &lpvPtr2, &dwBytes2, 0); - - if SUCCEEDED(hr) { - // Read from pointers. - CopyMemory(lpbSoundData, lpvPtr1, dwBytes1); - if (lpvPtr2 != NULL) - CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2); - - // Release the data back to DirectSound. - hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); - if SUCCEEDED(hr) - return TRUE; - } - - // Lock, Unlock, or Restore failed. - return FALSE; -} - - -static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer. - DWORD dwOffset, // Our own write cursor. - LPBYTE lpbSoundData, // Start of our data. - DWORD dwSoundBytes) // Size of block to copy. -{ - LPVOID lpvPtr1; - DWORD dwBytes1; - LPVOID lpvPtr2; - DWORD dwBytes2; - HRESULT hr; - - // Obtain memory address of write block. This will be in two parts - // if the block wraps around. - - hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1, - &dwBytes1, &lpvPtr2, &dwBytes2, 0); - - // If the buffer was lost, restore and retry lock. - if (DSERR_BUFFERLOST == hr) { - IDirectSoundBuffer_Restore(lpDsb); - hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, - &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); - } - if SUCCEEDED(hr) { - CopyMemory(lpvPtr1, lpbSoundData, dwBytes1); - if (NULL != lpvPtr2) - CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2); - - hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); - if SUCCEEDED(hr) - return TRUE; - } - - return FALSE; -} - - -/* - * Player thread. - */ -static DWORD WINAPI dsound_dev_thread(void *arg) -{ - struct Thread_Param *param = arg; - pj_snd_dev *dev = param->dev; - Direct_Sound_Descriptor *desc = param->desc; - unsigned bytes_per_pkt = dev->param.bytes_per_frame * - dev->param.frames_per_packet; - unsigned samples_per_pkt = dev->param.samples_per_frame * - dev->param.frames_per_packet; - void *buffer = NULL; - DWORD size; - pj_status_t status; - DWORD sample_pos; - DWORD byte_pos; - DWORD buffer_size = - (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * - dev->param.frames_per_packet); - HRESULT hr; - - PJ_LOG(4,(THIS_FILE, "DirectSound thread starting")); - - /* Allocate buffer for sound data */ - buffer = malloc(bytes_per_pkt); - if (!buffer) { - PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!")); - return (DWORD)-1; - } - - desc->thread = pj_thread_register ("dsound", desc->thread_desc); - if (desc->thread == NULL) - return (DWORD)-1; - - /* - * Start playing or recording! - */ - if (desc->type == DSOUND_TYPE_PLAYER) { - hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0); - if (FAILED(hr)) { - PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr)); - goto on_error; - } - - hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING); - if (FAILED(hr)) { - PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr)); - goto on_error; - } - } else { - hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING ); - if (FAILED(hr)) { - PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr)); - goto on_error; - } - } - - /* - * Reset initial positions. - */ - byte_pos = 0xFFFFFFFF; - sample_pos = 0; - - /* - * Wait to get the first notification. - */ - if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) { - PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification")); - goto on_error; - } - - - /* Get initial byte position. */ - if (desc->type == DSOUND_TYPE_PLAYER) { - hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer, - NULL, &byte_pos ); - if (FAILED(hr)) { - PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get " - "position, err %d", hr)); - goto on_error; - } - } else { - hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer, - NULL, &byte_pos ); - if (FAILED(hr)) { - PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get " - "position, err %d", hr)); - goto on_error; - } - } - - /* Signal main thread that we're running. */ - assert( desc->hStartEvent ); - SetEvent( desc->hStartEvent ); - - /* - * Loop while not signalled to quit, wait for event object to be signalled - * by DirectSound play buffer, then request for sound data from the - * application and write it to DirectSound buffer. - */ - do { - - /* Call callback to get sound data */ - if (desc->type == DSOUND_TYPE_PLAYER) { - size = bytes_per_pkt; - status = (*dev->play_cb)(dev, sample_pos, buffer, &size); - - /* Quit thread on error. */ - if (status != 0) - break; - - /* Write zeroes when we've got nothing from application. */ - if (size == 0) { - memset(buffer, 0, bytes_per_pkt); - size = bytes_per_pkt; - } - - /* Write to DirectSound buffer. */ - AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos, - (LPBYTE)buffer, size); - - } else { - /* Capture from DirectSound buffer. */ - size = bytes_per_pkt; - if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos, - (LPBYTE)buffer, size)) { - - } else { - memset(buffer, 0, size); - } - - /* Call callback */ - status = (*dev->rec_cb)(dev, sample_pos, buffer, size); - - /* Quit thread on error. */ - if (status != 0) - break; - } - - /* Increment position. */ - byte_pos += size; - if (byte_pos >= buffer_size) - byte_pos -= buffer_size; - sample_pos += samples_per_pkt; - - while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 && - (!desc->dwThreadQuitFlag)) - { - Sleep(1); - } - } while (!desc->dwThreadQuitFlag); - - - PJ_LOG(4,(THIS_FILE, "DirectSound: stopping..")); - - free(buffer); - if (desc->type == DSOUND_TYPE_PLAYER) { - IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer ); - } else { - IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer ); - } - return 0; - -on_error: - PJ_LOG(4,(THIS_FILE, "DirectSound play stopping")); - - if (buffer) - free(buffer); - if (desc->type == DSOUND_TYPE_PLAYER) { - IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer ); - } else { - IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer ); - } - desc->dwThreadQuitFlag = 1; - - /* Signal main thread that we failed to initialize */ - assert( desc->hStartEvent ); - SetEvent( desc->hStartEvent ); - return -1; -} - - -/* - * Generic starter for play/record. - */ -static pj_status_t dsound_dev_play_record( pj_snd_dev *dev, - Direct_Sound_Descriptor *desc ) -{ - DWORD threadId; - int op_type = desc->type; - const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record"; - struct Thread_Param param; - - PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name)); - - /* - * Create event for the thread to signal us that it is starting or - * quitting during startup. - */ - desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if (desc->hStartEvent == NULL) { - PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name)); - return -1; - } - - param.dev = dev; - param.desc = desc; - - /* - * Create thread to handle feeding up data to player/recorder. - */ - desc->hThread = NULL; - desc->dwThreadQuitFlag = 0; - desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, ¶m, - CREATE_SUSPENDED, &threadId); - if (!desc->hThread) { - PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name)); - return -1; - } - - SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST); - - /* - * Resume thread. - */ - if (ResumeThread(desc->hThread) == (DWORD)-1) { - PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name)); - goto on_error; - } - - /* - * Wait until we've got signal from the thread that it has successfully - * started, or when it is quitting. - */ - WaitForSingleObject( desc->hStartEvent, INFINITE); - - /* We can destroy the event now. */ - CloseHandle( desc->hStartEvent ); - desc->hStartEvent = NULL; - - /* Examine thread status. */ - if (desc->dwThreadQuitFlag != 0) { - /* Thread failed to initialize */ - WaitForSingleObject(desc->hThread, INFINITE); - CloseHandle(desc->hThread); - desc->hThread = NULL; - return -1; - } - - return 0; - -on_error: - TerminateThread(desc->hThread, -1); - CloseHandle(desc->hThread); - desc->hThread = NULL; - return -1; -} - -/* - * Start playing. - */ -static pj_status_t dsound_dev_play( pj_snd_dev *dev ) -{ - PJ_Direct_Sound_Device *dsDev = dev->device; - - assert(dsDev); - if (!dsDev) { - assert(0); - return -1; - } - - return dsound_dev_play_record( dev, &dsDev->playDesc ); -} - -/* - * Start recording. - */ -static pj_status_t dsound_dev_record( pj_snd_dev *dev ) -{ - PJ_Direct_Sound_Device *dsDev = dev->device; - - assert(dsDev); - if (!dsDev) { - assert(0); - return -1; - } - - return dsound_dev_play_record( dev, &dsDev->recDesc ); -} - -#ifdef _MSC_VER -# pragma warning(pop) -# pragma warning(disable: 4514) // unreferenced inline function has been removed -#endif +/* $Header: /pjproject/pjmedia/src/pjmedia/dsound.c 6 6/14/05 12:54a Bennylp $ */ + +#ifdef _MSC_VER +//# pragma warning(disable: 4201) // non-standard extension: nameless struct/union +# pragma warning(push, 3) +#endif +#include +#include +#include + +#include +#include +#include +#include + +#define THIS_FILE "dsound.c" + +/* + * Constants + */ +#define PACKET_BUFFER_COUNT 4 + +typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device; + + +/* + * DirectSound Factory Operations + */ +static pj_status_t dsound_init(void); +static const char *dsound_get_name(void); +static pj_status_t dsound_destroy(void); +static pj_status_t dsound_enum_devices(int *count, char *dev_names[]); +static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev); +static pj_status_t dsound_destroy_dev(pj_snd_dev *dev); + + +/* + * DirectSound Device Operations + */ +static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ); +static pj_status_t dsound_dev_close( pj_snd_dev *dev ); +static pj_status_t dsound_dev_play( pj_snd_dev *dev ); +static pj_status_t dsound_dev_record( pj_snd_dev *dev ); + +/* + * Utils. + */ +static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev ); + + +static pj_snd_dev_factory dsound_factory = +{ + &dsound_init, + &dsound_get_name, + &dsound_destroy, + &dsound_enum_devices, + &dsound_create_dev, + &dsound_destroy_dev +}; + +static struct pj_snd_dev_op dsound_dev_op = +{ + &dsound_dev_open, + &dsound_dev_close, + &dsound_dev_play, + &dsound_dev_record +}; + +#define DSOUND_TYPE_PLAYER 1 +#define DSOUND_TYPE_RECORDER 2 + +typedef struct Direct_Sound_Descriptor +{ + int type; + + LPDIRECTSOUND lpDsPlay; + LPDIRECTSOUNDBUFFER lpDsPlayBuffer; + + LPDIRECTSOUNDCAPTURE lpDsCapture; + LPDIRECTSOUNDCAPTUREBUFFER lpDsCaptureBuffer; + + LPDIRECTSOUNDNOTIFY lpDsNotify; + HANDLE hEvent; + HANDLE hThread; + HANDLE hStartEvent; + DWORD dwThreadQuitFlag; + pj_thread_desc thread_desc; + pj_thread_t *thread; +} Direct_Sound_Descriptor; + +struct PJ_Direct_Sound_Device +{ + Direct_Sound_Descriptor playDesc; + Direct_Sound_Descriptor recDesc; +}; + +struct Thread_Param +{ + pj_snd_dev *dev; + Direct_Sound_Descriptor *desc; +}; + +PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory() +{ + return &dsound_factory; +} + +/* + * Init DirectSound. + */ +static pj_status_t dsound_init(void) +{ + /* Nothing to do. */ + return 0; +} + +/* + * Get the name of the factory. + */ +static const char *dsound_get_name(void) +{ + return "DirectSound"; +} + +/* + * Destroy DirectSound. + */ +static pj_status_t dsound_destroy(void) +{ + /* TODO: clean up devices in case application haven't done it. */ + return 0; +} + +/* + * Enum devices in the system. + */ +static pj_status_t dsound_enum_devices(int *count, char *dev_names[]) +{ + dev_names[0] = "DirectSound Default Device"; + *count = 1; + return 0; +} + +/* + * Create DirectSound device. + */ +static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev) +{ + PJ_Direct_Sound_Device *dsDev; + + /* TODO: create based on the name. */ + PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME); + + /* Create DirectSound structure. */ + dsDev = malloc(sizeof(*dsDev)); + if (!dsDev) { + PJ_LOG(1,(THIS_FILE, "No memory to allocate device!")); + return -1; + } + memset(dsDev, 0, sizeof(*dsDev)); + + /* Associate DirectSound device with device. */ + dev->device = dsDev; + dev->op = &dsound_dev_op; + + return 0; +} + +/* + * Destroy DirectSound device. + */ +static pj_status_t dsound_destroy_dev( pj_snd_dev *dev ) +{ + if (dev->device) { + free(dev->device); + dev->device = NULL; + } + return 0; +} + +static void dsound_release_descriptor(Direct_Sound_Descriptor *desc) +{ + if (desc->lpDsNotify) + IDirectSoundNotify_Release( desc->lpDsNotify ); + + if (desc->hEvent) + CloseHandle(desc->hEvent); + + if (desc->lpDsPlayBuffer) + IDirectSoundBuffer_Release( desc->lpDsPlayBuffer ); + + if (desc->lpDsPlay) + IDirectSound_Release( desc->lpDsPlay ); + + if (desc->lpDsCaptureBuffer) + IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer); + + if (desc->lpDsCapture) + IDirectSoundCapture_Release(desc->lpDsCapture); +} + +/* + * Destroy DirectSound resources. + */ +static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev ) +{ + dsound_release_descriptor( &dsDev->playDesc ); + dsound_release_descriptor( &dsDev->recDesc ); + memset(dsDev, 0, sizeof(*dsDev)); + return 0; +} + +static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev) +{ + memset(pcmwf, 0, sizeof(PCMWAVEFORMAT)); + pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM; + pcmwf->wf.nChannels = 1; + pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec; + pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame; + pcmwf->wf.nAvgBytesPerSec = + dev->param.samples_per_sec * dev->param.bytes_per_frame; + pcmwf->wBitsPerSample = dev->param.bits_per_sample; +} + +/* + * Initialize DirectSound player device. + */ +static pj_status_t dsound_init_player (pj_snd_dev *dev) +{ + HRESULT hr; + HWND hwnd; + PCMWAVEFORMAT pcmwf; + DSBUFFERDESC dsbdesc; + DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT]; + unsigned i; + PJ_Direct_Sound_Device *dsDev = dev->device; + + /* + * Check parameters. + */ + if (dev->play_cb == NULL) { + assert(0); + return -1; + } + if (dev->device == NULL) { + assert(0); + return -1; + } + + PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device")); + + /* + * Create DirectSound device. + */ + hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL); + if (FAILED(hr)) + goto on_error; + + hwnd = GetForegroundWindow(); + if (hwnd == NULL) { + hwnd = GetDesktopWindow(); + } + hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd, + DSSCL_PRIORITY); + if FAILED(hr) + goto on_error; + + /* + * Create DirectSound play buffer. + */ + // Set up wave format structure. + init_waveformatex (&pcmwf, dev); + + // Set up DSBUFFERDESC structure. + memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | + DSBCAPS_GETCURRENTPOSITION2; + + dsbdesc.dwBufferBytes = + (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * + dev->param.frames_per_packet); + dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; + + // Create buffer. + hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc, + &dsDev->playDesc.lpDsPlayBuffer, NULL); + if (FAILED(hr) ) + goto on_error; + + /* + * Create event for play notification. + */ + dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); + if (dsDev->playDesc.hEvent == NULL) + goto on_error; + + /* + * Setup notification for play. + */ + hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer, + &IID_IDirectSoundNotify, + (LPVOID *)&dsDev->playDesc.lpDsNotify); + if (FAILED(hr)) + goto on_error; + + + for (i=0; iparam.bytes_per_frame * + dev->param.frames_per_packet; + dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent; + } + + hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify, + PACKET_BUFFER_COUNT, + dsPosNotify); + if (FAILED(hr)) + goto on_error; + + /* Done setting up play device. */ + PJ_LOG(4,(THIS_FILE, "DirectSound player device created")); + + return 0; + +on_error: + PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr)); + dsound_destroy_dsound_dev(dsDev); + return -1; +} + +/* + * Initialize DirectSound recorder device + */ +static pj_status_t dsound_init_recorder (pj_snd_dev *dev) +{ + HRESULT hr; + PCMWAVEFORMAT pcmwf; + DSCBUFFERDESC dscbdesc; + DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT]; + unsigned i; + PJ_Direct_Sound_Device *dsDev = dev->device; + + /* + * Check parameters. + */ + if (dev->rec_cb == NULL) { + assert(0); + return -1; + } + if (dev->device == NULL) { + assert(0); + return -1; + } + + PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device")); + + /* + * Creating recorder device. + */ + hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL); + if (FAILED(hr)) + goto on_error; + + /* Init wave format */ + init_waveformatex (&pcmwf, dev); + + /* + * Setup capture buffer using sound buffer structure that was passed + * to play buffer creation earlier. + */ + memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC)); + dscbdesc.dwSize = sizeof(DSCBUFFERDESC); + dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ; + dscbdesc.dwBufferBytes = + (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * + dev->param.frames_per_packet); + dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; + + hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture, + &dscbdesc, + &dsDev->recDesc.lpDsCaptureBuffer, + NULL); + if (FAILED(hr)) + goto on_error; + + /* + * Create event for play notification. + */ + dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL); + if (dsDev->recDesc.hEvent == NULL) + goto on_error; + + /* + * Setup notifications for recording. + */ + hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer, + &IID_IDirectSoundNotify, + (LPVOID *)&dsDev->recDesc.lpDsNotify); + if (FAILED(hr)) + goto on_error; + + + for (i=0; iparam.bytes_per_frame * + dev->param.frames_per_packet; + dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent; + } + + hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify, + PACKET_BUFFER_COUNT, + dsPosNotify); + if (FAILED(hr)) + goto on_error; + + /* Done setting up recorder device. */ + PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created")); + + return 0; + +on_error: + PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr)); + dsound_destroy_dsound_dev(dsDev); + return -1; +} + +/* + * Initialize DirectSound device. + */ +static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ) +{ + PJ_Direct_Sound_Device *dsDev = dev->device; + pj_status_t status; + + dsDev->playDesc.type = DSOUND_TYPE_PLAYER; + dsDev->recDesc.type = DSOUND_TYPE_RECORDER; + + if (role & PJ_SOUND_PLAYER) { + status = dsound_init_player (dev); + if (status != 0) + return status; + } + + if (role & PJ_SOUND_RECORDER) { + status = dsound_init_recorder (dev); + if (status != 0) + return status; + } + + return 0; +} + +/* + * Close DirectSound device. + */ +static pj_status_t dsound_dev_close( pj_snd_dev *dev ) +{ + PJ_LOG(4,(THIS_FILE, "Closing DirectSound device")); + + if (dev->device) { + PJ_Direct_Sound_Device *dsDev = dev->device; + + if (dsDev->playDesc.hThread) { + PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player")); + dsDev->playDesc.dwThreadQuitFlag = 1; + SetEvent(dsDev->playDesc.hEvent); + if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) { + PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit")); + TerminateThread(dsDev->playDesc.hThread, -1); + IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer ); + } + + pj_thread_destroy (dsDev->playDesc.thread); + } + + if (dsDev->recDesc.hThread) { + PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder")); + dsDev->recDesc.dwThreadQuitFlag = 1; + SetEvent(dsDev->recDesc.hEvent); + if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) { + PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit")); + TerminateThread(dsDev->recDesc.hThread, -1); + IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer ); + } + + pj_thread_destroy (dsDev->recDesc.thread); + } + + dsound_destroy_dsound_dev( dev->device ); + dev->op = NULL; + } + + PJ_LOG(4,(THIS_FILE, "DirectSound device closed")); + return 0; +} + +static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer. + DWORD dwOffset, // Our own write cursor. + LPBYTE lpbSoundData, // Start of our data. + DWORD dwSoundBytes) // Size of block to copy. +{ + LPVOID lpvPtr1; + DWORD dwBytes1; + LPVOID lpvPtr2; + DWORD dwBytes2; + HRESULT hr; + + // Obtain memory address of write block. This will be in two parts + // if the block wraps around. + + hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1, + &dwBytes1, &lpvPtr2, &dwBytes2, 0); + + if SUCCEEDED(hr) { + // Read from pointers. + CopyMemory(lpbSoundData, lpvPtr1, dwBytes1); + if (lpvPtr2 != NULL) + CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2); + + // Release the data back to DirectSound. + hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); + if SUCCEEDED(hr) + return TRUE; + } + + // Lock, Unlock, or Restore failed. + return FALSE; +} + + +static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer. + DWORD dwOffset, // Our own write cursor. + LPBYTE lpbSoundData, // Start of our data. + DWORD dwSoundBytes) // Size of block to copy. +{ + LPVOID lpvPtr1; + DWORD dwBytes1; + LPVOID lpvPtr2; + DWORD dwBytes2; + HRESULT hr; + + // Obtain memory address of write block. This will be in two parts + // if the block wraps around. + + hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1, + &dwBytes1, &lpvPtr2, &dwBytes2, 0); + + // If the buffer was lost, restore and retry lock. + if (DSERR_BUFFERLOST == hr) { + IDirectSoundBuffer_Restore(lpDsb); + hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, + &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); + } + if SUCCEEDED(hr) { + CopyMemory(lpvPtr1, lpbSoundData, dwBytes1); + if (NULL != lpvPtr2) + CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2); + + hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); + if SUCCEEDED(hr) + return TRUE; + } + + return FALSE; +} + + +/* + * Player thread. + */ +static DWORD WINAPI dsound_dev_thread(void *arg) +{ + struct Thread_Param *param = arg; + pj_snd_dev *dev = param->dev; + Direct_Sound_Descriptor *desc = param->desc; + unsigned bytes_per_pkt = dev->param.bytes_per_frame * + dev->param.frames_per_packet; + unsigned samples_per_pkt = dev->param.samples_per_frame * + dev->param.frames_per_packet; + void *buffer = NULL; + DWORD size; + pj_status_t status; + DWORD sample_pos; + DWORD byte_pos; + DWORD buffer_size = + (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame * + dev->param.frames_per_packet); + HRESULT hr; + + PJ_LOG(4,(THIS_FILE, "DirectSound thread starting")); + + /* Allocate buffer for sound data */ + buffer = malloc(bytes_per_pkt); + if (!buffer) { + PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!")); + return (DWORD)-1; + } + + desc->thread = pj_thread_register ("dsound", desc->thread_desc); + if (desc->thread == NULL) + return (DWORD)-1; + + /* + * Start playing or recording! + */ + if (desc->type == DSOUND_TYPE_PLAYER) { + hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0); + if (FAILED(hr)) { + PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr)); + goto on_error; + } + + hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING); + if (FAILED(hr)) { + PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr)); + goto on_error; + } + } else { + hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING ); + if (FAILED(hr)) { + PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr)); + goto on_error; + } + } + + /* + * Reset initial positions. + */ + byte_pos = 0xFFFFFFFF; + sample_pos = 0; + + /* + * Wait to get the first notification. + */ + if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) { + PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification")); + goto on_error; + } + + + /* Get initial byte position. */ + if (desc->type == DSOUND_TYPE_PLAYER) { + hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer, + NULL, &byte_pos ); + if (FAILED(hr)) { + PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get " + "position, err %d", hr)); + goto on_error; + } + } else { + hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer, + NULL, &byte_pos ); + if (FAILED(hr)) { + PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get " + "position, err %d", hr)); + goto on_error; + } + } + + /* Signal main thread that we're running. */ + assert( desc->hStartEvent ); + SetEvent( desc->hStartEvent ); + + /* + * Loop while not signalled to quit, wait for event object to be signalled + * by DirectSound play buffer, then request for sound data from the + * application and write it to DirectSound buffer. + */ + do { + + /* Call callback to get sound data */ + if (desc->type == DSOUND_TYPE_PLAYER) { + size = bytes_per_pkt; + status = (*dev->play_cb)(dev, sample_pos, buffer, &size); + + /* Quit thread on error. */ + if (status != 0) + break; + + /* Write zeroes when we've got nothing from application. */ + if (size == 0) { + memset(buffer, 0, bytes_per_pkt); + size = bytes_per_pkt; + } + + /* Write to DirectSound buffer. */ + AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos, + (LPBYTE)buffer, size); + + } else { + /* Capture from DirectSound buffer. */ + size = bytes_per_pkt; + if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos, + (LPBYTE)buffer, size)) { + + } else { + memset(buffer, 0, size); + } + + /* Call callback */ + status = (*dev->rec_cb)(dev, sample_pos, buffer, size); + + /* Quit thread on error. */ + if (status != 0) + break; + } + + /* Increment position. */ + byte_pos += size; + if (byte_pos >= buffer_size) + byte_pos -= buffer_size; + sample_pos += samples_per_pkt; + + while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 && + (!desc->dwThreadQuitFlag)) + { + Sleep(1); + } + } while (!desc->dwThreadQuitFlag); + + + PJ_LOG(4,(THIS_FILE, "DirectSound: stopping..")); + + free(buffer); + if (desc->type == DSOUND_TYPE_PLAYER) { + IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer ); + } else { + IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer ); + } + return 0; + +on_error: + PJ_LOG(4,(THIS_FILE, "DirectSound play stopping")); + + if (buffer) + free(buffer); + if (desc->type == DSOUND_TYPE_PLAYER) { + IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer ); + } else { + IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer ); + } + desc->dwThreadQuitFlag = 1; + + /* Signal main thread that we failed to initialize */ + assert( desc->hStartEvent ); + SetEvent( desc->hStartEvent ); + return -1; +} + + +/* + * Generic starter for play/record. + */ +static pj_status_t dsound_dev_play_record( pj_snd_dev *dev, + Direct_Sound_Descriptor *desc ) +{ + DWORD threadId; + int op_type = desc->type; + const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record"; + struct Thread_Param param; + + PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name)); + + /* + * Create event for the thread to signal us that it is starting or + * quitting during startup. + */ + desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if (desc->hStartEvent == NULL) { + PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name)); + return -1; + } + + param.dev = dev; + param.desc = desc; + + /* + * Create thread to handle feeding up data to player/recorder. + */ + desc->hThread = NULL; + desc->dwThreadQuitFlag = 0; + desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, ¶m, + CREATE_SUSPENDED, &threadId); + if (!desc->hThread) { + PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name)); + return -1; + } + + SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST); + + /* + * Resume thread. + */ + if (ResumeThread(desc->hThread) == (DWORD)-1) { + PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name)); + goto on_error; + } + + /* + * Wait until we've got signal from the thread that it has successfully + * started, or when it is quitting. + */ + WaitForSingleObject( desc->hStartEvent, INFINITE); + + /* We can destroy the event now. */ + CloseHandle( desc->hStartEvent ); + desc->hStartEvent = NULL; + + /* Examine thread status. */ + if (desc->dwThreadQuitFlag != 0) { + /* Thread failed to initialize */ + WaitForSingleObject(desc->hThread, INFINITE); + CloseHandle(desc->hThread); + desc->hThread = NULL; + return -1; + } + + return 0; + +on_error: + TerminateThread(desc->hThread, -1); + CloseHandle(desc->hThread); + desc->hThread = NULL; + return -1; +} + +/* + * Start playing. + */ +static pj_status_t dsound_dev_play( pj_snd_dev *dev ) +{ + PJ_Direct_Sound_Device *dsDev = dev->device; + + assert(dsDev); + if (!dsDev) { + assert(0); + return -1; + } + + return dsound_dev_play_record( dev, &dsDev->playDesc ); +} + +/* + * Start recording. + */ +static pj_status_t dsound_dev_record( pj_snd_dev *dev ) +{ + PJ_Direct_Sound_Device *dsDev = dev->device; + + assert(dsDev); + if (!dsDev) { + assert(0); + return -1; + } + + return dsound_dev_play_record( dev, &dsDev->recDesc ); +} + +#ifdef _MSC_VER +# pragma warning(pop) +# pragma warning(disable: 4514) // unreferenced inline function has been removed +#endif diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c index 55ced1b9c..0acf2d463 100644 --- a/pjmedia/src/pjmedia/g711.c +++ b/pjmedia/src/pjmedia/g711.c @@ -1,600 +1,600 @@ -/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/g711.c 5 10/14/05 12:23a Bennylp $ */ -/* This file contains file from Sun Microsystems, Inc, with the complete - * copyright notice in the second half of this file. - */ -#include -#include -#include -#include /* memset */ - -#define G711_BPS 64000 -#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */ - -/* These are the only public functions exported to applications */ -PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool); -PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory); - -/* Algorithm prototypes. */ -static unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */ -static int alaw2linear(unsigned char a_val); -static unsigned char linear2ulaw(int pcm_val); -static int ulaw2linear(unsigned char u_val); - -/* Prototypes for G711 factory */ -static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id ); -static pj_status_t g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr ); -static unsigned g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]); -static pj_codec* g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id); -static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec ); - -/* Prototypes for G711 implementation. */ -static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr); -static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool ); -static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr ); -static pj_status_t g711_close( pj_codec *codec ); -static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); -static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output); - -/* Definition for G711 codec operations. */ -static pj_codec_op g711_op = -{ - &g711_codec_default_attr , - &g711_init, - &g711_open, - &g711_close, - &g711_encode, - &g711_decode -}; - -/* Definition for G711 codec factory operations. */ -static pj_codec_factory_op g711_factory_op = -{ - &g711_match_id, - &g711_default_attr, - &g711_enum_codecs, - &g711_alloc_codec, - &g711_dealloc_codec -}; - -/* G711 factory private data */ -struct g711_factory_private -{ - pj_pool_t *pool; - pj_codec codec_list; -}; - -/* G711 codec private data. */ -struct g711_private -{ - unsigned pt; -}; - - -PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool) -{ - struct g711_factory_private *priv; - //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 }; - - /* Create pool. */ - /* - pool = pj_pool_pool_create_pool(pp, "g711ftry", - G711_CODEC_CNT*CODEC_MEM_SIZE + - sizeof(struct g711_factory_private), - CODEC_MEM_SIZE, NULL); - if (!pool) - return -1; - */ - - priv = pj_pool_alloc(pool, sizeof(struct g711_factory_private)); - if (!priv) - return -1; - - factory->factory_data = priv; - factory->op = &g711_factory_op; - - priv->pool = pool; - pj_list_init(&priv->codec_list); - return 0; -} - -PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory) -{ - struct g711_factory_private *priv = factory->factory_data; - - /* Invalidate member to help detect errors */ - priv->pool = NULL; - priv->codec_list.next = priv->codec_list.prev = NULL; - return 0; -} - -static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id ) -{ - PJ_UNUSED_ARG(factory) - - /* It's sufficient to check payload type only. */ - return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1; -} - -static pj_status_t g711_default_attr (pj_codec_factory *factory, - const pj_codec_id *id, - pj_codec_attr *attr ) -{ - PJ_UNUSED_ARG(factory) - - memset(attr, 0, sizeof(pj_codec_attr)); - attr->sample_rate = 8000; - attr->avg_bps = G711_BPS; - attr->pcm_bits_per_sample = 16; - attr->ptime = 20; - attr->pt = id->pt; - - /* Default all flag bits disabled. */ - - return PJ_SUCCESS; -} - -static unsigned g711_enum_codecs (pj_codec_factory *factory, - unsigned count, pj_codec_id codecs[]) -{ - PJ_UNUSED_ARG(factory) - - if (count > 0) { - codecs[0].type = PJ_MEDIA_TYPE_AUDIO; - codecs[0].pt = PJ_RTP_PT_PCMU; - codecs[0].encoding_name = pj_str("PCMU"); - codecs[0].sample_rate = 8000; - } - if (count > 1) { - codecs[1].type = PJ_MEDIA_TYPE_AUDIO; - codecs[1].pt = PJ_RTP_PT_PCMA; - codecs[1].encoding_name = pj_str("PCMA"); - codecs[1].sample_rate = 8000; - } - - return 2; -} - -static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id) -{ - struct g711_factory_private *priv = factory->factory_data; - pj_codec *codec = NULL; - - /* Allocate new codec if no more is available */ - if (pj_list_empty(&priv->codec_list)) { - struct g711_private *codec_priv; - - codec = pj_pool_alloc(priv->pool, sizeof(pj_codec)); - codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private)); - if (!codec || !codec_priv) - return NULL; - - codec_priv->pt = id->pt; - - codec->factory = factory; - codec->op = &g711_op; - codec->codec_data = codec_priv; - } else { - codec = priv->codec_list.next; - pj_list_erase(codec); - } - - /* Zero the list, for error detection in g711_dealloc_codec */ - codec->next = codec->prev = NULL; - - return codec; -} - -static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec ) -{ - struct g711_factory_private *priv = factory->factory_data; - - /* Check that this node has not been deallocated before */ - pj_assert (codec->next==NULL && codec->prev==NULL); - if (codec->next!=NULL || codec->prev!=NULL) { - return; - } - - /* Insert at the back of the list */ - pj_list_insert_before(&priv->codec_list, codec); -} - -static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr) -{ - struct g711_private *priv = codec->codec_data; - pj_codec_id id; - - id.pt = priv->pt; - return g711_default_attr (NULL, &id, attr); -} - -static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool ) -{ - /* There's nothing to do here really */ - PJ_UNUSED_ARG(codec) - PJ_UNUSED_ARG(pool) - - return PJ_SUCCESS; -} - -static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr ) -{ - struct g711_private *priv = codec->codec_data; - priv->pt = attr->pt; - return PJ_SUCCESS; -} - -static pj_status_t g711_close( pj_codec *codec ) -{ - PJ_UNUSED_ARG(codec); - /* Nothing to do */ - return PJ_SUCCESS; -} - -static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output) -{ - pj_int16_t *samples = (pj_int16_t*) input->buf; - struct g711_private *priv = codec->codec_data; - - /* Check output buffer length */ - if (output_buf_len < input->size / 2) - return -1; - - /* Encode */ - if (priv->pt == PJ_RTP_PT_PCMA) { - unsigned i; - pj_uint8_t *dst = output->buf; - - for (i=0; i!=input->size/2; ++i, ++dst) { - *dst = linear2alaw(samples[i]); - } - } else if (priv->pt == PJ_RTP_PT_PCMU) { - unsigned i; - pj_uint8_t *dst = output->buf; - - for (i=0; i!=input->size/2; ++i, ++dst) { - *dst = linear2ulaw(samples[i]); - } - - } else { - return -1; - } - - output->type = PJ_AUDIO_FRAME_AUDIO; - output->size = input->size / 2; - - return 0; -} - -static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input, - unsigned output_buf_len, struct pj_audio_frame *output) -{ - struct g711_private *priv = codec->codec_data; - - /* Check output buffer length */ - if (output_buf_len < input->size * 2) - return -1; - - /* Decode */ - if (priv->pt == PJ_RTP_PT_PCMA) { - unsigned i; - pj_uint8_t *src = input->buf; - pj_uint16_t *dst = output->buf; - - for (i=0; i!=input->size; ++i) { - *dst++ = (pj_uint16_t) alaw2linear(*src++); - } - } else if (priv->pt == PJ_RTP_PT_PCMU) { - unsigned i; - pj_uint8_t *src = input->buf; - pj_uint16_t *dst = output->buf; - - for (i=0; i!=input->size; ++i) { - *dst++ = (pj_uint16_t) ulaw2linear(*src++); - } - - } else { - return -1; - } - - output->type = PJ_AUDIO_FRAME_AUDIO; - output->size = input->size * 2; - - return 0; -} - - -/* - * This source code is a product of Sun Microsystems, Inc. and is provided - * for unrestricted use. Users may copy or modify this source code without - * charge. - * - * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING - * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. - * - * Sun source code is provided with no support and without any obligation on - * the part of Sun Microsystems, Inc. to assist in its use, correction, - * modification or enhancement. - * - * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE - * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE - * OR ANY PART THEREOF. - * - * In no event will Sun Microsystems, Inc. be liable for any lost revenue - * or profits or other special, indirect and consequential damages, even if - * Sun has been advised of the possibility of such damages. - * - * Sun Microsystems, Inc. - * 2550 Garcia Avenue - * Mountain View, California 94043 - */ - - - -#ifdef _MSC_VER -# pragma warning ( disable: 4244 ) /* Conversion from int to char etc */ -#endif - -/* - * g711.c - * - * u-law, A-law and linear PCM conversions. - */ -#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ -#define QUANT_MASK (0xf) /* Quantization field mask. */ -#define NSEGS (8) /* Number of A-law segments. */ -#define SEG_SHIFT (4) /* Left shift for segment number. */ -#define SEG_MASK (0x70) /* Segment field mask. */ - -static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, - 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; - -/* copy from CCITT G.711 specifications */ -static unsigned char _u2a[128] = { /* u- to A-law conversions */ - 1, 1, 2, 2, 3, 3, 4, 4, - 5, 5, 6, 6, 7, 7, 8, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, - 25, 27, 29, 31, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, - 46, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, 62, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 81, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 93, 94, 95, 96, - 97, 98, 99, 100, 101, 102, 103, 104, - 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 126, 127, 128}; - -static unsigned char _a2u[128] = { /* A- to u-law conversions */ - 1, 3, 5, 7, 9, 11, 13, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 32, 33, 33, 34, 34, 35, 35, - 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 47, 48, 48, 49, 49, - 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 64, - 65, 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, 79, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}; - -static int -search( - int val, - short *table, - int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (val <= *table++) - return (i); - } - return (size); -} - -/* - * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law - * - * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. - * - * Linear Input Code Compressed Code - * ------------------------ --------------- - * 0000000wxyza 000wxyz - * 0000001wxyza 001wxyz - * 000001wxyzab 010wxyz - * 00001wxyzabc 011wxyz - * 0001wxyzabcd 100wxyz - * 001wxyzabcde 101wxyz - * 01wxyzabcdef 110wxyz - * 1wxyzabcdefg 111wxyz - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ -static unsigned char -linear2alaw( - int pcm_val) /* 2's complement (16-bit range) */ -{ - int mask; - int seg; - unsigned char aval; - - if (pcm_val >= 0) { - mask = 0xD5; /* sign (7th) bit = 1 */ - } else { - mask = 0x55; /* sign bit = 0 */ - pcm_val = -pcm_val - 8; - } - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, seg_end, 8); - - /* Combine the sign, segment, and quantization bits. */ - - if (seg >= 8) /* out of range, return maximum value. */ - return (0x7F ^ mask); - else { - aval = seg << SEG_SHIFT; - if (seg < 2) - aval |= (pcm_val >> 4) & QUANT_MASK; - else - aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; - return (aval ^ mask); - } -} - -/* - * alaw2linear() - Convert an A-law value to 16-bit linear PCM - * - */ -static int -alaw2linear( - unsigned char a_val) -{ - int t; - int seg; - - a_val ^= 0x55; - - t = (a_val & QUANT_MASK) << 4; - seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; - switch (seg) { - case 0: - t += 8; - break; - case 1: - t += 0x108; - break; - default: - t += 0x108; - t <<= seg - 1; - } - return ((a_val & SIGN_BIT) ? t : -t); -} - -#define BIAS (0x84) /* Bias for linear code. */ - -/* - * linear2ulaw() - Convert a linear PCM value to u-law - * - * In order to simplify the encoding process, the original linear magnitude - * is biased by adding 33 which shifts the encoding range from (0 - 8158) to - * (33 - 8191). The result can be seen in the following encoding table: - * - * Biased Linear Input Code Compressed Code - * ------------------------ --------------- - * 00000001wxyza 000wxyz - * 0000001wxyzab 001wxyz - * 000001wxyzabc 010wxyz - * 00001wxyzabcd 011wxyz - * 0001wxyzabcde 100wxyz - * 001wxyzabcdef 101wxyz - * 01wxyzabcdefg 110wxyz - * 1wxyzabcdefgh 111wxyz - * - * Each biased linear code has a leading 1 which identifies the segment - * number. The value of the segment number is equal to 7 minus the number - * of leading 0's. The quantization interval is directly available as the - * four bits wxyz. * The trailing bits (a - h) are ignored. - * - * Ordinarily the complement of the resulting code word is used for - * transmission, and so the code word is complemented before it is returned. - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ -static unsigned char -linear2ulaw( - int pcm_val) /* 2's complement (16-bit range) */ -{ - int mask; - int seg; - unsigned char uval; - - /* Get the sign and the magnitude of the value. */ - if (pcm_val < 0) { - pcm_val = BIAS - pcm_val; - mask = 0x7F; - } else { - pcm_val += BIAS; - mask = 0xFF; - } - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, seg_end, 8); - - /* - * Combine the sign, segment, quantization bits; - * and complement the code word. - */ - if (seg >= 8) /* out of range, return maximum value. */ - return (0x7F ^ mask); - else { - uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); - return (uval ^ mask); - } - -} - -/* - * ulaw2linear() - Convert a u-law value to 16-bit linear PCM - * - * First, a biased linear code is derived from the code word. An unbiased - * output can then be obtained by subtracting 33 from the biased code. - * - * Note that this function expects to be passed the complement of the - * original code word. This is in keeping with ISDN conventions. - */ -static int -ulaw2linear( - unsigned char u_val) -{ - int t; - - /* Complement to obtain normal u-law value. */ - u_val = ~u_val; - - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = ((u_val & QUANT_MASK) << 3) + BIAS; - t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; - - return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); -} - -/* A-law to u-law conversion */ -unsigned char -alaw2ulaw( - unsigned char aval) -{ - aval &= 0xff; - return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : - (0x7F ^ _a2u[aval ^ 0x55])); -} - -/* u-law to A-law conversion */ -unsigned char -ulaw2alaw( - unsigned char uval) -{ - uval &= 0xff; - return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : - (0x55 ^ (_u2a[0x7F ^ uval] - 1))); -} - - - +/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/g711.c 5 10/14/05 12:23a Bennylp $ */ +/* This file contains file from Sun Microsystems, Inc, with the complete + * copyright notice in the second half of this file. + */ +#include +#include +#include +#include /* memset */ + +#define G711_BPS 64000 +#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */ + +/* These are the only public functions exported to applications */ +PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool); +PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory); + +/* Algorithm prototypes. */ +static unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */ +static int alaw2linear(unsigned char a_val); +static unsigned char linear2ulaw(int pcm_val); +static int ulaw2linear(unsigned char u_val); + +/* Prototypes for G711 factory */ +static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id ); +static pj_status_t g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr ); +static unsigned g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]); +static pj_codec* g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id); +static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec ); + +/* Prototypes for G711 implementation. */ +static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr); +static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool ); +static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr ); +static pj_status_t g711_close( pj_codec *codec ); +static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input, + unsigned output_buf_len, struct pj_audio_frame *output); +static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input, + unsigned output_buf_len, struct pj_audio_frame *output); + +/* Definition for G711 codec operations. */ +static pj_codec_op g711_op = +{ + &g711_codec_default_attr , + &g711_init, + &g711_open, + &g711_close, + &g711_encode, + &g711_decode +}; + +/* Definition for G711 codec factory operations. */ +static pj_codec_factory_op g711_factory_op = +{ + &g711_match_id, + &g711_default_attr, + &g711_enum_codecs, + &g711_alloc_codec, + &g711_dealloc_codec +}; + +/* G711 factory private data */ +struct g711_factory_private +{ + pj_pool_t *pool; + pj_codec codec_list; +}; + +/* G711 codec private data. */ +struct g711_private +{ + unsigned pt; +}; + + +PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool) +{ + struct g711_factory_private *priv; + //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 }; + + /* Create pool. */ + /* + pool = pj_pool_pool_create_pool(pp, "g711ftry", + G711_CODEC_CNT*CODEC_MEM_SIZE + + sizeof(struct g711_factory_private), + CODEC_MEM_SIZE, NULL); + if (!pool) + return -1; + */ + + priv = pj_pool_alloc(pool, sizeof(struct g711_factory_private)); + if (!priv) + return -1; + + factory->factory_data = priv; + factory->op = &g711_factory_op; + + priv->pool = pool; + pj_list_init(&priv->codec_list); + return 0; +} + +PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory) +{ + struct g711_factory_private *priv = factory->factory_data; + + /* Invalidate member to help detect errors */ + priv->pool = NULL; + priv->codec_list.next = priv->codec_list.prev = NULL; + return 0; +} + +static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id ) +{ + PJ_UNUSED_ARG(factory) + + /* It's sufficient to check payload type only. */ + return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1; +} + +static pj_status_t g711_default_attr (pj_codec_factory *factory, + const pj_codec_id *id, + pj_codec_attr *attr ) +{ + PJ_UNUSED_ARG(factory) + + memset(attr, 0, sizeof(pj_codec_attr)); + attr->sample_rate = 8000; + attr->avg_bps = G711_BPS; + attr->pcm_bits_per_sample = 16; + attr->ptime = 20; + attr->pt = id->pt; + + /* Default all flag bits disabled. */ + + return PJ_SUCCESS; +} + +static unsigned g711_enum_codecs (pj_codec_factory *factory, + unsigned count, pj_codec_id codecs[]) +{ + PJ_UNUSED_ARG(factory) + + if (count > 0) { + codecs[0].type = PJ_MEDIA_TYPE_AUDIO; + codecs[0].pt = PJ_RTP_PT_PCMU; + codecs[0].encoding_name = pj_str("PCMU"); + codecs[0].sample_rate = 8000; + } + if (count > 1) { + codecs[1].type = PJ_MEDIA_TYPE_AUDIO; + codecs[1].pt = PJ_RTP_PT_PCMA; + codecs[1].encoding_name = pj_str("PCMA"); + codecs[1].sample_rate = 8000; + } + + return 2; +} + +static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id) +{ + struct g711_factory_private *priv = factory->factory_data; + pj_codec *codec = NULL; + + /* Allocate new codec if no more is available */ + if (pj_list_empty(&priv->codec_list)) { + struct g711_private *codec_priv; + + codec = pj_pool_alloc(priv->pool, sizeof(pj_codec)); + codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private)); + if (!codec || !codec_priv) + return NULL; + + codec_priv->pt = id->pt; + + codec->factory = factory; + codec->op = &g711_op; + codec->codec_data = codec_priv; + } else { + codec = priv->codec_list.next; + pj_list_erase(codec); + } + + /* Zero the list, for error detection in g711_dealloc_codec */ + codec->next = codec->prev = NULL; + + return codec; +} + +static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec ) +{ + struct g711_factory_private *priv = factory->factory_data; + + /* Check that this node has not been deallocated before */ + pj_assert (codec->next==NULL && codec->prev==NULL); + if (codec->next!=NULL || codec->prev!=NULL) { + return; + } + + /* Insert at the back of the list */ + pj_list_insert_before(&priv->codec_list, codec); +} + +static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr) +{ + struct g711_private *priv = codec->codec_data; + pj_codec_id id; + + id.pt = priv->pt; + return g711_default_attr (NULL, &id, attr); +} + +static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool ) +{ + /* There's nothing to do here really */ + PJ_UNUSED_ARG(codec) + PJ_UNUSED_ARG(pool) + + return PJ_SUCCESS; +} + +static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr ) +{ + struct g711_private *priv = codec->codec_data; + priv->pt = attr->pt; + return PJ_SUCCESS; +} + +static pj_status_t g711_close( pj_codec *codec ) +{ + PJ_UNUSED_ARG(codec); + /* Nothing to do */ + return PJ_SUCCESS; +} + +static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input, + unsigned output_buf_len, struct pj_audio_frame *output) +{ + pj_int16_t *samples = (pj_int16_t*) input->buf; + struct g711_private *priv = codec->codec_data; + + /* Check output buffer length */ + if (output_buf_len < input->size / 2) + return -1; + + /* Encode */ + if (priv->pt == PJ_RTP_PT_PCMA) { + unsigned i; + pj_uint8_t *dst = output->buf; + + for (i=0; i!=input->size/2; ++i, ++dst) { + *dst = linear2alaw(samples[i]); + } + } else if (priv->pt == PJ_RTP_PT_PCMU) { + unsigned i; + pj_uint8_t *dst = output->buf; + + for (i=0; i!=input->size/2; ++i, ++dst) { + *dst = linear2ulaw(samples[i]); + } + + } else { + return -1; + } + + output->type = PJ_AUDIO_FRAME_AUDIO; + output->size = input->size / 2; + + return 0; +} + +static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input, + unsigned output_buf_len, struct pj_audio_frame *output) +{ + struct g711_private *priv = codec->codec_data; + + /* Check output buffer length */ + if (output_buf_len < input->size * 2) + return -1; + + /* Decode */ + if (priv->pt == PJ_RTP_PT_PCMA) { + unsigned i; + pj_uint8_t *src = input->buf; + pj_uint16_t *dst = output->buf; + + for (i=0; i!=input->size; ++i) { + *dst++ = (pj_uint16_t) alaw2linear(*src++); + } + } else if (priv->pt == PJ_RTP_PT_PCMU) { + unsigned i; + pj_uint8_t *src = input->buf; + pj_uint16_t *dst = output->buf; + + for (i=0; i!=input->size; ++i) { + *dst++ = (pj_uint16_t) ulaw2linear(*src++); + } + + } else { + return -1; + } + + output->type = PJ_AUDIO_FRAME_AUDIO; + output->size = input->size * 2; + + return 0; +} + + +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + + + +#ifdef _MSC_VER +# pragma warning ( disable: 4244 ) /* Conversion from int to char etc */ +#endif + +/* + * g711.c + * + * u-law, A-law and linear PCM conversions. + */ +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; + +/* copy from CCITT G.711 specifications */ +static unsigned char _u2a[128] = { /* u- to A-law conversions */ + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128}; + +static unsigned char _a2u[128] = { /* A- to u-law conversions */ + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}; + +static int +search( + int val, + short *table, + int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +static unsigned char +linear2alaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + aval = seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 4) & QUANT_MASK; + else + aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; + return (aval ^ mask); + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ +static int +alaw2linear( + unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + + t = (a_val & QUANT_MASK) << 4; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return ((a_val & SIGN_BIT) ? t : -t); +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +static unsigned char +linear2ulaw( + int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return (uval ^ mask); + } + +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static int +ulaw2linear( + unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +/* A-law to u-law conversion */ +unsigned char +alaw2ulaw( + unsigned char aval) +{ + aval &= 0xff; + return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : + (0x7F ^ _a2u[aval ^ 0x55])); +} + +/* u-law to A-law conversion */ +unsigned char +ulaw2alaw( + unsigned char uval) +{ + uval &= 0xff; + return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : + (0x55 ^ (_u2a[0x7F ^ uval] - 1))); +} + + + diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c index 1120fcdf1..dc9d6e038 100644 --- a/pjmedia/src/pjmedia/jbuf.c +++ b/pjmedia/src/pjmedia/jbuf.c @@ -1,404 +1,404 @@ -/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/jbuf.c 8 10/14/05 12:23a Bennylp $ */ - -#include -#include -#include -#include /* memset() */ - -/* - * At the current state, this is basicly an ugly jitter buffer. - * It worked before by observing level, bit it doesn't work. - * Then I used the size, which makes the level obsolete. - * That's why it's ugly! - */ - -#define MAX_SEQ_RANGE 1000 /* Range in which sequence is considered still within session */ -#define UPDATE_DURATION 20 /* Number of frames retrieved before jitter is updated */ - -#define THIS_FILE "jbuf" - -/* Individual frame in the frame list. */ -struct pj_jbframe -{ - pj_uint32_t extseq; - void *buf; -}; - - -/* Jitter buffer state. */ -typedef enum jb_state_t -{ - JB_PREFETCH, - JB_NORMAL, -} jb_state_t; - - -/* Jitter buffer last operation. */ -typedef enum jb_op_t -{ - JB_PUT, - JB_GET, -} jb_op_t; - - -/* Short name for convenience. */ -typedef struct pj_jitter_buffer JB; - - -/* Initialize framelist. */ -static pj_status_t -pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount ) -{ - PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount)); - - memset(lst, 0, sizeof(*lst)); - lst->maxcount = maxcount; - lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) ); - if (lst->frames == NULL) { - PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!")); - return -1; - } - return 0; -} - -/* Reset framelist. */ -static void -pj_framelist_reset( pj_jbframelist *lst ) -{ - PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst)); - - lst->count = 0; - lst->head = 0; - lst->frames[0].extseq = 0; -} - -/* Put a buffer with the specified sequence into the ordered list. */ -static int -pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf ) -{ - unsigned pos = (unsigned)-1; - pj_uint32_t startseq = lst->frames[lst->head].extseq; - - if (lst->count == 0) { - /* Empty list. Initialize frame list. */ - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d", - lst, extseq, lst->head)); - - lst->head = 0; - lst->count = 1; - lst->frames[0].buf = buf; - lst->frames[0].extseq = extseq; - return 0; - - } else if (extseq < startseq) { - /* The sequence number is lower than our oldest packet. This can mean - two things: - - old packet has been receieved, or - - the sequence number has wrapped around. - */ - if (startseq + lst->maxcount <= extseq) { - /* The sequence number has wrapped around, but it is beyond - the capacity of the list (i.e. too soon). - */ - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_SOON; - - } else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) { - /* The sequence number has wrapped around, and it is still inside - the 'window' of the framelist. - */ - pos = extseq - startseq; - } else { - /* The new frame is too old. */ - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_OLD; - } - - } else if (extseq > startseq + lst->maxcount) { - /* Two possibilities here. Either: - - packet is really too soon, or - - sequence number of startseq has just wrapped around, and old packet - which hasn't wrapped is received. - */ - if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) { - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_SOON; - } else { - PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", - lst, extseq, startseq)); - return PJ_JB_STATUS_TOO_OLD; - } - } - - /* The new frame is within the framelist capacity. - Calculate position where to put it in the list. - */ - if (pos == (unsigned)-1) - pos = ((extseq - startseq) + lst->head) % lst->maxcount; - - pj_assert(pos < lst->maxcount); - - /* Update count only if we're not overwriting existing frame. */ - if (lst->frames[pos].buf == NULL) - ++lst->count; - - lst->frames[pos].buf = buf; - lst->frames[pos].extseq = extseq; - - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d", - lst, extseq, startseq, lst->head, pos)); - return 0; -} - -/* Get the first element of the list. */ -static int -pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf ) -{ - if (lst->count == 0) { - /* Empty. */ - *buf = NULL; - *extseq = 0; - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p], empty!", lst)); - return -1; - - } else { - *buf = lst->frames[lst->head].buf; - *extseq = lst->frames[lst->head].extseq; - lst->frames[lst->head].buf = NULL; - - PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p] seq=%u, head=%d", - lst, *extseq, lst->head)); - - lst->head = (lst->head + 1) % lst->maxcount; - --lst->count; - return 0; - } -} - - -/***************************************************************************** - * Reset jitter buffer. - **************************************************************************** -*/ -PJ_DEF(void) pj_jb_reset(JB *jb) -{ - PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb)); - - jb->level = jb->max_level = 1; - jb->prefetch = jb->min; - jb->get_cnt = 0; - jb->lastseq = 0; - jb->state = JB_PREFETCH; - jb->upd_count = 0; - jb->last_op = -1; - pj_framelist_reset( &jb->lst ); -} - - -/***************************************************************************** - * Create jitter buffer. - ***************************************************************************** - */ -PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool, - unsigned min, unsigned max, unsigned maxcount) -{ - pj_status_t status; - - if (maxcount <= max) { - maxcount = max * 5 / 4; - PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted.")); - } - - jb->min = min; - jb->max = max; - - status = pj_framelist_init( &jb->lst, pool, maxcount ); - if (status != PJ_SUCCESS) - return status; - - pj_jb_reset(jb); - - PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d", - jb, min, max, maxcount)); - return PJ_SUCCESS; -} - - -/***************************************************************************** - * Put a packet to the jitter buffer. - ***************************************************************************** - */ -PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf ) -{ - unsigned distance; - int status; - - PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf)); - - if (jb->lastseq == 0) - jb->lastseq = extseq - 1; - - /* Calculate distance between this packet and last received packet - to detect long jump (indicating probably remote has just been - restarted. - */ - distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq; - if (distance > MAX_SEQ_RANGE) { - /* Distance is out of range, reset jitter while maintaining current jitter - level. - */ - int old_level = jb->level; - int old_prefetch = jb->prefetch; - - PJ_LOG(4, (THIS_FILE, " ..[jb=%p] distance out of range, resetting", jb)); - - pj_jb_reset(jb); - jb->level = old_level; - jb->prefetch = old_prefetch; - distance = 1; - jb->lastseq = extseq - 1; - } - - jb->lastseq = extseq; - - status = pj_framelist_put( &jb->lst, extseq, buf ); - if (status == PJ_JB_STATUS_TOO_OLD) - return -1; - - if (status == PJ_JB_STATUS_TOO_SOON) { - /* TODO: discard old packets.. */ - /* No, don't do it without putting a way to inform application so that - it can free the memory */ - } - - - if (jb->last_op != JB_PUT) { - if (jb->state != JB_PREFETCH) - jb->level--; - } else { - jb->level++; - } - - if (jb->lst.count > jb->max_level) - jb->max_level++; - - jb->last_op = JB_PUT; - return 0; -} - - -/* - * Update jitter buffer algorithm. - */ -static void jb_update(JB *jb, int apply, int log_info) -{ - unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level; - unsigned new_prefetch; - - /* Update prefetch count */ - if (abs_level > jb->prefetch) - new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10; - else { - new_prefetch = (jb->prefetch*4 + abs_level) / 5; - pj_assert(new_prefetch <= jb->prefetch); - } - - if (log_info) { - PJ_LOG(5, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", - jb, jb->level, jb->max_level, jb->prefetch, new_prefetch)); - } else { - PJ_LOG(6, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", - jb, jb->level, jb->max_level, jb->prefetch, new_prefetch)); - } - - if (new_prefetch < jb->min) new_prefetch = jb->min; - if (new_prefetch > jb->max) new_prefetch = jb->max; - - /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the - new prefetch setting. - */ - if (jb->lst.count == 0) { - jb->state = JB_PREFETCH; - jb->get_cnt = 0; - } else { - /* Check if delay is too long, which in this case probably better to - discard some frames.. - */ - /* No, don't do it without putting a way to inform application so that - it can free the memory */ - } - - - if (apply) { - jb->prefetch = new_prefetch; - if (jb->max_level > 0) - jb->max_level--; - } else { - jb->level = new_prefetch; - } -} - - -/***************************************************************************** - * Get the oldest frame from jitter buffer. - ***************************************************************************** - */ -PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf ) -{ - pj_status_t status; - - PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb)); - - /* - * Check whether we're ready to give frame. When we're in JB_PREFETCH state, - * only give frames only when: - * - the buffer has enough frames in it (jb->list.count > jb->prefetch), OR - * - after 'prefetch' attempts, there's still no frame, which in this - * case PJ_JB_STATUS_FRAME_NULL will be returned by the next check. - */ - if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) { - jb->get_cnt++; - jb->last_op = JB_GET; - PJ_LOG(5, (THIS_FILE, " ..[jb=%p] bufferring...", jb)); - return PJ_JB_STATUS_FRAME_NULL; - } - - /* Attempt to get one frame from the list. */ - status = pj_framelist_get( &jb->lst, extseq, buf ); - if (status != 0) { - PJ_LOG(6, (THIS_FILE, " ..[jb=%p] no packet!", jb)); - status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL; - jb_update(jb, 1, 0); - return status; - } - - /* Force state to NORMAL */ - jb->state = JB_NORMAL; - - /* Increase level only when last operation is GET. - * This is to prevent level from increasing during silence period, which - * no packets is receieved. - */ - if (jb->last_op != JB_GET) { - int apply; - - //jb->level++; - jb->last_op = JB_GET; - - apply = (++jb->upd_count > UPDATE_DURATION); - if (apply) - jb->upd_count = 0; - - jb_update(jb, apply, apply); - } - - PJ_LOG(6, (THIS_FILE, " ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d", - jb, *extseq, jb->level, jb->prefetch, jb->lst.count, - jb->lastseq - *extseq)); - return 0; -} - - +/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/jbuf.c 8 10/14/05 12:23a Bennylp $ */ + +#include +#include +#include +#include /* memset() */ + +/* + * At the current state, this is basicly an ugly jitter buffer. + * It worked before by observing level, bit it doesn't work. + * Then I used the size, which makes the level obsolete. + * That's why it's ugly! + */ + +#define MAX_SEQ_RANGE 1000 /* Range in which sequence is considered still within session */ +#define UPDATE_DURATION 20 /* Number of frames retrieved before jitter is updated */ + +#define THIS_FILE "jbuf" + +/* Individual frame in the frame list. */ +struct pj_jbframe +{ + pj_uint32_t extseq; + void *buf; +}; + + +/* Jitter buffer state. */ +typedef enum jb_state_t +{ + JB_PREFETCH, + JB_NORMAL, +} jb_state_t; + + +/* Jitter buffer last operation. */ +typedef enum jb_op_t +{ + JB_PUT, + JB_GET, +} jb_op_t; + + +/* Short name for convenience. */ +typedef struct pj_jitter_buffer JB; + + +/* Initialize framelist. */ +static pj_status_t +pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount ) +{ + PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount)); + + memset(lst, 0, sizeof(*lst)); + lst->maxcount = maxcount; + lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) ); + if (lst->frames == NULL) { + PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!")); + return -1; + } + return 0; +} + +/* Reset framelist. */ +static void +pj_framelist_reset( pj_jbframelist *lst ) +{ + PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst)); + + lst->count = 0; + lst->head = 0; + lst->frames[0].extseq = 0; +} + +/* Put a buffer with the specified sequence into the ordered list. */ +static int +pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf ) +{ + unsigned pos = (unsigned)-1; + pj_uint32_t startseq = lst->frames[lst->head].extseq; + + if (lst->count == 0) { + /* Empty list. Initialize frame list. */ + PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d", + lst, extseq, lst->head)); + + lst->head = 0; + lst->count = 1; + lst->frames[0].buf = buf; + lst->frames[0].extseq = extseq; + return 0; + + } else if (extseq < startseq) { + /* The sequence number is lower than our oldest packet. This can mean + two things: + - old packet has been receieved, or + - the sequence number has wrapped around. + */ + if (startseq + lst->maxcount <= extseq) { + /* The sequence number has wrapped around, but it is beyond + the capacity of the list (i.e. too soon). + */ + PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", + lst, extseq, startseq)); + return PJ_JB_STATUS_TOO_SOON; + + } else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) { + /* The sequence number has wrapped around, and it is still inside + the 'window' of the framelist. + */ + pos = extseq - startseq; + } else { + /* The new frame is too old. */ + PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", + lst, extseq, startseq)); + return PJ_JB_STATUS_TOO_OLD; + } + + } else if (extseq > startseq + lst->maxcount) { + /* Two possibilities here. Either: + - packet is really too soon, or + - sequence number of startseq has just wrapped around, and old packet + which hasn't wrapped is received. + */ + if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) { + PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d", + lst, extseq, startseq)); + return PJ_JB_STATUS_TOO_SOON; + } else { + PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d", + lst, extseq, startseq)); + return PJ_JB_STATUS_TOO_OLD; + } + } + + /* The new frame is within the framelist capacity. + Calculate position where to put it in the list. + */ + if (pos == (unsigned)-1) + pos = ((extseq - startseq) + lst->head) % lst->maxcount; + + pj_assert(pos < lst->maxcount); + + /* Update count only if we're not overwriting existing frame. */ + if (lst->frames[pos].buf == NULL) + ++lst->count; + + lst->frames[pos].buf = buf; + lst->frames[pos].extseq = extseq; + + PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d", + lst, extseq, startseq, lst->head, pos)); + return 0; +} + +/* Get the first element of the list. */ +static int +pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf ) +{ + if (lst->count == 0) { + /* Empty. */ + *buf = NULL; + *extseq = 0; + PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p], empty!", lst)); + return -1; + + } else { + *buf = lst->frames[lst->head].buf; + *extseq = lst->frames[lst->head].extseq; + lst->frames[lst->head].buf = NULL; + + PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p] seq=%u, head=%d", + lst, *extseq, lst->head)); + + lst->head = (lst->head + 1) % lst->maxcount; + --lst->count; + return 0; + } +} + + +/***************************************************************************** + * Reset jitter buffer. + **************************************************************************** +*/ +PJ_DEF(void) pj_jb_reset(JB *jb) +{ + PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb)); + + jb->level = jb->max_level = 1; + jb->prefetch = jb->min; + jb->get_cnt = 0; + jb->lastseq = 0; + jb->state = JB_PREFETCH; + jb->upd_count = 0; + jb->last_op = -1; + pj_framelist_reset( &jb->lst ); +} + + +/***************************************************************************** + * Create jitter buffer. + ***************************************************************************** + */ +PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool, + unsigned min, unsigned max, unsigned maxcount) +{ + pj_status_t status; + + if (maxcount <= max) { + maxcount = max * 5 / 4; + PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted.")); + } + + jb->min = min; + jb->max = max; + + status = pj_framelist_init( &jb->lst, pool, maxcount ); + if (status != PJ_SUCCESS) + return status; + + pj_jb_reset(jb); + + PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d", + jb, min, max, maxcount)); + return PJ_SUCCESS; +} + + +/***************************************************************************** + * Put a packet to the jitter buffer. + ***************************************************************************** + */ +PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf ) +{ + unsigned distance; + int status; + + PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf)); + + if (jb->lastseq == 0) + jb->lastseq = extseq - 1; + + /* Calculate distance between this packet and last received packet + to detect long jump (indicating probably remote has just been + restarted. + */ + distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq; + if (distance > MAX_SEQ_RANGE) { + /* Distance is out of range, reset jitter while maintaining current jitter + level. + */ + int old_level = jb->level; + int old_prefetch = jb->prefetch; + + PJ_LOG(4, (THIS_FILE, " ..[jb=%p] distance out of range, resetting", jb)); + + pj_jb_reset(jb); + jb->level = old_level; + jb->prefetch = old_prefetch; + distance = 1; + jb->lastseq = extseq - 1; + } + + jb->lastseq = extseq; + + status = pj_framelist_put( &jb->lst, extseq, buf ); + if (status == PJ_JB_STATUS_TOO_OLD) + return -1; + + if (status == PJ_JB_STATUS_TOO_SOON) { + /* TODO: discard old packets.. */ + /* No, don't do it without putting a way to inform application so that + it can free the memory */ + } + + + if (jb->last_op != JB_PUT) { + if (jb->state != JB_PREFETCH) + jb->level--; + } else { + jb->level++; + } + + if (jb->lst.count > jb->max_level) + jb->max_level++; + + jb->last_op = JB_PUT; + return 0; +} + + +/* + * Update jitter buffer algorithm. + */ +static void jb_update(JB *jb, int apply, int log_info) +{ + unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level; + unsigned new_prefetch; + + /* Update prefetch count */ + if (abs_level > jb->prefetch) + new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10; + else { + new_prefetch = (jb->prefetch*4 + abs_level) / 5; + pj_assert(new_prefetch <= jb->prefetch); + } + + if (log_info) { + PJ_LOG(5, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", + jb, jb->level, jb->max_level, jb->prefetch, new_prefetch)); + } else { + PJ_LOG(6, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d", + jb, jb->level, jb->max_level, jb->prefetch, new_prefetch)); + } + + if (new_prefetch < jb->min) new_prefetch = jb->min; + if (new_prefetch > jb->max) new_prefetch = jb->max; + + /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the + new prefetch setting. + */ + if (jb->lst.count == 0) { + jb->state = JB_PREFETCH; + jb->get_cnt = 0; + } else { + /* Check if delay is too long, which in this case probably better to + discard some frames.. + */ + /* No, don't do it without putting a way to inform application so that + it can free the memory */ + } + + + if (apply) { + jb->prefetch = new_prefetch; + if (jb->max_level > 0) + jb->max_level--; + } else { + jb->level = new_prefetch; + } +} + + +/***************************************************************************** + * Get the oldest frame from jitter buffer. + ***************************************************************************** + */ +PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf ) +{ + pj_status_t status; + + PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb)); + + /* + * Check whether we're ready to give frame. When we're in JB_PREFETCH state, + * only give frames only when: + * - the buffer has enough frames in it (jb->list.count > jb->prefetch), OR + * - after 'prefetch' attempts, there's still no frame, which in this + * case PJ_JB_STATUS_FRAME_NULL will be returned by the next check. + */ + if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) { + jb->get_cnt++; + jb->last_op = JB_GET; + PJ_LOG(5, (THIS_FILE, " ..[jb=%p] bufferring...", jb)); + return PJ_JB_STATUS_FRAME_NULL; + } + + /* Attempt to get one frame from the list. */ + status = pj_framelist_get( &jb->lst, extseq, buf ); + if (status != 0) { + PJ_LOG(6, (THIS_FILE, " ..[jb=%p] no packet!", jb)); + status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL; + jb_update(jb, 1, 0); + return status; + } + + /* Force state to NORMAL */ + jb->state = JB_NORMAL; + + /* Increase level only when last operation is GET. + * This is to prevent level from increasing during silence period, which + * no packets is receieved. + */ + if (jb->last_op != JB_GET) { + int apply; + + //jb->level++; + jb->last_op = JB_GET; + + apply = (++jb->upd_count > UPDATE_DURATION); + if (apply) + jb->upd_count = 0; + + jb_update(jb, apply, apply); + } + + PJ_LOG(6, (THIS_FILE, " ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d", + jb, *extseq, jb->level, jb->prefetch, jb->lst.count, + jb->lastseq - *extseq)); + return 0; +} + + diff --git a/pjmedia/src/pjmedia/jbuf.h b/pjmedia/src/pjmedia/jbuf.h index 5bd4a5e9d..4d1e9f4e3 100644 --- a/pjmedia/src/pjmedia/jbuf.h +++ b/pjmedia/src/pjmedia/jbuf.h @@ -1,126 +1,126 @@ -/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/jbuf.h 7 10/14/05 12:23a Bennylp $ */ - -#ifndef __PJMEDIA_JBUF_H__ -#define __PJMEDIA_JBUF_H__ - - -/** - * @file jbuf.h - * @brief Adaptive jitter buffer implementation. - */ -/** - * @defgroup PJMED_JBUF Adaptive jitter buffer - * @ingroup PJMEDIA - * @{ - */ - -#include - - -PJ_BEGIN_DECL - - -/** - * Opaque declaration of internal frame type used by jitter buffer. - */ -struct pj_jbframe; - - -/** - * Miscelaneous operation result/status. - */ -typedef enum jb_op_status -{ - PJ_JB_STATUS_TOO_OLD = -2, /** The packet is too old. */ - PJ_JB_STATUS_TOO_SOON = -3, /** The packet is too soon. */ - PJ_JB_STATUS_FRAME_NULL = -4, /** No packet can be retrieved */ - PJ_JB_STATUS_FRAME_MISSING = -5, /** The specified packet is missing/lost */ -} jb_op_status; - - -/* - * Frame list, container abstraction for ordered list with fixed maximum - * size. It is used internally by the jitter buffer. - */ -typedef struct pj_jbframelist -{ - unsigned head, count, maxcount; - struct pj_jbframe *frames; -} pj_jbframelist; - - -/** - * Jitter buffer implementation. - */ -typedef struct pj_jitter_buffer -{ - pj_jbframelist lst; /** The frame list. */ - int level; /** Current, real-time jitter level. */ - int max_level; /** Maximum level for the period. */ - unsigned prefetch; /** Prefetch count currently used. */ - unsigned get_cnt; /** Number of get operation during prefetch state. */ - unsigned min; /** Minimum jitter size, in packets. */ - unsigned max; /** Maximum jitter size, in packets. */ - pj_uint32_t lastseq; /** Last sequence number put to jitter buffer. */ - unsigned upd_count; /** Internal counter to manage update interval. */ - int state; /** Jitter buffer state (1==operational) */ - int last_op; /** Last jitter buffer operation. */ -} pj_jitter_buffer; - - -/** - * Initialize jitter buffer with the specified parameters. - * This function will allocate internal frame buffer from the specified pool. - * @param jb The jitter buffer to be initialized. - * @param pool Pool where memory will be allocated for the frame buffer. - * @param min The minimum value of jitter buffer, in packets. - * @param max The maximum value of jitter buffer, in packets. - * @param maxcount The maximum number of delay, in packets, which must be - * greater than max. - * @return PJ_SUCCESS on success. - */ -PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool, - unsigned min, unsigned max, unsigned maxcount); - -/** - * Reset jitter buffer according to the parameters specified when the jitter - * buffer was initialized. Any packets currently in the buffer will be - * discarded. - */ -PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb); - -/** - * Put a pointer to the buffer with the specified sequence number. The pointer - * normally points to a buffer held by application, and this pointer will be - * returned later on when pj_jb_get() is called. The jitter buffer will not try - * to interpret the content of this pointer. - * @return: - * - PJ_SUCCESS on success. - * - PJ_JB_STATUS_TOO_OLD when the packet is too old. - * - PJ_JB_STATUS_TOO_SOON when the packet is too soon. - */ -PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf ); - -/** - * Get the earliest data from the jitter buffer. ONLY when the operation succeeds, - * the function returns both sequence number and a pointer in the parameters. - * This returned data corresponds to sequence number and pointer that were - * given to jitter buffer in the previous pj_jb_put operation. - * @return - * - PJ_SUCCESS on success - * - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned. - * - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet - * is lost. Application may run packet concealment algorithm when it - * receives this status. - */ -PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf ); - - - -PJ_END_DECL - -/** - * @} - */ - -#endif /* __PJMEDIA_JBUF_H__ */ +/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/jbuf.h 7 10/14/05 12:23a Bennylp $ */ + +#ifndef __PJMEDIA_JBUF_H__ +#define __PJMEDIA_JBUF_H__ + + +/** + * @file jbuf.h + * @brief Adaptive jitter buffer implementation. + */ +/** + * @defgroup PJMED_JBUF Adaptive jitter buffer + * @ingroup PJMEDIA + * @{ + */ + +#include + + +PJ_BEGIN_DECL + + +/** + * Opaque declaration of internal frame type used by jitter buffer. + */ +struct pj_jbframe; + + +/** + * Miscelaneous operation result/status. + */ +typedef enum jb_op_status +{ + PJ_JB_STATUS_TOO_OLD = -2, /** The packet is too old. */ + PJ_JB_STATUS_TOO_SOON = -3, /** The packet is too soon. */ + PJ_JB_STATUS_FRAME_NULL = -4, /** No packet can be retrieved */ + PJ_JB_STATUS_FRAME_MISSING = -5, /** The specified packet is missing/lost */ +} jb_op_status; + + +/* + * Frame list, container abstraction for ordered list with fixed maximum + * size. It is used internally by the jitter buffer. + */ +typedef struct pj_jbframelist +{ + unsigned head, count, maxcount; + struct pj_jbframe *frames; +} pj_jbframelist; + + +/** + * Jitter buffer implementation. + */ +typedef struct pj_jitter_buffer +{ + pj_jbframelist lst; /** The frame list. */ + int level; /** Current, real-time jitter level. */ + int max_level; /** Maximum level for the period. */ + unsigned prefetch; /** Prefetch count currently used. */ + unsigned get_cnt; /** Number of get operation during prefetch state. */ + unsigned min; /** Minimum jitter size, in packets. */ + unsigned max; /** Maximum jitter size, in packets. */ + pj_uint32_t lastseq; /** Last sequence number put to jitter buffer. */ + unsigned upd_count; /** Internal counter to manage update interval. */ + int state; /** Jitter buffer state (1==operational) */ + int last_op; /** Last jitter buffer operation. */ +} pj_jitter_buffer; + + +/** + * Initialize jitter buffer with the specified parameters. + * This function will allocate internal frame buffer from the specified pool. + * @param jb The jitter buffer to be initialized. + * @param pool Pool where memory will be allocated for the frame buffer. + * @param min The minimum value of jitter buffer, in packets. + * @param max The maximum value of jitter buffer, in packets. + * @param maxcount The maximum number of delay, in packets, which must be + * greater than max. + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool, + unsigned min, unsigned max, unsigned maxcount); + +/** + * Reset jitter buffer according to the parameters specified when the jitter + * buffer was initialized. Any packets currently in the buffer will be + * discarded. + */ +PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb); + +/** + * Put a pointer to the buffer with the specified sequence number. The pointer + * normally points to a buffer held by application, and this pointer will be + * returned later on when pj_jb_get() is called. The jitter buffer will not try + * to interpret the content of this pointer. + * @return: + * - PJ_SUCCESS on success. + * - PJ_JB_STATUS_TOO_OLD when the packet is too old. + * - PJ_JB_STATUS_TOO_SOON when the packet is too soon. + */ +PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf ); + +/** + * Get the earliest data from the jitter buffer. ONLY when the operation succeeds, + * the function returns both sequence number and a pointer in the parameters. + * This returned data corresponds to sequence number and pointer that were + * given to jitter buffer in the previous pj_jb_put operation. + * @return + * - PJ_SUCCESS on success + * - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned. + * - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet + * is lost. Application may run packet concealment algorithm when it + * receives this status. + */ +PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf ); + + + +PJ_END_DECL + +/** + * @} + */ + +#endif /* __PJMEDIA_JBUF_H__ */ diff --git a/pjmedia/src/pjmedia/mediamgr.c b/pjmedia/src/pjmedia/mediamgr.c index f09a2f390..dec2db71e 100644 --- a/pjmedia/src/pjmedia/mediamgr.c +++ b/pjmedia/src/pjmedia/mediamgr.c @@ -1,95 +1,95 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/mediamgr.c 8 6/24/05 1:00a Bennylp $ */ -#include -#include -#include -#include - -PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool); -PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory); - -/** Concrete declaration of media manager. */ -struct pj_med_mgr_t -{ - /** Pool. */ - pj_pool_t *pool; - - /** Pool factory. */ - pj_pool_factory *pf; - - /** Codec manager. */ - pj_codec_mgr codec_mgr; -}; - -/** - * Initialize and get the instance of media manager. - */ -PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf) -{ - pj_pool_t *pool; - pj_med_mgr_t *mm; - pj_codec_factory *cf; - pj_status_t status; - - pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL); - if (!pool) - return NULL; - - mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t)); - mm->pool = pool; - mm->pf = pf; - - /* Sound */ - pj_snd_init(pf); - - /* Init codec manager. */ - status = pj_codec_mgr_init(&mm->codec_mgr); - if (status != 0) { - pj_snd_deinit(); - goto on_error; - } - - /* Init and register G.711 codec. */ - cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory)); - - status = g711_init_factory (cf, mm->pool); - if (status != 0) { - pj_snd_deinit(); - return NULL; - } - - status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf); - if (status != 0) - return NULL; - - return mm; - -on_error: - pj_pool_release(pool); - return NULL; -} - -/** - * Get the codec manager instance. - */ -PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr) -{ - return &mgr->codec_mgr; -} - -/** - * Deinitialize media manager. - */ -PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr) -{ - pj_snd_deinit(); - pj_pool_release (mgr->pool); - return 0; -} - -/** - * Get pool factory. - */ -PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr) -{ - return mgr->pf; -} +/* $Header: /pjproject/pjmedia/src/pjmedia/mediamgr.c 8 6/24/05 1:00a Bennylp $ */ +#include +#include +#include +#include + +PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool); +PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory); + +/** Concrete declaration of media manager. */ +struct pj_med_mgr_t +{ + /** Pool. */ + pj_pool_t *pool; + + /** Pool factory. */ + pj_pool_factory *pf; + + /** Codec manager. */ + pj_codec_mgr codec_mgr; +}; + +/** + * Initialize and get the instance of media manager. + */ +PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf) +{ + pj_pool_t *pool; + pj_med_mgr_t *mm; + pj_codec_factory *cf; + pj_status_t status; + + pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL); + if (!pool) + return NULL; + + mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t)); + mm->pool = pool; + mm->pf = pf; + + /* Sound */ + pj_snd_init(pf); + + /* Init codec manager. */ + status = pj_codec_mgr_init(&mm->codec_mgr); + if (status != 0) { + pj_snd_deinit(); + goto on_error; + } + + /* Init and register G.711 codec. */ + cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory)); + + status = g711_init_factory (cf, mm->pool); + if (status != 0) { + pj_snd_deinit(); + return NULL; + } + + status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf); + if (status != 0) + return NULL; + + return mm; + +on_error: + pj_pool_release(pool); + return NULL; +} + +/** + * Get the codec manager instance. + */ +PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr) +{ + return &mgr->codec_mgr; +} + +/** + * Deinitialize media manager. + */ +PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr) +{ + pj_snd_deinit(); + pj_pool_release (mgr->pool); + return 0; +} + +/** + * Get pool factory. + */ +PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr) +{ + return mgr->pf; +} diff --git a/pjmedia/src/pjmedia/mediamgr.h b/pjmedia/src/pjmedia/mediamgr.h index f92708c25..d434227ea 100644 --- a/pjmedia/src/pjmedia/mediamgr.h +++ b/pjmedia/src/pjmedia/mediamgr.h @@ -1,84 +1,84 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/mediamgr.h 7 8/24/05 10:29a Bennylp $ */ - -#ifndef __PJMEDIA_MEDIAMGR_H__ -#define __PJMEDIA_MEDIAMGR_H__ - - -/** - * @file mediamgr.h - * @brief Media Manager. - */ -/** - * @defgroup PJMED_MGR Media Manager - * @ingroup PJMEDIA - * @{ - * - * The media manager acts as placeholder for endpoint capabilities. Each - * media manager will have a codec manager to manage list of codecs installed - * in the endpoint and a sound device factory. - * - * A reference to media manager instance is required when application wants - * to create a media session (#pj_media_session_create or - * #pj_media_session_create_from_sdp). - */ - -#include -#include - - -PJ_BEGIN_DECL - - -/** Opague declaration of media manager. */ -typedef struct pj_med_mgr_t pj_med_mgr_t; - -/** - * Create an instance of media manager. - * - * @param pf Pool factory. - * @param conn_addr Connection address to be used by this media manager. - * - * @return A new instance of media manager, or NULL if failed. - */ -PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf); - -/** - * Destroy media manager instance. - * - * @param mgr Media manager instance. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr); - -/** - * Get pool factory of the media manager as specified when the media - * manager was created. - * - * @param mgr The media manager instance. - * - * @return Pool factory instance of the media manager. - */ -PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr); - -/** - * Get the codec manager instance. - * - * @param mgr The media manager instance. - * - * @return The instance of codec manager. - */ -PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr); - - - -PJ_END_DECL - - -/** - * @} - */ - - - -#endif /* __PJMEDIA_MEDIAMGR_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/mediamgr.h 7 8/24/05 10:29a Bennylp $ */ + +#ifndef __PJMEDIA_MEDIAMGR_H__ +#define __PJMEDIA_MEDIAMGR_H__ + + +/** + * @file mediamgr.h + * @brief Media Manager. + */ +/** + * @defgroup PJMED_MGR Media Manager + * @ingroup PJMEDIA + * @{ + * + * The media manager acts as placeholder for endpoint capabilities. Each + * media manager will have a codec manager to manage list of codecs installed + * in the endpoint and a sound device factory. + * + * A reference to media manager instance is required when application wants + * to create a media session (#pj_media_session_create or + * #pj_media_session_create_from_sdp). + */ + +#include +#include + + +PJ_BEGIN_DECL + + +/** Opague declaration of media manager. */ +typedef struct pj_med_mgr_t pj_med_mgr_t; + +/** + * Create an instance of media manager. + * + * @param pf Pool factory. + * @param conn_addr Connection address to be used by this media manager. + * + * @return A new instance of media manager, or NULL if failed. + */ +PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf); + +/** + * Destroy media manager instance. + * + * @param mgr Media manager instance. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr); + +/** + * Get pool factory of the media manager as specified when the media + * manager was created. + * + * @param mgr The media manager instance. + * + * @return Pool factory instance of the media manager. + */ +PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr); + +/** + * Get the codec manager instance. + * + * @param mgr The media manager instance. + * + * @return The instance of codec manager. + */ +PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr); + + + +PJ_END_DECL + + +/** + * @} + */ + + + +#endif /* __PJMEDIA_MEDIAMGR_H__ */ diff --git a/pjmedia/src/pjmedia/nullsound.c b/pjmedia/src/pjmedia/nullsound.c index 7f1800044..8b5122e32 100644 --- a/pjmedia/src/pjmedia/nullsound.c +++ b/pjmedia/src/pjmedia/nullsound.c @@ -1,110 +1,110 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/nullsound.c 3 6/14/05 12:54a Bennylp $ */ -#include - -/* - * Null Factory Operations - */ -static pj_status_t null_sound_init(void); -static const char *null_sound_get_name(void); -static pj_status_t null_sound_destroy(void); -static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]); -static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev); -static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev); - - -/* - * Null Device Operations - */ -static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ); -static pj_status_t null_sound_dev_close( pj_snd_dev *dev ); -static pj_status_t null_sound_dev_play( pj_snd_dev *dev ); -static pj_status_t null_sound_dev_record( pj_snd_dev *dev ); - - -static pj_snd_dev_factory null_sound_factory = -{ - &null_sound_init, - &null_sound_get_name, - &null_sound_destroy, - &null_sound_enum_devices, - &null_sound_create_dev, - &null_sound_destroy_dev -}; - -static struct pj_snd_dev_op null_sound_dev_op = -{ - &null_sound_dev_open, - &null_sound_dev_close, - &null_sound_dev_play, - &null_sound_dev_record -}; - -PJ_DEF(pj_snd_dev_factory*) pj_nullsound_get_factory() -{ - return &null_sound_factory; -} - -static pj_status_t null_sound_init(void) -{ - return 0; -} - -static const char *null_sound_get_name(void) -{ - return "nullsound"; -} - -static pj_status_t null_sound_destroy(void) -{ - return 0; -} - -static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]) -{ - *count = 1; - dev_names[0] = "nullsound"; - return 0; -} - -static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev) -{ - PJ_UNUSED_ARG(dev_name); - dev->op = &null_sound_dev_op; - return 0; -} - -static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev) -{ - PJ_UNUSED_ARG(dev); - return 0; -} - - -/* - * Null Device Operations - */ -static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ) -{ - PJ_UNUSED_ARG(dev); - PJ_UNUSED_ARG(role); - return 0; -} - -static pj_status_t null_sound_dev_close( pj_snd_dev *dev ) -{ - PJ_UNUSED_ARG(dev); - return 0; -} - -static pj_status_t null_sound_dev_play( pj_snd_dev *dev ) -{ - PJ_UNUSED_ARG(dev); - return 0; -} - -static pj_status_t null_sound_dev_record( pj_snd_dev *dev ) -{ - PJ_UNUSED_ARG(dev); - return 0; -} - +/* $Header: /pjproject/pjmedia/src/pjmedia/nullsound.c 3 6/14/05 12:54a Bennylp $ */ +#include + +/* + * Null Factory Operations + */ +static pj_status_t null_sound_init(void); +static const char *null_sound_get_name(void); +static pj_status_t null_sound_destroy(void); +static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]); +static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev); +static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev); + + +/* + * Null Device Operations + */ +static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ); +static pj_status_t null_sound_dev_close( pj_snd_dev *dev ); +static pj_status_t null_sound_dev_play( pj_snd_dev *dev ); +static pj_status_t null_sound_dev_record( pj_snd_dev *dev ); + + +static pj_snd_dev_factory null_sound_factory = +{ + &null_sound_init, + &null_sound_get_name, + &null_sound_destroy, + &null_sound_enum_devices, + &null_sound_create_dev, + &null_sound_destroy_dev +}; + +static struct pj_snd_dev_op null_sound_dev_op = +{ + &null_sound_dev_open, + &null_sound_dev_close, + &null_sound_dev_play, + &null_sound_dev_record +}; + +PJ_DEF(pj_snd_dev_factory*) pj_nullsound_get_factory() +{ + return &null_sound_factory; +} + +static pj_status_t null_sound_init(void) +{ + return 0; +} + +static const char *null_sound_get_name(void) +{ + return "nullsound"; +} + +static pj_status_t null_sound_destroy(void) +{ + return 0; +} + +static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]) +{ + *count = 1; + dev_names[0] = "nullsound"; + return 0; +} + +static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev) +{ + PJ_UNUSED_ARG(dev_name); + dev->op = &null_sound_dev_op; + return 0; +} + +static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev) +{ + PJ_UNUSED_ARG(dev); + return 0; +} + + +/* + * Null Device Operations + */ +static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role ) +{ + PJ_UNUSED_ARG(dev); + PJ_UNUSED_ARG(role); + return 0; +} + +static pj_status_t null_sound_dev_close( pj_snd_dev *dev ) +{ + PJ_UNUSED_ARG(dev); + return 0; +} + +static pj_status_t null_sound_dev_play( pj_snd_dev *dev ) +{ + PJ_UNUSED_ARG(dev); + return 0; +} + +static pj_status_t null_sound_dev_record( pj_snd_dev *dev ) +{ + PJ_UNUSED_ARG(dev); + return 0; +} + diff --git a/pjmedia/src/pjmedia/pasound.c b/pjmedia/src/pjmedia/pasound.c index 8b2aa5ab0..acebaa0a0 100644 --- a/pjmedia/src/pjmedia/pasound.c +++ b/pjmedia/src/pjmedia/pasound.c @@ -1,387 +1,387 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/pasound.c 3 6/24/05 11:13p Bennylp $ */ -#include -#include -#include -#include -#include - -#define THIS_FILE "pasound.c" - -static struct snd_mgr -{ - pj_pool_factory *factory; -} snd_mgr; - -struct pj_snd_stream -{ - pj_pool_t *pool; - PaStream *stream; - int dev_index; - int bytes_per_sample; - pj_uint32_t samples_per_sec; - pj_uint32_t timestamp; - pj_uint32_t underflow; - pj_uint32_t overflow; - void *user_data; - pj_snd_rec_cb rec_cb; - pj_snd_play_cb play_cb; - pj_bool_t quit_flag; - pj_bool_t thread_has_exited; - pj_bool_t thread_initialized; - pj_thread_desc thread_desc; - pj_thread_t *thread; -}; - - -static int PaRecorderCallback(const void *input, - void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - pj_snd_stream *stream = userData; - pj_status_t status; - - PJ_UNUSED_ARG(output) - PJ_UNUSED_ARG(timeInfo) - - if (stream->quit_flag) - goto on_break; - - if (stream->thread_initialized == 0) { - stream->thread = pj_thread_register("pa_rec", stream->thread_desc); - stream->thread_initialized = 1; - } - - if (statusFlags & paInputUnderflow) - ++stream->underflow; - if (statusFlags & paInputOverflow) - ++stream->overflow; - - stream->timestamp += frameCount; - - status = (*stream->rec_cb)(stream->user_data, stream->timestamp, - input, frameCount * stream->bytes_per_sample); - - if (status==0) - return paContinue; - -on_break: - stream->thread_has_exited = 1; - return paAbort; -} - -static int PaPlayerCallback( const void *input, - void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) -{ - pj_snd_stream *stream = userData; - pj_status_t status; - unsigned size = frameCount * stream->bytes_per_sample; - - PJ_UNUSED_ARG(input) - PJ_UNUSED_ARG(timeInfo) - - if (stream->quit_flag) - goto on_break; - - if (stream->thread_initialized == 0) { - stream->thread = pj_thread_register("pa_rec", stream->thread_desc); - stream->thread_initialized = 1; - } - - if (statusFlags & paInputUnderflow) - ++stream->underflow; - if (statusFlags & paInputOverflow) - ++stream->overflow; - - stream->timestamp += frameCount; - - status = (*stream->play_cb)(stream->user_data, stream->timestamp, - output, size); - - if (status==0) - return paContinue; - -on_break: - stream->thread_has_exited = 1; - return paAbort; -} - - -/* - * Init sound library. - */ -PJ_DEF(pj_status_t) pj_snd_init(pj_pool_factory *factory) -{ - int err; - - snd_mgr.factory = factory; - err = Pa_Initialize(); - - PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err)); - - return err; -} - - -/* - * Get device count. - */ -PJ_DEF(int) pj_snd_get_dev_count(void) -{ - return Pa_GetDeviceCount(); -} - - -/* - * Get device info. - */ -PJ_DEF(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index) -{ - static pj_snd_dev_info info; - const PaDeviceInfo *pa_info; - - pa_info = Pa_GetDeviceInfo(index); - if (!pa_info) - return NULL; - - pj_memset(&info, 0, sizeof(info)); - strncpy(info.name, pa_info->name, sizeof(info.name)); - info.name[sizeof(info.name)-1] = '\0'; - info.input_count = pa_info->maxInputChannels; - info.output_count = pa_info->maxOutputChannels; - info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate; - - return &info; -} - - -/* - * Open stream. - */ -PJ_DEF(pj_snd_stream*) pj_snd_open_recorder( int index, - const pj_snd_stream_info *param, - pj_snd_rec_cb rec_cb, - void *user_data) -{ - pj_pool_t *pool; - pj_snd_stream *stream; - PaStreamParameters inputParam; - int sampleFormat; - const PaDeviceInfo *paDevInfo = NULL; - PaError err; - - if (index == -1) { - int count = Pa_GetDeviceCount(); - for (index=0; indexmaxInputChannels > 0) - break; - } - if (index == count) { - PJ_LOG(2,(THIS_FILE, "Error: unable to find recorder device")); - return NULL; - } - } else { - paDevInfo = Pa_GetDeviceInfo(index); - if (!paDevInfo) - return NULL; - } - - if (param->bits_per_sample == 8) - sampleFormat = paUInt8; - else if (param->bits_per_sample == 16) - sampleFormat = paInt16; - else if (param->bits_per_sample == 32) - sampleFormat = paInt32; - else - return NULL; - - pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL); - if (!pool) - return NULL; - - stream = pj_pool_calloc(pool, 1, sizeof(*stream)); - stream->pool = pool; - stream->user_data = user_data; - stream->dev_index = index; - stream->samples_per_sec = param->samples_per_frame; - stream->bytes_per_sample = param->bits_per_sample / 8; - stream->rec_cb = rec_cb; - - pj_memset(&inputParam, 0, sizeof(inputParam)); - inputParam.device = index; - inputParam.channelCount = 1; - inputParam.hostApiSpecificStreamInfo = NULL; - inputParam.sampleFormat = sampleFormat; - inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency; - - err = Pa_OpenStream( &stream->stream, &inputParam, NULL, - param->samples_per_sec, - param->samples_per_frame * param->frames_per_packet, - 0, - &PaRecorderCallback, stream ); - if (err != paNoError) { - pj_pool_release(pool); - PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err))); - return NULL; - } - - PJ_LOG(4,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, " - "%d bits per sample, %d samples per buffer", - (err==0 ? "Success" : "Error"), - paDevInfo->name, param->samples_per_sec, - param->bits_per_sample, - param->samples_per_frame * param->frames_per_packet)); - - return stream; -} - - -PJ_DEF(pj_snd_stream*) pj_snd_open_player(int index, - const pj_snd_stream_info *param, - pj_snd_play_cb play_cb, - void *user_data) -{ - pj_pool_t *pool; - pj_snd_stream *stream; - PaStreamParameters outputParam; - int sampleFormat; - const PaDeviceInfo *paDevInfo = NULL; - PaError err; - - if (index == -1) { - int count = Pa_GetDeviceCount(); - for (index=0; indexmaxOutputChannels > 0) - break; - } - if (index == count) { - PJ_LOG(2,(THIS_FILE, "Error: unable to find player device")); - return NULL; - } - } else { - paDevInfo = Pa_GetDeviceInfo(index); - if (!paDevInfo) - return NULL; - } - - if (param->bits_per_sample == 8) - sampleFormat = paUInt8; - else if (param->bits_per_sample == 16) - sampleFormat = paInt16; - else if (param->bits_per_sample == 32) - sampleFormat = paInt32; - else - return NULL; - - pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL); - if (!pool) - return NULL; - - stream = pj_pool_calloc(pool, 1, sizeof(*stream)); - stream->pool = pool; - stream->user_data = user_data; - stream->samples_per_sec = param->samples_per_frame; - stream->bytes_per_sample = param->bits_per_sample / 8; - stream->dev_index = index; - stream->play_cb = play_cb; - - pj_memset(&outputParam, 0, sizeof(outputParam)); - outputParam.device = index; - outputParam.channelCount = 1; - outputParam.hostApiSpecificStreamInfo = NULL; - outputParam.sampleFormat = sampleFormat; - outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency; - - err = Pa_OpenStream( &stream->stream, NULL, &outputParam, - param->samples_per_sec, - param->samples_per_frame * param->frames_per_packet, - 0, - &PaPlayerCallback, stream ); - if (err != paNoError) { - pj_pool_release(pool); - PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err))); - return NULL; - } - - PJ_LOG(4,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, " - "%d bits per sample, %d samples per buffer", - (err==0 ? "Success" : "Error"), - paDevInfo->name, param->samples_per_sec, - param->bits_per_sample, - param->samples_per_frame * param->frames_per_packet)); - - return stream; -} - - -/* - * Start stream. - */ -PJ_DEF(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream) -{ - PJ_LOG(4,(THIS_FILE, "Starting stream..")); - return Pa_StartStream(stream->stream); -} - -/* - * Stop stream. - */ -PJ_DEF(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream) -{ - int i, err; - - stream->quit_flag = 1; - for (i=0; !stream->thread_has_exited && i<100; ++i) - pj_thread_sleep(10); - - pj_thread_sleep(1); - - PJ_LOG(4,(THIS_FILE, "Stopping stream..")); - - err = Pa_StopStream(stream->stream); - return err; -} - -/* - * Destroy stream. - */ -PJ_DEF(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream) -{ - int i, err; - const PaDeviceInfo *paDevInfo; - - stream->quit_flag = 1; - for (i=0; !stream->thread_has_exited && i<100; ++i) - pj_thread_sleep(10); - - pj_thread_sleep(1); - - paDevInfo = Pa_GetDeviceInfo(stream->dev_index); - - PJ_LOG(4,(THIS_FILE, "Closing %s: %lu underflow, %lu overflow", - paDevInfo->name, - stream->underflow, stream->overflow)); - - err = Pa_CloseStream(stream->stream); - pj_pool_release(stream->pool); - return err; -} - -/* - * Deinitialize sound library. - */ -PJ_DEF(pj_status_t) pj_snd_deinit(void) -{ - PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down..")); - - return Pa_Terminate(); -} - +/* $Header: /pjproject/pjmedia/src/pjmedia/pasound.c 3 6/24/05 11:13p Bennylp $ */ +#include +#include +#include +#include +#include + +#define THIS_FILE "pasound.c" + +static struct snd_mgr +{ + pj_pool_factory *factory; +} snd_mgr; + +struct pj_snd_stream +{ + pj_pool_t *pool; + PaStream *stream; + int dev_index; + int bytes_per_sample; + pj_uint32_t samples_per_sec; + pj_uint32_t timestamp; + pj_uint32_t underflow; + pj_uint32_t overflow; + void *user_data; + pj_snd_rec_cb rec_cb; + pj_snd_play_cb play_cb; + pj_bool_t quit_flag; + pj_bool_t thread_has_exited; + pj_bool_t thread_initialized; + pj_thread_desc thread_desc; + pj_thread_t *thread; +}; + + +static int PaRecorderCallback(const void *input, + void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + pj_snd_stream *stream = userData; + pj_status_t status; + + PJ_UNUSED_ARG(output) + PJ_UNUSED_ARG(timeInfo) + + if (stream->quit_flag) + goto on_break; + + if (stream->thread_initialized == 0) { + stream->thread = pj_thread_register("pa_rec", stream->thread_desc); + stream->thread_initialized = 1; + } + + if (statusFlags & paInputUnderflow) + ++stream->underflow; + if (statusFlags & paInputOverflow) + ++stream->overflow; + + stream->timestamp += frameCount; + + status = (*stream->rec_cb)(stream->user_data, stream->timestamp, + input, frameCount * stream->bytes_per_sample); + + if (status==0) + return paContinue; + +on_break: + stream->thread_has_exited = 1; + return paAbort; +} + +static int PaPlayerCallback( const void *input, + void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + pj_snd_stream *stream = userData; + pj_status_t status; + unsigned size = frameCount * stream->bytes_per_sample; + + PJ_UNUSED_ARG(input) + PJ_UNUSED_ARG(timeInfo) + + if (stream->quit_flag) + goto on_break; + + if (stream->thread_initialized == 0) { + stream->thread = pj_thread_register("pa_rec", stream->thread_desc); + stream->thread_initialized = 1; + } + + if (statusFlags & paInputUnderflow) + ++stream->underflow; + if (statusFlags & paInputOverflow) + ++stream->overflow; + + stream->timestamp += frameCount; + + status = (*stream->play_cb)(stream->user_data, stream->timestamp, + output, size); + + if (status==0) + return paContinue; + +on_break: + stream->thread_has_exited = 1; + return paAbort; +} + + +/* + * Init sound library. + */ +PJ_DEF(pj_status_t) pj_snd_init(pj_pool_factory *factory) +{ + int err; + + snd_mgr.factory = factory; + err = Pa_Initialize(); + + PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err)); + + return err; +} + + +/* + * Get device count. + */ +PJ_DEF(int) pj_snd_get_dev_count(void) +{ + return Pa_GetDeviceCount(); +} + + +/* + * Get device info. + */ +PJ_DEF(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index) +{ + static pj_snd_dev_info info; + const PaDeviceInfo *pa_info; + + pa_info = Pa_GetDeviceInfo(index); + if (!pa_info) + return NULL; + + pj_memset(&info, 0, sizeof(info)); + strncpy(info.name, pa_info->name, sizeof(info.name)); + info.name[sizeof(info.name)-1] = '\0'; + info.input_count = pa_info->maxInputChannels; + info.output_count = pa_info->maxOutputChannels; + info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate; + + return &info; +} + + +/* + * Open stream. + */ +PJ_DEF(pj_snd_stream*) pj_snd_open_recorder( int index, + const pj_snd_stream_info *param, + pj_snd_rec_cb rec_cb, + void *user_data) +{ + pj_pool_t *pool; + pj_snd_stream *stream; + PaStreamParameters inputParam; + int sampleFormat; + const PaDeviceInfo *paDevInfo = NULL; + PaError err; + + if (index == -1) { + int count = Pa_GetDeviceCount(); + for (index=0; indexmaxInputChannels > 0) + break; + } + if (index == count) { + PJ_LOG(2,(THIS_FILE, "Error: unable to find recorder device")); + return NULL; + } + } else { + paDevInfo = Pa_GetDeviceInfo(index); + if (!paDevInfo) + return NULL; + } + + if (param->bits_per_sample == 8) + sampleFormat = paUInt8; + else if (param->bits_per_sample == 16) + sampleFormat = paInt16; + else if (param->bits_per_sample == 32) + sampleFormat = paInt32; + else + return NULL; + + pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL); + if (!pool) + return NULL; + + stream = pj_pool_calloc(pool, 1, sizeof(*stream)); + stream->pool = pool; + stream->user_data = user_data; + stream->dev_index = index; + stream->samples_per_sec = param->samples_per_frame; + stream->bytes_per_sample = param->bits_per_sample / 8; + stream->rec_cb = rec_cb; + + pj_memset(&inputParam, 0, sizeof(inputParam)); + inputParam.device = index; + inputParam.channelCount = 1; + inputParam.hostApiSpecificStreamInfo = NULL; + inputParam.sampleFormat = sampleFormat; + inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency; + + err = Pa_OpenStream( &stream->stream, &inputParam, NULL, + param->samples_per_sec, + param->samples_per_frame * param->frames_per_packet, + 0, + &PaRecorderCallback, stream ); + if (err != paNoError) { + pj_pool_release(pool); + PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err))); + return NULL; + } + + PJ_LOG(4,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, " + "%d bits per sample, %d samples per buffer", + (err==0 ? "Success" : "Error"), + paDevInfo->name, param->samples_per_sec, + param->bits_per_sample, + param->samples_per_frame * param->frames_per_packet)); + + return stream; +} + + +PJ_DEF(pj_snd_stream*) pj_snd_open_player(int index, + const pj_snd_stream_info *param, + pj_snd_play_cb play_cb, + void *user_data) +{ + pj_pool_t *pool; + pj_snd_stream *stream; + PaStreamParameters outputParam; + int sampleFormat; + const PaDeviceInfo *paDevInfo = NULL; + PaError err; + + if (index == -1) { + int count = Pa_GetDeviceCount(); + for (index=0; indexmaxOutputChannels > 0) + break; + } + if (index == count) { + PJ_LOG(2,(THIS_FILE, "Error: unable to find player device")); + return NULL; + } + } else { + paDevInfo = Pa_GetDeviceInfo(index); + if (!paDevInfo) + return NULL; + } + + if (param->bits_per_sample == 8) + sampleFormat = paUInt8; + else if (param->bits_per_sample == 16) + sampleFormat = paInt16; + else if (param->bits_per_sample == 32) + sampleFormat = paInt32; + else + return NULL; + + pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL); + if (!pool) + return NULL; + + stream = pj_pool_calloc(pool, 1, sizeof(*stream)); + stream->pool = pool; + stream->user_data = user_data; + stream->samples_per_sec = param->samples_per_frame; + stream->bytes_per_sample = param->bits_per_sample / 8; + stream->dev_index = index; + stream->play_cb = play_cb; + + pj_memset(&outputParam, 0, sizeof(outputParam)); + outputParam.device = index; + outputParam.channelCount = 1; + outputParam.hostApiSpecificStreamInfo = NULL; + outputParam.sampleFormat = sampleFormat; + outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency; + + err = Pa_OpenStream( &stream->stream, NULL, &outputParam, + param->samples_per_sec, + param->samples_per_frame * param->frames_per_packet, + 0, + &PaPlayerCallback, stream ); + if (err != paNoError) { + pj_pool_release(pool); + PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err))); + return NULL; + } + + PJ_LOG(4,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, " + "%d bits per sample, %d samples per buffer", + (err==0 ? "Success" : "Error"), + paDevInfo->name, param->samples_per_sec, + param->bits_per_sample, + param->samples_per_frame * param->frames_per_packet)); + + return stream; +} + + +/* + * Start stream. + */ +PJ_DEF(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream) +{ + PJ_LOG(4,(THIS_FILE, "Starting stream..")); + return Pa_StartStream(stream->stream); +} + +/* + * Stop stream. + */ +PJ_DEF(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream) +{ + int i, err; + + stream->quit_flag = 1; + for (i=0; !stream->thread_has_exited && i<100; ++i) + pj_thread_sleep(10); + + pj_thread_sleep(1); + + PJ_LOG(4,(THIS_FILE, "Stopping stream..")); + + err = Pa_StopStream(stream->stream); + return err; +} + +/* + * Destroy stream. + */ +PJ_DEF(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream) +{ + int i, err; + const PaDeviceInfo *paDevInfo; + + stream->quit_flag = 1; + for (i=0; !stream->thread_has_exited && i<100; ++i) + pj_thread_sleep(10); + + pj_thread_sleep(1); + + paDevInfo = Pa_GetDeviceInfo(stream->dev_index); + + PJ_LOG(4,(THIS_FILE, "Closing %s: %lu underflow, %lu overflow", + paDevInfo->name, + stream->underflow, stream->overflow)); + + err = Pa_CloseStream(stream->stream); + pj_pool_release(stream->pool); + return err; +} + +/* + * Deinitialize sound library. + */ +PJ_DEF(pj_status_t) pj_snd_deinit(void) +{ + PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down..")); + + return Pa_Terminate(); +} + diff --git a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c index 767145b71..207d28734 100644 --- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c +++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c @@ -1,616 +1,616 @@ -/* - * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina Exp $ - * Simplified DirectSound interface. - * - * Author: Phil Burk & Robert Marsanyi - * - * PortAudio Portable Real-Time Audio Library - * For more information see: http://www.softsynth.com/portaudio/ - * DirectSound Implementation - * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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 "dsound_wrapper.h" -#include "pa_trace.h" - -/* - Rather than linking with dxguid.a or using "#define INITGUID" to force a - header file to instantiate the required GUID(s), we define them directly - below. -*/ -#include // needed for the DEFINE_GUID macro -DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); - - -/************************************************************************************/ -DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 }; -/************************************************************************************/ -static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter) -{ - (void)lpcGuidDevice; /* unused parameter */ - (void)ppDS; /* unused parameter */ - (void)pUnkOuter; /* unused parameter */ - return E_NOTIMPL; -} - -static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext) -{ - (void)lpDSEnumCallback; /* unused parameter */ - (void)lpContext; /* unused parameter */ - return E_NOTIMPL; -} - -static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext) -{ - (void)lpDSEnumCallback; /* unused parameter */ - (void)lpContext; /* unused parameter */ - return E_NOTIMPL; -} - -static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter) -{ - (void)lpcGUID; /* unused parameter */ - (void)lplpDSC; /* unused parameter */ - (void)pUnkOuter; /* unused parameter */ - return E_NOTIMPL; -} - -static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext) -{ - (void)lpDSCEnumCallback; /* unused parameter */ - (void)lpContext; /* unused parameter */ - return E_NOTIMPL; -} - -static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext) -{ - (void)lpDSCEnumCallback; /* unused parameter */ - (void)lpContext; /* unused parameter */ - return E_NOTIMPL; -} -/************************************************************************************/ -void DSW_InitializeDSoundEntryPoints(void) -{ - dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll"); - if( dswDSoundEntryPoints.hInstance_ != NULL ) - { - dswDSoundEntryPoints.DirectSoundCreate = - (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN)) - GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" ); - if( dswDSoundEntryPoints.DirectSoundCreate == NULL ) - dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; - - dswDSoundEntryPoints.DirectSoundEnumerateW = - (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) - GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" ); - if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL ) - dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; - - dswDSoundEntryPoints.DirectSoundEnumerateA = - (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) - GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" ); - if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL ) - dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; - - dswDSoundEntryPoints.DirectSoundCaptureCreate = - (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN)) - GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" ); - if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL ) - dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; - - dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = - (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) - GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" ); - if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL ) - dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; - - dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = - (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) - GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" ); - if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL ) - dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; - } - else - { - /* initialize with dummy entry points to make live easy when ds isn't present */ - dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; - dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; - dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; - dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; - dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; - dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; - } -} -/************************************************************************************/ -void DSW_TerminateDSoundEntryPoints(void) -{ - if( dswDSoundEntryPoints.hInstance_ != NULL ) - { - FreeLibrary( dswDSoundEntryPoints.hInstance_ ); - dswDSoundEntryPoints.hInstance_ = NULL; - /* ensure that we crash reliably if the entry points arent initialised */ - dswDSoundEntryPoints.DirectSoundCreate = 0; - dswDSoundEntryPoints.DirectSoundEnumerateW = 0; - dswDSoundEntryPoints.DirectSoundEnumerateA = 0; - dswDSoundEntryPoints.DirectSoundCaptureCreate = 0; - dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0; - dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0; - } -} -/************************************************************************************/ -void DSW_Term( DSoundWrapper *dsw ) -{ - // Cleanup the sound buffers - if (dsw->dsw_OutputBuffer) - { - IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); - IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer ); - dsw->dsw_OutputBuffer = NULL; - } - - if (dsw->dsw_InputBuffer) - { - IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); - IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer ); - dsw->dsw_InputBuffer = NULL; - } - - if (dsw->dsw_pDirectSoundCapture) - { - IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture ); - dsw->dsw_pDirectSoundCapture = NULL; - } - - if (dsw->dsw_pDirectSound) - { - IDirectSound_Release( dsw->dsw_pDirectSound ); - dsw->dsw_pDirectSound = NULL; - } -} -/************************************************************************************/ -HRESULT DSW_Init( DSoundWrapper *dsw ) -{ - memset( dsw, 0, sizeof(DSoundWrapper) ); - return 0; -} -/************************************************************************************/ -HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) -{ - // Create the DS object - HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL ); - if( hr != DS_OK ) return hr; - return hr; -} - -/************************************************************************************/ -HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer ) -{ - DWORD dwDataLen; - DWORD playCursor; - HRESULT result; - LPDIRECTSOUNDBUFFER pPrimaryBuffer; - HWND hWnd; - HRESULT hr; - WAVEFORMATEX wfFormat; - DSBUFFERDESC primaryDesc; - DSBUFFERDESC secondaryDesc; - unsigned char* pDSBuffData; - LARGE_INTEGER counterFrequency; - - dsw->dsw_OutputSize = bytesPerBuffer; - dsw->dsw_OutputRunning = FALSE; - dsw->dsw_OutputUnderflows = 0; - dsw->dsw_FramesWritten = 0; - dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short); - - // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the - // applications's window. Also if that window is closed before the Buffer is closed - // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.) - // So we will use GetDesktopWindow() which was suggested by Miller Puckette. - // hWnd = GetForegroundWindow(); - // - // FIXME: The example code I have on the net creates a hidden window that - // is managed by our code - I think we should do that - one hidden - // window for the whole of Pa_DS - // - hWnd = GetDesktopWindow(); - - // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz. - // Exclusize also prevents unexpected sounds from other apps during a performance. - if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound, - hWnd, DSSCL_EXCLUSIVE)) != DS_OK) - { - return hr; - } - - // ----------------------------------------------------------------------- - // Create primary buffer and set format just so we can specify our custom format. - // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz. - // Setup the primary buffer description - ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC)); - primaryDesc.dwSize = sizeof(DSBUFFERDESC); - primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth - primaryDesc.dwBufferBytes = 0; - primaryDesc.lpwfxFormat = NULL; - // Create the buffer - if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, - &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result; - // Define the buffer format - wfFormat.wFormatTag = WAVE_FORMAT_PCM; - wfFormat.nChannels = nChannels; - wfFormat.nSamplesPerSec = nFrameRate; - wfFormat.wBitsPerSample = 8 * sizeof(short); - wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); - wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; - wfFormat.cbSize = 0; /* No extended format info. */ - // Set the primary buffer's format - if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result; - - // ---------------------------------------------------------------------- - // Setup the secondary buffer description - ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC)); - secondaryDesc.dwSize = sizeof(DSBUFFERDESC); - secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; - secondaryDesc.dwBufferBytes = bytesPerBuffer; - secondaryDesc.lpwfxFormat = &wfFormat; - // Create the secondary buffer - if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, - &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result; - // Lock the DS buffer - if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData, - &dwDataLen, NULL, 0, 0)) != DS_OK) return result; - // Zero the DS buffer - ZeroMemory(pDSBuffData, dwDataLen); - // Unlock the DS buffer - if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result; - if( QueryPerformanceFrequency( &counterFrequency ) ) - { - int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short)); - dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate; - } - else - { - dsw->dsw_CounterTicksPerBuffer.QuadPart = 0; - } - // Let DSound set the starting write position because if we set it to zero, it looks like the - // buffer is full to begin with. This causes a long pause before sound starts when using large buffers. - hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset ); - if( hr != DS_OK ) - { - return hr; - } - dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame; - /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */ - return DS_OK; -} - -/************************************************************************************/ -HRESULT DSW_StartOutput( DSoundWrapper *dsw ) -{ - HRESULT hr; - QueryPerformanceCounter( &dsw->dsw_LastPlayTime ); - dsw->dsw_LastPlayCursor = 0; - dsw->dsw_FramesPlayed = 0; - hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 ); - if( hr != DS_OK ) - { - return hr; - } - // Start the buffer playback in a loop. - if( dsw->dsw_OutputBuffer != NULL ) - { - hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING ); - if( hr != DS_OK ) - { - return hr; - } - dsw->dsw_OutputRunning = TRUE; - } - - return 0; -} -/************************************************************************************/ -HRESULT DSW_StopOutput( DSoundWrapper *dsw ) -{ - // Stop the buffer playback - if( dsw->dsw_OutputBuffer != NULL ) - { - dsw->dsw_OutputRunning = FALSE; - return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); - } - else return 0; -} - -/************************************************************************************/ -HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr ) -{ - HRESULT hr; - DWORD playCursor; - DWORD writeCursor; - long bytesFilled; - // Query to see where play position is. - // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly - // so let's pass a pointer just to be safe. - hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); - if( hr != DS_OK ) - { - return hr; - } - bytesFilled = dsw->dsw_WriteOffset - playCursor; - if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset - *bytesFilledPtr = bytesFilled; - return hr; -} - -/************************************************************************************ - * Determine how much space can be safely written to in DS buffer. - * Detect underflows and overflows. - * Does not allow writing into safety gap maintained by DirectSound. - */ -HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ) -{ - HRESULT hr; - DWORD playCursor; - DWORD writeCursor; - long numBytesEmpty; - long playWriteGap; - // Query to see how much room is in buffer. - hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); - if( hr != DS_OK ) - { - return hr; - } - // Determine size of gap between playIndex and WriteIndex that we cannot write into. - playWriteGap = writeCursor - playCursor; - if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap - /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */ - /* Attempt to detect playCursor wrap-around and correct it. */ - if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) ) - { - /* How much time has elapsed since last check. */ - LARGE_INTEGER currentTime; - LARGE_INTEGER elapsedTime; - long bytesPlayed; - long bytesExpected; - long buffersWrapped; - QueryPerformanceCounter( ¤tTime ); - elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart; - dsw->dsw_LastPlayTime = currentTime; - /* How many bytes does DirectSound say have been played. */ - bytesPlayed = playCursor - dsw->dsw_LastPlayCursor; - if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap - dsw->dsw_LastPlayCursor = playCursor; - /* Calculate how many bytes we would have expected to been played by now. */ - bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart); - buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize; - if( buffersWrapped > 0 ) - { - playCursor += (buffersWrapped * dsw->dsw_OutputSize); - bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize); - } - /* Maintain frame output cursor. */ - dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame); - } - numBytesEmpty = playCursor - dsw->dsw_WriteOffset; - if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset - /* Have we underflowed? */ - if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) ) - { - if( dsw->dsw_OutputRunning ) - { - dsw->dsw_OutputUnderflows += 1; - } - dsw->dsw_WriteOffset = writeCursor; - numBytesEmpty = dsw->dsw_OutputSize - playWriteGap; - } - *bytesEmpty = numBytesEmpty; - return hr; -} - -/************************************************************************************/ -HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ) -{ - HRESULT hr; - LPBYTE lpbuf1 = NULL; - LPBYTE lpbuf2 = NULL; - DWORD dwsize1 = 0; - DWORD dwsize2 = 0; - long bytesEmpty; - hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed - if (hr != DS_OK) return hr; - if( bytesEmpty == 0 ) return DS_OK; - // Lock free space in the DS - hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1, - (void **) &lpbuf2, &dwsize2, 0); - if (hr == DS_OK) - { - // Copy the buffer into the DS - ZeroMemory(lpbuf1, dwsize1); - if(lpbuf2 != NULL) - { - ZeroMemory(lpbuf2, dwsize2); - } - // Update our buffer offset and unlock sound buffer - dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; - IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); - dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame; - } - return hr; -} - -/************************************************************************************/ -HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ) -{ - HRESULT hr; - LPBYTE lpbuf1 = NULL; - LPBYTE lpbuf2 = NULL; - DWORD dwsize1 = 0; - DWORD dwsize2 = 0; - // Lock free space in the DS - hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1, - (void **) &lpbuf2, &dwsize2, 0); - if (hr == DS_OK) - { - // Copy the buffer into the DS - CopyMemory(lpbuf1, buf, dwsize1); - if(lpbuf2 != NULL) - { - CopyMemory(lpbuf2, buf+dwsize1, dwsize2); - } - // Update our buffer offset and unlock sound buffer - dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; - IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); - dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame; - } - return hr; -} - -/************************************************************************************/ -DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ) -{ - DWORD status; - if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK) - return( DSERR_INVALIDPARAM ); - else - return( status ); -} - -/* These routines are used to support audio input. - * Do NOT compile these calls when using NT4 because it does - * not support the entry points. - */ -/************************************************************************************/ -HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) -{ - HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); - if( hr != DS_OK ) return hr; - return hr; -} -/************************************************************************************/ -HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer ) -{ - DSCBUFFERDESC captureDesc; - WAVEFORMATEX wfFormat; - HRESULT result; - - dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short); - - // Define the buffer format - wfFormat.wFormatTag = WAVE_FORMAT_PCM; - wfFormat.nChannels = nChannels; - wfFormat.nSamplesPerSec = nFrameRate; - wfFormat.wBitsPerSample = 8 * sizeof(short); - wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); - wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; - wfFormat.cbSize = 0; /* No extended format info. */ - dsw->dsw_InputSize = bytesPerBuffer; - // ---------------------------------------------------------------------- - // Setup the secondary buffer description - ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC)); - captureDesc.dwSize = sizeof(DSCBUFFERDESC); - captureDesc.dwFlags = 0; - captureDesc.dwBufferBytes = bytesPerBuffer; - captureDesc.lpwfxFormat = &wfFormat; - // Create the capture buffer - if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture, - &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result; - dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer - return DS_OK; -} - -/************************************************************************************/ -HRESULT DSW_StartInput( DSoundWrapper *dsw ) -{ - // Start the buffer playback - if( dsw->dsw_InputBuffer != NULL ) - { - return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING ); - } - else return 0; -} - -/************************************************************************************/ -HRESULT DSW_StopInput( DSoundWrapper *dsw ) -{ - // Stop the buffer playback - if( dsw->dsw_InputBuffer != NULL ) - { - return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); - } - else return 0; -} - -/************************************************************************************/ -HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ) -{ - HRESULT hr; - DWORD capturePos; - DWORD readPos; - long filled; - // Query to see how much data is in buffer. - // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly - // so let's pass a pointer just to be safe. - hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos ); - if( hr != DS_OK ) - { - return hr; - } - filled = readPos - dsw->dsw_ReadOffset; - if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset - *bytesFilled = filled; - return hr; -} - -/************************************************************************************/ -HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ) -{ - HRESULT hr; - LPBYTE lpbuf1 = NULL; - LPBYTE lpbuf2 = NULL; - DWORD dwsize1 = 0; - DWORD dwsize2 = 0; - // Lock free space in the DS - hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1, - (void **) &lpbuf2, &dwsize2, 0); - if (hr == DS_OK) - { - // Copy from DS to the buffer - CopyMemory( buf, lpbuf1, dwsize1); - if(lpbuf2 != NULL) - { - CopyMemory( buf+dwsize1, lpbuf2, dwsize2); - } - // Update our buffer offset and unlock sound buffer - dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize; - IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); - } - return hr; -} - +/* + * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina Exp $ + * Simplified DirectSound interface. + * + * Author: Phil Burk & Robert Marsanyi + * + * PortAudio Portable Real-Time Audio Library + * For more information see: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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 "dsound_wrapper.h" +#include "pa_trace.h" + +/* + Rather than linking with dxguid.a or using "#define INITGUID" to force a + header file to instantiate the required GUID(s), we define them directly + below. +*/ +#include // needed for the DEFINE_GUID macro +DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + + +/************************************************************************************/ +DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 }; +/************************************************************************************/ +static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter) +{ + (void)lpcGuidDevice; /* unused parameter */ + (void)ppDS; /* unused parameter */ + (void)pUnkOuter; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext) +{ + (void)lpDSEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext) +{ + (void)lpDSEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter) +{ + (void)lpcGUID; /* unused parameter */ + (void)lplpDSC; /* unused parameter */ + (void)pUnkOuter; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext) +{ + (void)lpDSCEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext) +{ + (void)lpDSCEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} +/************************************************************************************/ +void DSW_InitializeDSoundEntryPoints(void) +{ + dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll"); + if( dswDSoundEntryPoints.hInstance_ != NULL ) + { + dswDSoundEntryPoints.DirectSoundCreate = + (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" ); + if( dswDSoundEntryPoints.DirectSoundCreate == NULL ) + dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; + + dswDSoundEntryPoints.DirectSoundEnumerateW = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" ); + if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL ) + dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; + + dswDSoundEntryPoints.DirectSoundEnumerateA = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" ); + if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL ) + dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; + + dswDSoundEntryPoints.DirectSoundCaptureCreate = + (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" ); + if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL ) + dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; + + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" ); + if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL ) + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; + + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" ); + if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL ) + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; + } + else + { + /* initialize with dummy entry points to make live easy when ds isn't present */ + dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; + dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; + dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; + dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; + } +} +/************************************************************************************/ +void DSW_TerminateDSoundEntryPoints(void) +{ + if( dswDSoundEntryPoints.hInstance_ != NULL ) + { + FreeLibrary( dswDSoundEntryPoints.hInstance_ ); + dswDSoundEntryPoints.hInstance_ = NULL; + /* ensure that we crash reliably if the entry points arent initialised */ + dswDSoundEntryPoints.DirectSoundCreate = 0; + dswDSoundEntryPoints.DirectSoundEnumerateW = 0; + dswDSoundEntryPoints.DirectSoundEnumerateA = 0; + dswDSoundEntryPoints.DirectSoundCaptureCreate = 0; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0; + } +} +/************************************************************************************/ +void DSW_Term( DSoundWrapper *dsw ) +{ + // Cleanup the sound buffers + if (dsw->dsw_OutputBuffer) + { + IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); + IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer ); + dsw->dsw_OutputBuffer = NULL; + } + + if (dsw->dsw_InputBuffer) + { + IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); + IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer ); + dsw->dsw_InputBuffer = NULL; + } + + if (dsw->dsw_pDirectSoundCapture) + { + IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture ); + dsw->dsw_pDirectSoundCapture = NULL; + } + + if (dsw->dsw_pDirectSound) + { + IDirectSound_Release( dsw->dsw_pDirectSound ); + dsw->dsw_pDirectSound = NULL; + } +} +/************************************************************************************/ +HRESULT DSW_Init( DSoundWrapper *dsw ) +{ + memset( dsw, 0, sizeof(DSoundWrapper) ); + return 0; +} +/************************************************************************************/ +HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) +{ + // Create the DS object + HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL ); + if( hr != DS_OK ) return hr; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer ) +{ + DWORD dwDataLen; + DWORD playCursor; + HRESULT result; + LPDIRECTSOUNDBUFFER pPrimaryBuffer; + HWND hWnd; + HRESULT hr; + WAVEFORMATEX wfFormat; + DSBUFFERDESC primaryDesc; + DSBUFFERDESC secondaryDesc; + unsigned char* pDSBuffData; + LARGE_INTEGER counterFrequency; + + dsw->dsw_OutputSize = bytesPerBuffer; + dsw->dsw_OutputRunning = FALSE; + dsw->dsw_OutputUnderflows = 0; + dsw->dsw_FramesWritten = 0; + dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short); + + // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the + // applications's window. Also if that window is closed before the Buffer is closed + // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.) + // So we will use GetDesktopWindow() which was suggested by Miller Puckette. + // hWnd = GetForegroundWindow(); + // + // FIXME: The example code I have on the net creates a hidden window that + // is managed by our code - I think we should do that - one hidden + // window for the whole of Pa_DS + // + hWnd = GetDesktopWindow(); + + // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz. + // Exclusize also prevents unexpected sounds from other apps during a performance. + if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound, + hWnd, DSSCL_EXCLUSIVE)) != DS_OK) + { + return hr; + } + + // ----------------------------------------------------------------------- + // Create primary buffer and set format just so we can specify our custom format. + // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz. + // Setup the primary buffer description + ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC)); + primaryDesc.dwSize = sizeof(DSBUFFERDESC); + primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth + primaryDesc.dwBufferBytes = 0; + primaryDesc.lpwfxFormat = NULL; + // Create the buffer + if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, + &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result; + // Define the buffer format + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = nChannels; + wfFormat.nSamplesPerSec = nFrameRate; + wfFormat.wBitsPerSample = 8 * sizeof(short); + wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; /* No extended format info. */ + // Set the primary buffer's format + if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result; + + // ---------------------------------------------------------------------- + // Setup the secondary buffer description + ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC)); + secondaryDesc.dwSize = sizeof(DSBUFFERDESC); + secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; + secondaryDesc.dwBufferBytes = bytesPerBuffer; + secondaryDesc.lpwfxFormat = &wfFormat; + // Create the secondary buffer + if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, + &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result; + // Lock the DS buffer + if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData, + &dwDataLen, NULL, 0, 0)) != DS_OK) return result; + // Zero the DS buffer + ZeroMemory(pDSBuffData, dwDataLen); + // Unlock the DS buffer + if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result; + if( QueryPerformanceFrequency( &counterFrequency ) ) + { + int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short)); + dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate; + } + else + { + dsw->dsw_CounterTicksPerBuffer.QuadPart = 0; + } + // Let DSound set the starting write position because if we set it to zero, it looks like the + // buffer is full to begin with. This causes a long pause before sound starts when using large buffers. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset ); + if( hr != DS_OK ) + { + return hr; + } + dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame; + /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */ + return DS_OK; +} + +/************************************************************************************/ +HRESULT DSW_StartOutput( DSoundWrapper *dsw ) +{ + HRESULT hr; + QueryPerformanceCounter( &dsw->dsw_LastPlayTime ); + dsw->dsw_LastPlayCursor = 0; + dsw->dsw_FramesPlayed = 0; + hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 ); + if( hr != DS_OK ) + { + return hr; + } + // Start the buffer playback in a loop. + if( dsw->dsw_OutputBuffer != NULL ) + { + hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING ); + if( hr != DS_OK ) + { + return hr; + } + dsw->dsw_OutputRunning = TRUE; + } + + return 0; +} +/************************************************************************************/ +HRESULT DSW_StopOutput( DSoundWrapper *dsw ) +{ + // Stop the buffer playback + if( dsw->dsw_OutputBuffer != NULL ) + { + dsw->dsw_OutputRunning = FALSE; + return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr ) +{ + HRESULT hr; + DWORD playCursor; + DWORD writeCursor; + long bytesFilled; + // Query to see where play position is. + // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly + // so let's pass a pointer just to be safe. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); + if( hr != DS_OK ) + { + return hr; + } + bytesFilled = dsw->dsw_WriteOffset - playCursor; + if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset + *bytesFilledPtr = bytesFilled; + return hr; +} + +/************************************************************************************ + * Determine how much space can be safely written to in DS buffer. + * Detect underflows and overflows. + * Does not allow writing into safety gap maintained by DirectSound. + */ +HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ) +{ + HRESULT hr; + DWORD playCursor; + DWORD writeCursor; + long numBytesEmpty; + long playWriteGap; + // Query to see how much room is in buffer. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); + if( hr != DS_OK ) + { + return hr; + } + // Determine size of gap between playIndex and WriteIndex that we cannot write into. + playWriteGap = writeCursor - playCursor; + if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap + /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */ + /* Attempt to detect playCursor wrap-around and correct it. */ + if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) ) + { + /* How much time has elapsed since last check. */ + LARGE_INTEGER currentTime; + LARGE_INTEGER elapsedTime; + long bytesPlayed; + long bytesExpected; + long buffersWrapped; + QueryPerformanceCounter( ¤tTime ); + elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart; + dsw->dsw_LastPlayTime = currentTime; + /* How many bytes does DirectSound say have been played. */ + bytesPlayed = playCursor - dsw->dsw_LastPlayCursor; + if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap + dsw->dsw_LastPlayCursor = playCursor; + /* Calculate how many bytes we would have expected to been played by now. */ + bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart); + buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize; + if( buffersWrapped > 0 ) + { + playCursor += (buffersWrapped * dsw->dsw_OutputSize); + bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize); + } + /* Maintain frame output cursor. */ + dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame); + } + numBytesEmpty = playCursor - dsw->dsw_WriteOffset; + if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset + /* Have we underflowed? */ + if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) ) + { + if( dsw->dsw_OutputRunning ) + { + dsw->dsw_OutputUnderflows += 1; + } + dsw->dsw_WriteOffset = writeCursor; + numBytesEmpty = dsw->dsw_OutputSize - playWriteGap; + } + *bytesEmpty = numBytesEmpty; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + long bytesEmpty; + hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed + if (hr != DS_OK) return hr; + if( bytesEmpty == 0 ) return DS_OK; + // Lock free space in the DS + hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy the buffer into the DS + ZeroMemory(lpbuf1, dwsize1); + if(lpbuf2 != NULL) + { + ZeroMemory(lpbuf2, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame; + } + return hr; +} + +/************************************************************************************/ +HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + // Lock free space in the DS + hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy the buffer into the DS + CopyMemory(lpbuf1, buf, dwsize1); + if(lpbuf2 != NULL) + { + CopyMemory(lpbuf2, buf+dwsize1, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame; + } + return hr; +} + +/************************************************************************************/ +DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ) +{ + DWORD status; + if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK) + return( DSERR_INVALIDPARAM ); + else + return( status ); +} + +/* These routines are used to support audio input. + * Do NOT compile these calls when using NT4 because it does + * not support the entry points. + */ +/************************************************************************************/ +HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) +{ + HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); + if( hr != DS_OK ) return hr; + return hr; +} +/************************************************************************************/ +HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer ) +{ + DSCBUFFERDESC captureDesc; + WAVEFORMATEX wfFormat; + HRESULT result; + + dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short); + + // Define the buffer format + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = nChannels; + wfFormat.nSamplesPerSec = nFrameRate; + wfFormat.wBitsPerSample = 8 * sizeof(short); + wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; /* No extended format info. */ + dsw->dsw_InputSize = bytesPerBuffer; + // ---------------------------------------------------------------------- + // Setup the secondary buffer description + ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC)); + captureDesc.dwSize = sizeof(DSCBUFFERDESC); + captureDesc.dwFlags = 0; + captureDesc.dwBufferBytes = bytesPerBuffer; + captureDesc.lpwfxFormat = &wfFormat; + // Create the capture buffer + if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture, + &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result; + dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer + return DS_OK; +} + +/************************************************************************************/ +HRESULT DSW_StartInput( DSoundWrapper *dsw ) +{ + // Start the buffer playback + if( dsw->dsw_InputBuffer != NULL ) + { + return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_StopInput( DSoundWrapper *dsw ) +{ + // Stop the buffer playback + if( dsw->dsw_InputBuffer != NULL ) + { + return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ) +{ + HRESULT hr; + DWORD capturePos; + DWORD readPos; + long filled; + // Query to see how much data is in buffer. + // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly + // so let's pass a pointer just to be safe. + hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos ); + if( hr != DS_OK ) + { + return hr; + } + filled = readPos - dsw->dsw_ReadOffset; + if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset + *bytesFilled = filled; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + // Lock free space in the DS + hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy from DS to the buffer + CopyMemory( buf, lpbuf1, dwsize1); + if(lpbuf2 != NULL) + { + CopyMemory( buf+dwsize1, lpbuf2, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize; + IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + } + return hr; +} + diff --git a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h index 76087908c..9e3f565f2 100644 --- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h +++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h @@ -1,130 +1,130 @@ -#ifndef __DSOUND_WRAPPER_H -#define __DSOUND_WRAPPER_H -/* - * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 rossbencina Exp $ - * Simplified DirectSound interface. - * - * Author: Phil Burk & Robert Marsanyi - * - * For PortAudio Portable Real-Time Audio Library - * For more information see: http://www.softsynth.com/portaudio/ - * DirectSound Implementation - * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - * - */ - -/* on Borland compilers, WIN32 doesn't seem to be defined by default, which - breaks DSound.h. Adding the define here fixes the problem. - rossb. */ -#ifdef __BORLANDC__ -#if !defined(WIN32) -#define WIN32 -#endif -#endif - -/* - We are only using DX3 in here, no need to polute the namespace - davidv -*/ -#define DIRECTSOUND_VERSION 0x0300 - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -typedef struct -{ - HINSTANCE hInstance_; - - HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); - HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); - HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID); - - HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN); - HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); - HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID); -}DSoundEntryPoints; - -extern DSoundEntryPoints dswDSoundEntryPoints; - -void DSW_InitializeDSoundEntryPoints(void); -void DSW_TerminateDSoundEntryPoints(void); - -#define DSW_NUM_POSITIONS (4) -#define DSW_NUM_EVENTS (5) -#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS) - -typedef struct -{ -/* Output */ - LPDIRECTSOUND dsw_pDirectSound; - LPDIRECTSOUNDBUFFER dsw_OutputBuffer; - DWORD dsw_WriteOffset; /* last write position */ - INT dsw_OutputSize; - INT dsw_BytesPerOutputFrame; - /* Try to detect play buffer underflows. */ - LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */ - LARGE_INTEGER dsw_LastPlayTime; - UINT dsw_LastPlayCursor; - UINT dsw_OutputUnderflows; - BOOL dsw_OutputRunning; - /* use double which lets us can play for several thousand years with enough precision */ - double dsw_FramesWritten; - double dsw_FramesPlayed; -/* Input */ - INT dsw_BytesPerInputFrame; - LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture; - LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer; - UINT dsw_ReadOffset; /* last read position */ - UINT dsw_InputSize; -} DSoundWrapper; - -HRESULT DSW_Init( DSoundWrapper *dsw ); -void DSW_Term( DSoundWrapper *dsw ); -HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, - WORD nChannels, int bufSize ); -HRESULT DSW_StartOutput( DSoundWrapper *dsw ); -HRESULT DSW_StopOutput( DSoundWrapper *dsw ); -DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ); -HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ); -HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ); -HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ); -HRESULT DSW_Enumerate( DSoundWrapper *dsw ); - -HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, - WORD nChannels, int bufSize ); -HRESULT DSW_StartInput( DSoundWrapper *dsw ); -HRESULT DSW_StopInput( DSoundWrapper *dsw ); -HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ); -HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ); -HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* __DSOUND_WRAPPER_H */ +#ifndef __DSOUND_WRAPPER_H +#define __DSOUND_WRAPPER_H +/* + * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 rossbencina Exp $ + * Simplified DirectSound interface. + * + * Author: Phil Burk & Robert Marsanyi + * + * For PortAudio Portable Real-Time Audio Library + * For more information see: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + * + */ + +/* on Borland compilers, WIN32 doesn't seem to be defined by default, which + breaks DSound.h. Adding the define here fixes the problem. - rossb. */ +#ifdef __BORLANDC__ +#if !defined(WIN32) +#define WIN32 +#endif +#endif + +/* + We are only using DX3 in here, no need to polute the namespace - davidv +*/ +#define DIRECTSOUND_VERSION 0x0300 + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct +{ + HINSTANCE hInstance_; + + HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); + HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); + HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID); + + HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN); + HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); + HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID); +}DSoundEntryPoints; + +extern DSoundEntryPoints dswDSoundEntryPoints; + +void DSW_InitializeDSoundEntryPoints(void); +void DSW_TerminateDSoundEntryPoints(void); + +#define DSW_NUM_POSITIONS (4) +#define DSW_NUM_EVENTS (5) +#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS) + +typedef struct +{ +/* Output */ + LPDIRECTSOUND dsw_pDirectSound; + LPDIRECTSOUNDBUFFER dsw_OutputBuffer; + DWORD dsw_WriteOffset; /* last write position */ + INT dsw_OutputSize; + INT dsw_BytesPerOutputFrame; + /* Try to detect play buffer underflows. */ + LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */ + LARGE_INTEGER dsw_LastPlayTime; + UINT dsw_LastPlayCursor; + UINT dsw_OutputUnderflows; + BOOL dsw_OutputRunning; + /* use double which lets us can play for several thousand years with enough precision */ + double dsw_FramesWritten; + double dsw_FramesPlayed; +/* Input */ + INT dsw_BytesPerInputFrame; + LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture; + LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer; + UINT dsw_ReadOffset; /* last read position */ + UINT dsw_InputSize; +} DSoundWrapper; + +HRESULT DSW_Init( DSoundWrapper *dsw ); +void DSW_Term( DSoundWrapper *dsw ); +HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, + WORD nChannels, int bufSize ); +HRESULT DSW_StartOutput( DSoundWrapper *dsw ); +HRESULT DSW_StopOutput( DSoundWrapper *dsw ); +DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ); +HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ); +HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ); +HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ); +HRESULT DSW_Enumerate( DSoundWrapper *dsw ); + +HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, + WORD nChannels, int bufSize ); +HRESULT DSW_StartInput( DSoundWrapper *dsw ); +HRESULT DSW_StopInput( DSoundWrapper *dsw ); +HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ); +HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ); +HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* __DSOUND_WRAPPER_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_allocation.c b/pjmedia/src/pjmedia/portaudio/pa_allocation.c index 8d02e416d..035b4d0b5 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_allocation.c +++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.c @@ -1,234 +1,234 @@ -/* - * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $ - * Portable Audio I/O Library allocation group implementation - * memory allocation group for tracking allocation groups - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Allocation Group implementation. -*/ - - -#include "pa_allocation.h" -#include "pa_util.h" - - -/* - Maintain 3 singly linked lists... - linkBlocks: the buffers used to allocate the links - spareLinks: links available for use in the allocations list - allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory() - - Link block size is doubled every time new links are allocated. -*/ - - -#define PA_INITIAL_LINK_COUNT_ 16 - -struct PaUtilAllocationGroupLink -{ - struct PaUtilAllocationGroupLink *next; - void *buffer; -}; - -/* - Allocate a block of links. The first link will have it's buffer member - pointing to the block, and it's next member set to . The remaining - links will have NULL buffer members, and each link will point to - the next link except the last, which will point to -*/ -static struct PaUtilAllocationGroupLink *AllocateLinks( long count, - struct PaUtilAllocationGroupLink *nextBlock, - struct PaUtilAllocationGroupLink *nextSpare ) -{ - struct PaUtilAllocationGroupLink *result; - int i; - - result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory( - sizeof(struct PaUtilAllocationGroupLink) * count ); - if( result ) - { - /* the block link */ - result[0].buffer = result; - result[0].next = nextBlock; - - /* the spare links */ - for( i=1; ilinkCount = PA_INITIAL_LINK_COUNT_; - result->linkBlocks = &links[0]; - result->spareLinks = &links[1]; - result->allocations = 0; - } - else - { - PaUtil_FreeMemory( links ); - } - } - - return result; -} - - -void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group ) -{ - struct PaUtilAllocationGroupLink *current = group->linkBlocks; - struct PaUtilAllocationGroupLink *next; - - while( current ) - { - next = current->next; - PaUtil_FreeMemory( current->buffer ); - current = next; - } - - PaUtil_FreeMemory( group ); -} - - -void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size ) -{ - struct PaUtilAllocationGroupLink *links, *link; - void *result = 0; - - /* allocate more links if necessary */ - if( !group->spareLinks ) - { - /* double the link count on each block allocation */ - links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks ); - if( links ) - { - group->linkCount += group->linkCount; - group->linkBlocks = &links[0]; - group->spareLinks = &links[1]; - } - } - - if( group->spareLinks ) - { - result = PaUtil_AllocateMemory( size ); - if( result ) - { - link = group->spareLinks; - group->spareLinks = link->next; - - link->buffer = result; - link->next = group->allocations; - - group->allocations = link; - } - } - - return result; -} - - -void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer ) -{ - struct PaUtilAllocationGroupLink *current = group->allocations; - struct PaUtilAllocationGroupLink *previous = 0; - - if( buffer == 0 ) - return; - - /* find the right link and remove it */ - while( current ) - { - if( current->buffer == buffer ) - { - if( previous ) - { - previous->next = current->next; - } - else - { - group->allocations = current->next; - } - - current->buffer = 0; - current->next = group->spareLinks; - group->spareLinks = current; - - break; - } - - previous = current; - current = current->next; - } - - PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */ -} - - -void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group ) -{ - struct PaUtilAllocationGroupLink *current = group->allocations; - struct PaUtilAllocationGroupLink *previous = 0; - - /* free all buffers in the allocations list */ - while( current ) - { - PaUtil_FreeMemory( current->buffer ); - current->buffer = 0; - - previous = current; - current = current->next; - } - - /* link the former allocations list onto the front of the spareLinks list */ - if( previous ) - { - previous->next = group->spareLinks; - group->spareLinks = group->allocations; - group->allocations = 0; - } -} - +/* + * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $ + * Portable Audio I/O Library allocation group implementation + * memory allocation group for tracking allocation groups + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Allocation Group implementation. +*/ + + +#include "pa_allocation.h" +#include "pa_util.h" + + +/* + Maintain 3 singly linked lists... + linkBlocks: the buffers used to allocate the links + spareLinks: links available for use in the allocations list + allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory() + + Link block size is doubled every time new links are allocated. +*/ + + +#define PA_INITIAL_LINK_COUNT_ 16 + +struct PaUtilAllocationGroupLink +{ + struct PaUtilAllocationGroupLink *next; + void *buffer; +}; + +/* + Allocate a block of links. The first link will have it's buffer member + pointing to the block, and it's next member set to . The remaining + links will have NULL buffer members, and each link will point to + the next link except the last, which will point to +*/ +static struct PaUtilAllocationGroupLink *AllocateLinks( long count, + struct PaUtilAllocationGroupLink *nextBlock, + struct PaUtilAllocationGroupLink *nextSpare ) +{ + struct PaUtilAllocationGroupLink *result; + int i; + + result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory( + sizeof(struct PaUtilAllocationGroupLink) * count ); + if( result ) + { + /* the block link */ + result[0].buffer = result; + result[0].next = nextBlock; + + /* the spare links */ + for( i=1; ilinkCount = PA_INITIAL_LINK_COUNT_; + result->linkBlocks = &links[0]; + result->spareLinks = &links[1]; + result->allocations = 0; + } + else + { + PaUtil_FreeMemory( links ); + } + } + + return result; +} + + +void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group ) +{ + struct PaUtilAllocationGroupLink *current = group->linkBlocks; + struct PaUtilAllocationGroupLink *next; + + while( current ) + { + next = current->next; + PaUtil_FreeMemory( current->buffer ); + current = next; + } + + PaUtil_FreeMemory( group ); +} + + +void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size ) +{ + struct PaUtilAllocationGroupLink *links, *link; + void *result = 0; + + /* allocate more links if necessary */ + if( !group->spareLinks ) + { + /* double the link count on each block allocation */ + links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks ); + if( links ) + { + group->linkCount += group->linkCount; + group->linkBlocks = &links[0]; + group->spareLinks = &links[1]; + } + } + + if( group->spareLinks ) + { + result = PaUtil_AllocateMemory( size ); + if( result ) + { + link = group->spareLinks; + group->spareLinks = link->next; + + link->buffer = result; + link->next = group->allocations; + + group->allocations = link; + } + } + + return result; +} + + +void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer ) +{ + struct PaUtilAllocationGroupLink *current = group->allocations; + struct PaUtilAllocationGroupLink *previous = 0; + + if( buffer == 0 ) + return; + + /* find the right link and remove it */ + while( current ) + { + if( current->buffer == buffer ) + { + if( previous ) + { + previous->next = current->next; + } + else + { + group->allocations = current->next; + } + + current->buffer = 0; + current->next = group->spareLinks; + group->spareLinks = current; + + break; + } + + previous = current; + current = current->next; + } + + PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */ +} + + +void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group ) +{ + struct PaUtilAllocationGroupLink *current = group->allocations; + struct PaUtilAllocationGroupLink *previous = 0; + + /* free all buffers in the allocations list */ + while( current ) + { + PaUtil_FreeMemory( current->buffer ); + current->buffer = 0; + + previous = current; + current = current->next; + } + + /* link the former allocations list onto the front of the spareLinks list */ + if( previous ) + { + previous->next = group->spareLinks; + group->spareLinks = group->allocations; + group->allocations = 0; + } +} + diff --git a/pjmedia/src/pjmedia/portaudio/pa_allocation.h b/pjmedia/src/pjmedia/portaudio/pa_allocation.h index 13b9ee32a..fb9321a01 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_allocation.h +++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.h @@ -1,95 +1,95 @@ -#ifndef PA_ALLOCATION_H -#define PA_ALLOCATION_H -/* - * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $ - * Portable Audio I/O Library allocation context header - * memory allocation context for tracking allocation groups - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Allocation Group prototypes. An Allocation Group makes it easy to - allocate multiple blocks of memory and free them all simultanously. - - An allocation group is useful for keeping track of multiple blocks - of memory which are allocated at the same time (such as during initialization) - and need to be deallocated at the same time. The allocation group maintains - a list of allocated blocks, and can deallocate them all simultaneously which - can be usefull for cleaning up after a partially initialized object fails. - - The allocation group implementation is built on top of the lower - level allocation functions defined in pa_util.h -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -typedef struct -{ - long linkCount; - struct PaUtilAllocationGroupLink *linkBlocks; - struct PaUtilAllocationGroupLink *spareLinks; - struct PaUtilAllocationGroupLink *allocations; -}PaUtilAllocationGroup; - - - -/** Create an allocation group. -*/ -PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void ); - -/** Destroy an allocation group, but not the memory allocated through the group. -*/ -void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group ); - -/** Allocate a block of memory though an allocation group. -*/ -void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size ); - -/** Free a block of memory that was previously allocated though an allocation - group. Calling this function is a relatively time consuming operation. - Under normal circumstances clients should call PaUtil_FreeAllAllocations to - free all allocated blocks simultaneously. - @see PaUtil_FreeAllAllocations -*/ -void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer ); - -/** Free all blocks of memory which have been allocated through the allocation - group. This function doesn't destroy the group itself. -*/ -void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_ALLOCATION_H */ +#ifndef PA_ALLOCATION_H +#define PA_ALLOCATION_H +/* + * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $ + * Portable Audio I/O Library allocation context header + * memory allocation context for tracking allocation groups + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Allocation Group prototypes. An Allocation Group makes it easy to + allocate multiple blocks of memory and free them all simultanously. + + An allocation group is useful for keeping track of multiple blocks + of memory which are allocated at the same time (such as during initialization) + and need to be deallocated at the same time. The allocation group maintains + a list of allocated blocks, and can deallocate them all simultaneously which + can be usefull for cleaning up after a partially initialized object fails. + + The allocation group implementation is built on top of the lower + level allocation functions defined in pa_util.h +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct +{ + long linkCount; + struct PaUtilAllocationGroupLink *linkBlocks; + struct PaUtilAllocationGroupLink *spareLinks; + struct PaUtilAllocationGroupLink *allocations; +}PaUtilAllocationGroup; + + + +/** Create an allocation group. +*/ +PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void ); + +/** Destroy an allocation group, but not the memory allocated through the group. +*/ +void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group ); + +/** Allocate a block of memory though an allocation group. +*/ +void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size ); + +/** Free a block of memory that was previously allocated though an allocation + group. Calling this function is a relatively time consuming operation. + Under normal circumstances clients should call PaUtil_FreeAllAllocations to + free all allocated blocks simultaneously. + @see PaUtil_FreeAllAllocations +*/ +void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer ); + +/** Free all blocks of memory which have been allocated through the allocation + group. This function doesn't destroy the group itself. +*/ +void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_ALLOCATION_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_converters.c b/pjmedia/src/pjmedia/portaudio/pa_converters.c index 36992823e..4ee73c9ae 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_converters.c +++ b/pjmedia/src/pjmedia/portaudio/pa_converters.c @@ -1,1926 +1,1926 @@ -/* - * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $ - * Portable Audio I/O Library sample conversion mechanism - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Conversion functions implementations. - - If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it - - @todo Consider whether functions which dither but don't clip should exist, - V18 automatically enabled clipping whenever dithering was selected. Perhaps - we should do the same. - - @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither, - Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither, - Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither, - Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither, - - @todo review the converters marked REVIEW: Float32_To_Int32, - Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip, - Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32 -*/ - - -#include "pa_converters.h" -#include "pa_dither.h" -#include "pa_endianness.h" -#include "pa_types.h" - - -PaSampleFormat PaUtil_SelectClosestAvailableFormat( - PaSampleFormat availableFormats, PaSampleFormat format ) -{ - PaSampleFormat result; - - format &= ~paNonInterleaved; - availableFormats &= ~paNonInterleaved; - - if( (format & availableFormats) == 0 ) - { - /* NOTE: this code depends on the sample format constants being in - descending order of quality - ie best quality is 0 - FIXME: should write an assert which checks that all of the - known constants conform to that requirement. - */ - - if( format != 0x01 ) - { - /* scan for better formats */ - result = format; - do - { - result >>= 1; - } - while( (result & availableFormats) == 0 && result != 0 ); - } - else - { - result = 0; - } - - if( result == 0 ){ - /* scan for worse formats */ - result = format; - do - { - result <<= 1; - } - while( (result & availableFormats) == 0 && result != paCustomFormat ); - - if( (result & availableFormats) == 0 ) - result = paSampleFormatNotSupported; - } - - }else{ - result = format; - } - - return result; -} - -/* -------------------------------------------------------------------------- */ - -#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \ - switch( format & ~paNonInterleaved ){ \ - case paFloat32: \ - float32 \ - case paInt32: \ - int32 \ - case paInt24: \ - int24 \ - case paInt16: \ - int16 \ - case paInt8: \ - int8 \ - case paUInt8: \ - uint8 \ - default: return 0; \ - } - -/* -------------------------------------------------------------------------- */ - -#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination ) \ - if( flags & paClipOff ){ /* no clip */ \ - if( flags & paDitherOff ){ /* no dither */ \ - return paConverters. source ## _To_ ## destination; \ - }else{ /* dither */ \ - return paConverters. source ## _To_ ## destination ## _Dither; \ - } \ - }else{ /* clip */ \ - if( flags & paDitherOff ){ /* no dither */ \ - return paConverters. source ## _To_ ## destination ## _Clip; \ - }else{ /* dither */ \ - return paConverters. source ## _To_ ## destination ## _DitherClip; \ - } \ - } - -/* -------------------------------------------------------------------------- */ - -#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination ) \ - if( flags & paDitherOff ){ /* no dither */ \ - return paConverters. source ## _To_ ## destination; \ - }else{ /* dither */ \ - return paConverters. source ## _To_ ## destination ## _Dither; \ - } - -/* -------------------------------------------------------------------------- */ - -#define PA_USE_CONVERTER_( source, destination )\ - return paConverters. source ## _To_ ## destination; - -/* -------------------------------------------------------------------------- */ - -#define PA_UNITY_CONVERSION_( wordlength )\ - return paConverters. Copy_ ## wordlength ## _To_ ## wordlength; - -/* -------------------------------------------------------------------------- */ - -PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat, - PaSampleFormat destinationFormat, PaStreamFlags flags ) -{ - PA_SELECT_FORMAT_( sourceFormat, - /* paFloat32: */ - PA_SELECT_FORMAT_( destinationFormat, - /* paFloat32: */ PA_UNITY_CONVERSION_( 32 ), - /* paInt32: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ), - /* paInt24: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ), - /* paInt16: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ), - /* paInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ), - /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 ) - ), - /* paInt32: */ - PA_SELECT_FORMAT_( destinationFormat, - /* paFloat32: */ PA_USE_CONVERTER_( Int32, Float32 ), - /* paInt32: */ PA_UNITY_CONVERSION_( 32 ), - /* paInt24: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ), - /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ), - /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ), - /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 ) - ), - /* paInt24: */ - PA_SELECT_FORMAT_( destinationFormat, - /* paFloat32: */ PA_USE_CONVERTER_( Int24, Float32 ), - /* paInt32: */ PA_USE_CONVERTER_( Int24, Int32 ), - /* paInt24: */ PA_UNITY_CONVERSION_( 24 ), - /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ), - /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ), - /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 ) - ), - /* paInt16: */ - PA_SELECT_FORMAT_( destinationFormat, - /* paFloat32: */ PA_USE_CONVERTER_( Int16, Float32 ), - /* paInt32: */ PA_USE_CONVERTER_( Int16, Int32 ), - /* paInt24: */ PA_USE_CONVERTER_( Int16, Int24 ), - /* paInt16: */ PA_UNITY_CONVERSION_( 16 ), - /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ), - /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 ) - ), - /* paInt8: */ - PA_SELECT_FORMAT_( destinationFormat, - /* paFloat32: */ PA_USE_CONVERTER_( Int8, Float32 ), - /* paInt32: */ PA_USE_CONVERTER_( Int8, Int32 ), - /* paInt24: */ PA_USE_CONVERTER_( Int8, Int24 ), - /* paInt16: */ PA_USE_CONVERTER_( Int8, Int16 ), - /* paInt8: */ PA_UNITY_CONVERSION_( 8 ), - /* paUInt8: */ PA_USE_CONVERTER_( Int8, UInt8 ) - ), - /* paUInt8: */ - PA_SELECT_FORMAT_( destinationFormat, - /* paFloat32: */ PA_USE_CONVERTER_( UInt8, Float32 ), - /* paInt32: */ PA_USE_CONVERTER_( UInt8, Int32 ), - /* paInt24: */ PA_USE_CONVERTER_( UInt8, Int24 ), - /* paInt16: */ PA_USE_CONVERTER_( UInt8, Int16 ), - /* paInt8: */ PA_USE_CONVERTER_( UInt8, Int8 ), - /* paUInt8: */ PA_UNITY_CONVERSION_( 8 ) - ) - ) -} - -/* -------------------------------------------------------------------------- */ - -#ifdef PA_NO_STANDARD_CONVERTERS - -/* -------------------------------------------------------------------------- */ - -PaUtilConverterTable paConverters = { - 0, /* PaUtilConverter *Float32_To_Int32; */ - 0, /* PaUtilConverter *Float32_To_Int32_Dither; */ - 0, /* PaUtilConverter *Float32_To_Int32_Clip; */ - 0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */ - - 0, /* PaUtilConverter *Float32_To_Int24; */ - 0, /* PaUtilConverter *Float32_To_Int24_Dither; */ - 0, /* PaUtilConverter *Float32_To_Int24_Clip; */ - 0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */ - - 0, /* PaUtilConverter *Float32_To_Int16; */ - 0, /* PaUtilConverter *Float32_To_Int16_Dither; */ - 0, /* PaUtilConverter *Float32_To_Int16_Clip; */ - 0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */ - - 0, /* PaUtilConverter *Float32_To_Int8; */ - 0, /* PaUtilConverter *Float32_To_Int8_Dither; */ - 0, /* PaUtilConverter *Float32_To_Int8_Clip; */ - 0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */ - - 0, /* PaUtilConverter *Float32_To_UInt8; */ - 0, /* PaUtilConverter *Float32_To_UInt8_Dither; */ - 0, /* PaUtilConverter *Float32_To_UInt8_Clip; */ - 0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */ - - 0, /* PaUtilConverter *Int32_To_Float32; */ - 0, /* PaUtilConverter *Int32_To_Int24; */ - 0, /* PaUtilConverter *Int32_To_Int24_Dither; */ - 0, /* PaUtilConverter *Int32_To_Int16; */ - 0, /* PaUtilConverter *Int32_To_Int16_Dither; */ - 0, /* PaUtilConverter *Int32_To_Int8; */ - 0, /* PaUtilConverter *Int32_To_Int8_Dither; */ - 0, /* PaUtilConverter *Int32_To_UInt8; */ - 0, /* PaUtilConverter *Int32_To_UInt8_Dither; */ - - 0, /* PaUtilConverter *Int24_To_Float32; */ - 0, /* PaUtilConverter *Int24_To_Int32; */ - 0, /* PaUtilConverter *Int24_To_Int16; */ - 0, /* PaUtilConverter *Int24_To_Int16_Dither; */ - 0, /* PaUtilConverter *Int24_To_Int8; */ - 0, /* PaUtilConverter *Int24_To_Int8_Dither; */ - 0, /* PaUtilConverter *Int24_To_UInt8; */ - 0, /* PaUtilConverter *Int24_To_UInt8_Dither; */ - - 0, /* PaUtilConverter *Int16_To_Float32; */ - 0, /* PaUtilConverter *Int16_To_Int32; */ - 0, /* PaUtilConverter *Int16_To_Int24; */ - 0, /* PaUtilConverter *Int16_To_Int8; */ - 0, /* PaUtilConverter *Int16_To_Int8_Dither; */ - 0, /* PaUtilConverter *Int16_To_UInt8; */ - 0, /* PaUtilConverter *Int16_To_UInt8_Dither; */ - - 0, /* PaUtilConverter *Int8_To_Float32; */ - 0, /* PaUtilConverter *Int8_To_Int32; */ - 0, /* PaUtilConverter *Int8_To_Int24 */ - 0, /* PaUtilConverter *Int8_To_Int16; */ - 0, /* PaUtilConverter *Int8_To_UInt8; */ - - 0, /* PaUtilConverter *UInt8_To_Float32; */ - 0, /* PaUtilConverter *UInt8_To_Int32; */ - 0, /* PaUtilConverter *UInt8_To_Int24; */ - 0, /* PaUtilConverter *UInt8_To_Int16; */ - 0, /* PaUtilConverter *UInt8_To_Int8; */ - - 0, /* PaUtilConverter *Copy_8_To_8; */ - 0, /* PaUtilConverter *Copy_16_To_16; */ - 0, /* PaUtilConverter *Copy_24_To_24; */ - 0 /* PaUtilConverter *Copy_32_To_32; */ -}; - -/* -------------------------------------------------------------------------- */ - -#else /* PA_NO_STANDARD_CONVERTERS is not defined */ - -/* -------------------------------------------------------------------------- */ - -#define PA_CLIP_( val, min, max )\ - { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } - - -static const float const_1_div_128_ = 1.0f / 128.0f; /* 8 bit multiplier */ - -static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */ - -static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */ - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float scaled = *src * 0x7FFFFFFF; - *dest = lrintf(scaled-0.5f); -#else - double scaled = *src * 0x7FFFFFFF; - *dest = (signed long) scaled; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = ((float)*src * (2147483646.0f)) + dither; - *dest = lrintf(dithered - 0.5f); -#else - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - double dithered = ((double)*src * (2147483646.0)) + dither; - *dest = (signed long) dithered; -#endif - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648.f, 2147483647.f ); - *dest = lrintf(scaled-0.5f); -#else - double scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648., 2147483647. ); - *dest = (signed long) scaled; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - - while( count-- ) - { - /* REVIEW */ -#ifdef PA_USE_C99_LRINTF - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = ((float)*src * (2147483646.0f)) + dither; - PA_CLIP_( dithered, -2147483648.f, 2147483647.f ); - *dest = lrintf(dithered-0.5f); -#else - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - double dithered = ((double)*src * (2147483646.0)) + dither; - PA_CLIP_( dithered, -2147483648., 2147483647. ); - *dest = (signed long) dithered; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* convert to 32 bit and drop the low 8 bits */ - double scaled = *src * 0x7FFFFFFF; - temp = (signed long) scaled; - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(temp >> 24); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 8); -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - while( count-- ) - { - /* convert to 32 bit and drop the low 8 bits */ - - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - double dithered = ((double)*src * (2147483646.0)) + dither; - - temp = (signed long) dithered; - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(temp >> 24); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 8); -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* convert to 32 bit and drop the low 8 bits */ - double scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648., 2147483647. ); - temp = (signed long) scaled; - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(temp >> 24); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 8); -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - while( count-- ) - { - /* convert to 32 bit and drop the low 8 bits */ - - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - double dithered = ((double)*src * (2147483646.0)) + dither; - PA_CLIP_( dithered, -2147483648., 2147483647. ); - - temp = (signed long) dithered; - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(temp >> 24); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 8); -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { -#ifdef PA_USE_C99_LRINTF - float tempf = (*src * (32767.0f)) ; - *dest = lrintf(tempf-0.5f); -#else - short samp = (short) (*src * (32767.0f)); - *dest = samp; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - - while( count-- ) - { - - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = (*src * (32766.0f)) + dither; - -#ifdef PA_USE_C99_LRINTF - *dest = lrintf(dithered-0.5f); -#else - *dest = (signed short) dithered; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { -#ifdef PA_USE_C99_LRINTF - long samp = lrintf((*src * (32767.0f)) -0.5f); -#else - long samp = (signed long) (*src * (32767.0f)); -#endif - PA_CLIP_( samp, -0x8000, 0x7FFF ); - *dest = (signed short) samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = (*src * (32766.0f)) + dither; - signed long samp = (signed long) dithered; - PA_CLIP_( samp, -0x8000, 0x7FFF ); -#ifdef PA_USE_C99_LRINTF - *dest = lrintf(samp-0.5f); -#else - *dest = (signed short) samp; -#endif - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - signed char samp = (signed char) (*src * (127.0f)); - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = (*src * (126.0f)) + dither; - signed long samp = (signed long) dithered; - *dest = (signed char) samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int8_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - signed long samp = (signed long)(*src * (127.0f)); - PA_CLIP_( samp, -0x80, 0x7F ); - *dest = (signed char) samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int8_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - /* use smaller scaler to prevent overflow when we add the dither */ - float dithered = (*src * (126.0f)) + dither; - signed long samp = (signed long) dithered; - PA_CLIP_( samp, -0x80, 0x7F ); - *dest = (signed char) samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f)))); - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_UInt8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* IMPLEMENT ME */ - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_UInt8_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* IMPLEMENT ME */ - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_UInt8_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* IMPLEMENT ME */ - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Float32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - float *dest = (float*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = (float) ((double)*src * const_1_div_2147483648_); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW */ -#if defined(PA_LITTLE_ENDIAN) - dest[0] = (unsigned char)(*src >> 8); - dest[1] = (unsigned char)(*src >> 16); - dest[2] = (unsigned char)(*src >> 24); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(*src >> 24); - dest[1] = (unsigned char)(*src >> 16); - dest[2] = (unsigned char)(*src >> 8); -#endif - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int24_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = (signed short) ((*src) >> 16); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int16_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - signed long dither; - - while( count-- ) - { - /* REVIEW */ - dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); - *dest = (signed short) ((((*src)>>1) + dither) >> 15); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = (signed char) ((*src) >> 24); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - signed long dither; - - while( count-- ) - { - /* REVIEW */ - dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); - *dest = (signed char) ((((*src)>>1) + dither) >> 23); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (unsigned char)(((*src) >> 24) + 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int32_To_UInt8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed long *src = (signed long*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* IMPLEMENT ME */ - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Float32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - float *dest = (float*)destinationBuffer; - signed long temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - temp = (((long)src[0]) << 8); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 24); -#elif defined(PA_BIG_ENDIAN) - temp = (((long)src[0]) << 24); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 8); -#endif - - *dest = (float) ((double)temp * const_1_div_2147483648_); - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed long *dest = (signed long*) destinationBuffer; - signed long temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - temp = (((long)src[0]) << 8); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 24); -#elif defined(PA_BIG_ENDIAN) - temp = (((long)src[0]) << 24); - temp = temp | (((long)src[1]) << 16); - temp = temp | (((long)src[2]) << 8); -#endif - - *dest = temp; - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - - signed short temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - /* src[0] is discarded */ - temp = (((signed short)src[1])); - temp = temp | (signed short)(((signed short)src[2]) << 8); -#elif defined(PA_BIG_ENDIAN) - /* src[2] is discarded */ - temp = (signed short)(((signed short)src[0]) << 8); - temp = temp | (((signed short)src[1])); -#endif - - *dest = temp; - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int16_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - /* src[0] is discarded */ - /* src[1] is discarded */ - *dest = src[2]; -#elif defined(PA_BIG_ENDIAN) - /* src[2] is discarded */ - /* src[1] is discarded */ - *dest = src[0]; -#endif - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - /* src[0] is discarded */ - /* src[1] is discarded */ - *dest = (unsigned char)(src[2] + 128); -#elif defined(PA_BIG_ENDIAN) - *dest = (unsigned char)(src[0] + 128); - /* src[1] is discarded */ - /* src[2] is discarded */ -#endif - - src += sourceStride * 3; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int24_To_UInt8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - (void) destinationBuffer; /* unused parameters */ - (void) destinationStride; /* unused parameters */ - (void) sourceBuffer; /* unused parameters */ - (void) sourceStride; /* unused parameters */ - (void) count; /* unused parameters */ - (void) ditherGenerator; /* unused parameters */ - /* IMPLEMENT ME */ -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Float32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed short *src = (signed short*)sourceBuffer; - float *dest = (float*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */ - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Int32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed short *src = (signed short*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* REVIEW: we should consider something like - (*src << 16) | (*src & 0xFFFF) - */ - - *dest = *src << 16; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed short *src = (signed short*) sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed short temp; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - temp = *src; - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = 0; - dest[1] = (unsigned char)(temp); - dest[2] = (unsigned char)(temp >> 8); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp); - dest[2] = 0; -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed short *src = (signed short*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (signed char)((*src) >> 8); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_Int8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed short *src = (signed short*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* IMPLEMENT ME */ - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed short *src = (signed short*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (unsigned char)(((*src) >> 8) + 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int16_To_UInt8_Dither( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed short *src = (signed short*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - /* IMPLEMENT ME */ - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_Float32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - float *dest = (float*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - float samp = *src * const_1_div_128_; - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_Int32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (*src) << 24; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = 0; - dest[1] = 0; - dest[2] = (*src); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (*src); - dest[1] = 0; - dest[2] = 0; -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (signed short)((*src) << 8); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Int8_To_UInt8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - signed char *src = (signed char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (unsigned char)(*src + 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Float32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - float *dest = (float*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - float samp = (*src - 128) * const_1_div_128_; - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (*src - 128) << 24; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - (void) ditherGenerator; /* unused parameters */ - - while( count-- ) - { - -#if defined(PA_LITTLE_ENDIAN) - dest[0] = 0; - dest[1] = 0; - dest[2] = (unsigned char)(*src - 128); -#elif defined(PA_BIG_ENDIAN) - dest[0] = (unsigned char)(*src - 128); - dest[1] = 0; - dest[2] = 0; -#endif - - src += sourceStride; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (signed short)((*src - 128) << 8); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void UInt8_To_Int8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - signed char *dest = (signed char*)destinationBuffer; - (void)ditherGenerator; /* unused parameter */ - - while( count-- ) - { - (*dest) = (signed char)(*src - 128); - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Copy_8_To_8( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = *src; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Copy_16_To_16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaUint16 *src = (PaUint16 *)sourceBuffer; - PaUint16 *dest = (PaUint16 *)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = *src; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Copy_24_To_24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - unsigned char *src = (unsigned char*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - dest[0] = src[0]; - dest[1] = src[1]; - dest[2] = src[2]; - - src += sourceStride * 3; - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Copy_32_To_32( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - PaUint32 *dest = (PaUint32 *)destinationBuffer; - PaUint32 *src = (PaUint32 *)sourceBuffer; - - (void) ditherGenerator; /* unused parameter */ - - while( count-- ) - { - *dest = *src; - - src += sourceStride; - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -PaUtilConverterTable paConverters = { - Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */ - Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */ - Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */ - Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */ - - Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */ - Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */ - Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */ - Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */ - - Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */ - Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */ - Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */ - Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */ - - Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */ - Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */ - Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */ - Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */ - - Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */ - Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */ - Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */ - Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */ - - Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */ - Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */ - Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */ - Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */ - Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */ - Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */ - Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */ - Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */ - Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */ - - Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */ - Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */ - Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */ - Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */ - Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */ - Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */ - Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */ - Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */ - - Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */ - Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */ - Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */ - Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */ - Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */ - Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */ - Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */ - - Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */ - Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */ - Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */ - Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */ - Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */ - - UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */ - UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */ - UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */ - UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */ - UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */ - - Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */ - Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */ - Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */ - Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */ -}; - -/* -------------------------------------------------------------------------- */ - -#endif /* PA_NO_STANDARD_CONVERTERS */ - -/* -------------------------------------------------------------------------- */ - -PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat ) -{ - switch( destinationFormat & ~paNonInterleaved ){ - case paFloat32: - return paZeroers.Zero32; - case paInt32: - return paZeroers.Zero32; - case paInt24: - return paZeroers.Zero24; - case paInt16: - return paZeroers.Zero16; - case paInt8: - return paZeroers.Zero8; - case paUInt8: - return paZeroers.ZeroU8; - default: return 0; - } -} - -/* -------------------------------------------------------------------------- */ - -#ifdef PA_NO_STANDARD_ZEROERS - -/* -------------------------------------------------------------------------- */ - -PaUtilZeroerTable paZeroers = { - 0, /* PaUtilZeroer *ZeroU8; */ - 0, /* PaUtilZeroer *Zero8; */ - 0, /* PaUtilZeroer *Zero16; */ - 0, /* PaUtilZeroer *Zero24; */ - 0, /* PaUtilZeroer *Zero32; */ -}; - -/* -------------------------------------------------------------------------- */ - -#else /* PA_NO_STANDARD_ZEROERS is not defined */ - -/* -------------------------------------------------------------------------- */ - -static void ZeroU8( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - unsigned char *dest = (unsigned char*)destinationBuffer; - - while( count-- ) - { - *dest = 128; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero8( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - unsigned char *dest = (unsigned char*)destinationBuffer; - - while( count-- ) - { - *dest = 0; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero16( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - PaUint16 *dest = (PaUint16 *)destinationBuffer; - - while( count-- ) - { - *dest = 0; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero24( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - unsigned char *dest = (unsigned char*)destinationBuffer; - - while( count-- ) - { - dest[0] = 0; - dest[1] = 0; - dest[2] = 0; - - dest += destinationStride * 3; - } -} - -/* -------------------------------------------------------------------------- */ - -static void Zero32( void *destinationBuffer, signed int destinationStride, - unsigned int count ) -{ - PaUint32 *dest = (PaUint32 *)destinationBuffer; - - while( count-- ) - { - *dest = 0; - - dest += destinationStride; - } -} - -/* -------------------------------------------------------------------------- */ - -PaUtilZeroerTable paZeroers = { - ZeroU8, /* PaUtilZeroer *ZeroU8; */ - Zero8, /* PaUtilZeroer *Zero8; */ - Zero16, /* PaUtilZeroer *Zero16; */ - Zero24, /* PaUtilZeroer *Zero24; */ - Zero32, /* PaUtilZeroer *Zero32; */ -}; - -/* -------------------------------------------------------------------------- */ - -#endif /* PA_NO_STANDARD_ZEROERS */ +/* + * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $ + * Portable Audio I/O Library sample conversion mechanism + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Conversion functions implementations. + + If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it + + @todo Consider whether functions which dither but don't clip should exist, + V18 automatically enabled clipping whenever dithering was selected. Perhaps + we should do the same. + + @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither, + Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither, + Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither, + Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither, + + @todo review the converters marked REVIEW: Float32_To_Int32, + Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip, + Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32 +*/ + + +#include "pa_converters.h" +#include "pa_dither.h" +#include "pa_endianness.h" +#include "pa_types.h" + + +PaSampleFormat PaUtil_SelectClosestAvailableFormat( + PaSampleFormat availableFormats, PaSampleFormat format ) +{ + PaSampleFormat result; + + format &= ~paNonInterleaved; + availableFormats &= ~paNonInterleaved; + + if( (format & availableFormats) == 0 ) + { + /* NOTE: this code depends on the sample format constants being in + descending order of quality - ie best quality is 0 + FIXME: should write an assert which checks that all of the + known constants conform to that requirement. + */ + + if( format != 0x01 ) + { + /* scan for better formats */ + result = format; + do + { + result >>= 1; + } + while( (result & availableFormats) == 0 && result != 0 ); + } + else + { + result = 0; + } + + if( result == 0 ){ + /* scan for worse formats */ + result = format; + do + { + result <<= 1; + } + while( (result & availableFormats) == 0 && result != paCustomFormat ); + + if( (result & availableFormats) == 0 ) + result = paSampleFormatNotSupported; + } + + }else{ + result = format; + } + + return result; +} + +/* -------------------------------------------------------------------------- */ + +#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \ + switch( format & ~paNonInterleaved ){ \ + case paFloat32: \ + float32 \ + case paInt32: \ + int32 \ + case paInt24: \ + int24 \ + case paInt16: \ + int16 \ + case paInt8: \ + int8 \ + case paUInt8: \ + uint8 \ + default: return 0; \ + } + +/* -------------------------------------------------------------------------- */ + +#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination ) \ + if( flags & paClipOff ){ /* no clip */ \ + if( flags & paDitherOff ){ /* no dither */ \ + return paConverters. source ## _To_ ## destination; \ + }else{ /* dither */ \ + return paConverters. source ## _To_ ## destination ## _Dither; \ + } \ + }else{ /* clip */ \ + if( flags & paDitherOff ){ /* no dither */ \ + return paConverters. source ## _To_ ## destination ## _Clip; \ + }else{ /* dither */ \ + return paConverters. source ## _To_ ## destination ## _DitherClip; \ + } \ + } + +/* -------------------------------------------------------------------------- */ + +#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination ) \ + if( flags & paDitherOff ){ /* no dither */ \ + return paConverters. source ## _To_ ## destination; \ + }else{ /* dither */ \ + return paConverters. source ## _To_ ## destination ## _Dither; \ + } + +/* -------------------------------------------------------------------------- */ + +#define PA_USE_CONVERTER_( source, destination )\ + return paConverters. source ## _To_ ## destination; + +/* -------------------------------------------------------------------------- */ + +#define PA_UNITY_CONVERSION_( wordlength )\ + return paConverters. Copy_ ## wordlength ## _To_ ## wordlength; + +/* -------------------------------------------------------------------------- */ + +PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat, + PaSampleFormat destinationFormat, PaStreamFlags flags ) +{ + PA_SELECT_FORMAT_( sourceFormat, + /* paFloat32: */ + PA_SELECT_FORMAT_( destinationFormat, + /* paFloat32: */ PA_UNITY_CONVERSION_( 32 ), + /* paInt32: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ), + /* paInt24: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ), + /* paInt16: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ), + /* paInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ), + /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 ) + ), + /* paInt32: */ + PA_SELECT_FORMAT_( destinationFormat, + /* paFloat32: */ PA_USE_CONVERTER_( Int32, Float32 ), + /* paInt32: */ PA_UNITY_CONVERSION_( 32 ), + /* paInt24: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ), + /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ), + /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ), + /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 ) + ), + /* paInt24: */ + PA_SELECT_FORMAT_( destinationFormat, + /* paFloat32: */ PA_USE_CONVERTER_( Int24, Float32 ), + /* paInt32: */ PA_USE_CONVERTER_( Int24, Int32 ), + /* paInt24: */ PA_UNITY_CONVERSION_( 24 ), + /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ), + /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ), + /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 ) + ), + /* paInt16: */ + PA_SELECT_FORMAT_( destinationFormat, + /* paFloat32: */ PA_USE_CONVERTER_( Int16, Float32 ), + /* paInt32: */ PA_USE_CONVERTER_( Int16, Int32 ), + /* paInt24: */ PA_USE_CONVERTER_( Int16, Int24 ), + /* paInt16: */ PA_UNITY_CONVERSION_( 16 ), + /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ), + /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 ) + ), + /* paInt8: */ + PA_SELECT_FORMAT_( destinationFormat, + /* paFloat32: */ PA_USE_CONVERTER_( Int8, Float32 ), + /* paInt32: */ PA_USE_CONVERTER_( Int8, Int32 ), + /* paInt24: */ PA_USE_CONVERTER_( Int8, Int24 ), + /* paInt16: */ PA_USE_CONVERTER_( Int8, Int16 ), + /* paInt8: */ PA_UNITY_CONVERSION_( 8 ), + /* paUInt8: */ PA_USE_CONVERTER_( Int8, UInt8 ) + ), + /* paUInt8: */ + PA_SELECT_FORMAT_( destinationFormat, + /* paFloat32: */ PA_USE_CONVERTER_( UInt8, Float32 ), + /* paInt32: */ PA_USE_CONVERTER_( UInt8, Int32 ), + /* paInt24: */ PA_USE_CONVERTER_( UInt8, Int24 ), + /* paInt16: */ PA_USE_CONVERTER_( UInt8, Int16 ), + /* paInt8: */ PA_USE_CONVERTER_( UInt8, Int8 ), + /* paUInt8: */ PA_UNITY_CONVERSION_( 8 ) + ) + ) +} + +/* -------------------------------------------------------------------------- */ + +#ifdef PA_NO_STANDARD_CONVERTERS + +/* -------------------------------------------------------------------------- */ + +PaUtilConverterTable paConverters = { + 0, /* PaUtilConverter *Float32_To_Int32; */ + 0, /* PaUtilConverter *Float32_To_Int32_Dither; */ + 0, /* PaUtilConverter *Float32_To_Int32_Clip; */ + 0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */ + + 0, /* PaUtilConverter *Float32_To_Int24; */ + 0, /* PaUtilConverter *Float32_To_Int24_Dither; */ + 0, /* PaUtilConverter *Float32_To_Int24_Clip; */ + 0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */ + + 0, /* PaUtilConverter *Float32_To_Int16; */ + 0, /* PaUtilConverter *Float32_To_Int16_Dither; */ + 0, /* PaUtilConverter *Float32_To_Int16_Clip; */ + 0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */ + + 0, /* PaUtilConverter *Float32_To_Int8; */ + 0, /* PaUtilConverter *Float32_To_Int8_Dither; */ + 0, /* PaUtilConverter *Float32_To_Int8_Clip; */ + 0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */ + + 0, /* PaUtilConverter *Float32_To_UInt8; */ + 0, /* PaUtilConverter *Float32_To_UInt8_Dither; */ + 0, /* PaUtilConverter *Float32_To_UInt8_Clip; */ + 0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */ + + 0, /* PaUtilConverter *Int32_To_Float32; */ + 0, /* PaUtilConverter *Int32_To_Int24; */ + 0, /* PaUtilConverter *Int32_To_Int24_Dither; */ + 0, /* PaUtilConverter *Int32_To_Int16; */ + 0, /* PaUtilConverter *Int32_To_Int16_Dither; */ + 0, /* PaUtilConverter *Int32_To_Int8; */ + 0, /* PaUtilConverter *Int32_To_Int8_Dither; */ + 0, /* PaUtilConverter *Int32_To_UInt8; */ + 0, /* PaUtilConverter *Int32_To_UInt8_Dither; */ + + 0, /* PaUtilConverter *Int24_To_Float32; */ + 0, /* PaUtilConverter *Int24_To_Int32; */ + 0, /* PaUtilConverter *Int24_To_Int16; */ + 0, /* PaUtilConverter *Int24_To_Int16_Dither; */ + 0, /* PaUtilConverter *Int24_To_Int8; */ + 0, /* PaUtilConverter *Int24_To_Int8_Dither; */ + 0, /* PaUtilConverter *Int24_To_UInt8; */ + 0, /* PaUtilConverter *Int24_To_UInt8_Dither; */ + + 0, /* PaUtilConverter *Int16_To_Float32; */ + 0, /* PaUtilConverter *Int16_To_Int32; */ + 0, /* PaUtilConverter *Int16_To_Int24; */ + 0, /* PaUtilConverter *Int16_To_Int8; */ + 0, /* PaUtilConverter *Int16_To_Int8_Dither; */ + 0, /* PaUtilConverter *Int16_To_UInt8; */ + 0, /* PaUtilConverter *Int16_To_UInt8_Dither; */ + + 0, /* PaUtilConverter *Int8_To_Float32; */ + 0, /* PaUtilConverter *Int8_To_Int32; */ + 0, /* PaUtilConverter *Int8_To_Int24 */ + 0, /* PaUtilConverter *Int8_To_Int16; */ + 0, /* PaUtilConverter *Int8_To_UInt8; */ + + 0, /* PaUtilConverter *UInt8_To_Float32; */ + 0, /* PaUtilConverter *UInt8_To_Int32; */ + 0, /* PaUtilConverter *UInt8_To_Int24; */ + 0, /* PaUtilConverter *UInt8_To_Int16; */ + 0, /* PaUtilConverter *UInt8_To_Int8; */ + + 0, /* PaUtilConverter *Copy_8_To_8; */ + 0, /* PaUtilConverter *Copy_16_To_16; */ + 0, /* PaUtilConverter *Copy_24_To_24; */ + 0 /* PaUtilConverter *Copy_32_To_32; */ +}; + +/* -------------------------------------------------------------------------- */ + +#else /* PA_NO_STANDARD_CONVERTERS is not defined */ + +/* -------------------------------------------------------------------------- */ + +#define PA_CLIP_( val, min, max )\ + { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } + + +static const float const_1_div_128_ = 1.0f / 128.0f; /* 8 bit multiplier */ + +static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */ + +static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */ + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* REVIEW */ +#ifdef PA_USE_C99_LRINTF + float scaled = *src * 0x7FFFFFFF; + *dest = lrintf(scaled-0.5f); +#else + double scaled = *src * 0x7FFFFFFF; + *dest = (signed long) scaled; +#endif + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + + while( count-- ) + { + /* REVIEW */ +#ifdef PA_USE_C99_LRINTF + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = ((float)*src * (2147483646.0f)) + dither; + *dest = lrintf(dithered - 0.5f); +#else + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + *dest = (signed long) dithered; +#endif + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* REVIEW */ +#ifdef PA_USE_C99_LRINTF + float scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648.f, 2147483647.f ); + *dest = lrintf(scaled-0.5f); +#else + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + *dest = (signed long) scaled; +#endif + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + + while( count-- ) + { + /* REVIEW */ +#ifdef PA_USE_C99_LRINTF + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = ((float)*src * (2147483646.0f)) + dither; + PA_CLIP_( dithered, -2147483648.f, 2147483647.f ); + *dest = lrintf(dithered-0.5f); +#else + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + *dest = (signed long) dithered; +#endif + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + double scaled = *src * 0x7FFFFFFF; + temp = (signed long) scaled; + +#if defined(PA_LITTLE_ENDIAN) + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + + temp = (signed long) dithered; + +#if defined(PA_LITTLE_ENDIAN) + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + temp = (signed long) scaled; + +#if defined(PA_LITTLE_ENDIAN) + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + + temp = (signed long) dithered; + +#if defined(PA_LITTLE_ENDIAN) + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { +#ifdef PA_USE_C99_LRINTF + float tempf = (*src * (32767.0f)) ; + *dest = lrintf(tempf-0.5f); +#else + short samp = (short) (*src * (32767.0f)); + *dest = samp; +#endif + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + + while( count-- ) + { + + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = (*src * (32766.0f)) + dither; + +#ifdef PA_USE_C99_LRINTF + *dest = lrintf(dithered-0.5f); +#else + *dest = (signed short) dithered; +#endif + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { +#ifdef PA_USE_C99_LRINTF + long samp = lrintf((*src * (32767.0f)) -0.5f); +#else + long samp = (signed long) (*src * (32767.0f)); +#endif + PA_CLIP_( samp, -0x8000, 0x7FFF ); + *dest = (signed short) samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = (*src * (32766.0f)) + dither; + signed long samp = (signed long) dithered; + PA_CLIP_( samp, -0x8000, 0x7FFF ); +#ifdef PA_USE_C99_LRINTF + *dest = lrintf(samp-0.5f); +#else + *dest = (signed short) samp; +#endif + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + signed char samp = (signed char) (*src * (127.0f)); + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = (*src * (126.0f)) + dither; + signed long samp = (signed long) dithered; + *dest = (signed char) samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int8_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + signed long samp = (signed long)(*src * (127.0f)); + PA_CLIP_( samp, -0x80, 0x7F ); + *dest = (signed char) samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int8_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + float dithered = (*src * (126.0f)) + dither; + signed long samp = (signed long) dithered; + PA_CLIP_( samp, -0x80, 0x7F ); + *dest = (signed char) samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_UInt8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f)))); + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_UInt8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* IMPLEMENT ME */ + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_UInt8_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* IMPLEMENT ME */ + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_UInt8_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* IMPLEMENT ME */ + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Float32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + float *dest = (float*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + *dest = (float) ((double)*src * const_1_div_2147483648_); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* REVIEW */ +#if defined(PA_LITTLE_ENDIAN) + dest[0] = (unsigned char)(*src >> 8); + dest[1] = (unsigned char)(*src >> 16); + dest[2] = (unsigned char)(*src >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (unsigned char)(*src >> 24); + dest[1] = (unsigned char)(*src >> 16); + dest[2] = (unsigned char)(*src >> 8); +#endif + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int24_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + (void) destinationBuffer; /* unused parameters */ + (void) destinationStride; /* unused parameters */ + (void) sourceBuffer; /* unused parameters */ + (void) sourceStride; /* unused parameters */ + (void) count; /* unused parameters */ + (void) ditherGenerator; /* unused parameters */ + /* IMPLEMENT ME */ +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + *dest = (signed short) ((*src) >> 16); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int16_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + signed long dither; + + while( count-- ) + { + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (signed short) ((((*src)>>1) + dither) >> 15); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + *dest = (signed char) ((*src) >> 24); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + signed long dither; + + while( count-- ) + { + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (signed char) ((((*src)>>1) + dither) >> 23); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_UInt8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (unsigned char)(((*src) >> 24) + 128); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_UInt8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed long *src = (signed long*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* IMPLEMENT ME */ + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Float32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + float *dest = (float*)destinationBuffer; + signed long temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((long)src[0]) << 8); + temp = temp | (((long)src[1]) << 16); + temp = temp | (((long)src[2]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((long)src[0]) << 24); + temp = temp | (((long)src[1]) << 16); + temp = temp | (((long)src[2]) << 8); +#endif + + *dest = (float) ((double)temp * const_1_div_2147483648_); + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed long *dest = (signed long*) destinationBuffer; + signed long temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((long)src[0]) << 8); + temp = temp | (((long)src[1]) << 16); + temp = temp | (((long)src[2]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((long)src[0]) << 24); + temp = temp | (((long)src[1]) << 16); + temp = temp | (((long)src[2]) << 8); +#endif + + *dest = temp; + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + + signed short temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[0] is discarded */ + temp = (((signed short)src[1])); + temp = temp | (signed short)(((signed short)src[2]) << 8); +#elif defined(PA_BIG_ENDIAN) + /* src[2] is discarded */ + temp = (signed short)(((signed short)src[0]) << 8); + temp = temp | (((signed short)src[1])); +#endif + + *dest = temp; + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int16_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + (void) destinationBuffer; /* unused parameters */ + (void) destinationStride; /* unused parameters */ + (void) sourceBuffer; /* unused parameters */ + (void) sourceStride; /* unused parameters */ + (void) count; /* unused parameters */ + (void) ditherGenerator; /* unused parameters */ + /* IMPLEMENT ME */ +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[0] is discarded */ + /* src[1] is discarded */ + *dest = src[2]; +#elif defined(PA_BIG_ENDIAN) + /* src[2] is discarded */ + /* src[1] is discarded */ + *dest = src[0]; +#endif + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + (void) destinationBuffer; /* unused parameters */ + (void) destinationStride; /* unused parameters */ + (void) sourceBuffer; /* unused parameters */ + (void) sourceStride; /* unused parameters */ + (void) count; /* unused parameters */ + (void) ditherGenerator; /* unused parameters */ + /* IMPLEMENT ME */ +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_UInt8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[0] is discarded */ + /* src[1] is discarded */ + *dest = (unsigned char)(src[2] + 128); +#elif defined(PA_BIG_ENDIAN) + *dest = (unsigned char)(src[0] + 128); + /* src[1] is discarded */ + /* src[2] is discarded */ +#endif + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_UInt8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + (void) destinationBuffer; /* unused parameters */ + (void) destinationStride; /* unused parameters */ + (void) sourceBuffer; /* unused parameters */ + (void) sourceStride; /* unused parameters */ + (void) count; /* unused parameters */ + (void) ditherGenerator; /* unused parameters */ + /* IMPLEMENT ME */ +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_Float32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed short *src = (signed short*)sourceBuffer; + float *dest = (float*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */ + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_Int32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed short *src = (signed short*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* REVIEW: we should consider something like + (*src << 16) | (*src & 0xFFFF) + */ + + *dest = *src << 16; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed short *src = (signed short*) sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed short temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + temp = *src; + +#if defined(PA_LITTLE_ENDIAN) + dest[0] = 0; + dest[1] = (unsigned char)(temp); + dest[2] = (unsigned char)(temp >> 8); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp); + dest[2] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_Int8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed short *src = (signed short*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (signed char)((*src) >> 8); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_Int8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed short *src = (signed short*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* IMPLEMENT ME */ + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_UInt8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed short *src = (signed short*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (unsigned char)(((*src) >> 8) + 128); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_UInt8_Dither( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed short *src = (signed short*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* IMPLEMENT ME */ + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int8_To_Float32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed char *src = (signed char*)sourceBuffer; + float *dest = (float*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + float samp = *src * const_1_div_128_; + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int8_To_Int32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed char *src = (signed char*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (*src) << 24; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int8_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed char *src = (signed char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + dest[0] = 0; + dest[1] = 0; + dest[2] = (*src); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (*src); + dest[1] = 0; + dest[2] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int8_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed char *src = (signed char*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (signed short)((*src) << 8); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int8_To_UInt8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed char *src = (signed char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (unsigned char)(*src + 128); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void UInt8_To_Float32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + float *dest = (float*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + float samp = (*src - 128) * const_1_div_128_; + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void UInt8_To_Int32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (*src - 128) << 24; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void UInt8_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void) ditherGenerator; /* unused parameters */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + dest[0] = 0; + dest[1] = 0; + dest[2] = (unsigned char)(*src - 128); +#elif defined(PA_BIG_ENDIAN) + dest[0] = (unsigned char)(*src - 128); + dest[1] = 0; + dest[2] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void UInt8_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (signed short)((*src - 128) << 8); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void UInt8_To_Int8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + (*dest) = (signed char)(*src - 128); + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Copy_8_To_8( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + *dest = *src; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Copy_16_To_16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + PaUint16 *src = (PaUint16 *)sourceBuffer; + PaUint16 *dest = (PaUint16 *)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + *dest = *src; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Copy_24_To_24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + + src += sourceStride * 3; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Copy_32_To_32( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + PaUint32 *dest = (PaUint32 *)destinationBuffer; + PaUint32 *src = (PaUint32 *)sourceBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + *dest = *src; + + src += sourceStride; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +PaUtilConverterTable paConverters = { + Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */ + Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */ + Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */ + Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */ + + Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */ + Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */ + Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */ + Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */ + + Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */ + Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */ + Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */ + Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */ + + Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */ + Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */ + Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */ + Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */ + + Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */ + Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */ + Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */ + Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */ + + Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */ + Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */ + Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */ + Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */ + Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */ + Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */ + Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */ + Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */ + Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */ + + Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */ + Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */ + Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */ + Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */ + Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */ + Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */ + Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */ + Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */ + + Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */ + Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */ + Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */ + Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */ + Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */ + Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */ + Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */ + + Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */ + Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */ + Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */ + Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */ + Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */ + + UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */ + UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */ + UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */ + UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */ + UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */ + + Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */ + Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */ + Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */ + Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */ +}; + +/* -------------------------------------------------------------------------- */ + +#endif /* PA_NO_STANDARD_CONVERTERS */ + +/* -------------------------------------------------------------------------- */ + +PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat ) +{ + switch( destinationFormat & ~paNonInterleaved ){ + case paFloat32: + return paZeroers.Zero32; + case paInt32: + return paZeroers.Zero32; + case paInt24: + return paZeroers.Zero24; + case paInt16: + return paZeroers.Zero16; + case paInt8: + return paZeroers.Zero8; + case paUInt8: + return paZeroers.ZeroU8; + default: return 0; + } +} + +/* -------------------------------------------------------------------------- */ + +#ifdef PA_NO_STANDARD_ZEROERS + +/* -------------------------------------------------------------------------- */ + +PaUtilZeroerTable paZeroers = { + 0, /* PaUtilZeroer *ZeroU8; */ + 0, /* PaUtilZeroer *Zero8; */ + 0, /* PaUtilZeroer *Zero16; */ + 0, /* PaUtilZeroer *Zero24; */ + 0, /* PaUtilZeroer *Zero32; */ +}; + +/* -------------------------------------------------------------------------- */ + +#else /* PA_NO_STANDARD_ZEROERS is not defined */ + +/* -------------------------------------------------------------------------- */ + +static void ZeroU8( void *destinationBuffer, signed int destinationStride, + unsigned int count ) +{ + unsigned char *dest = (unsigned char*)destinationBuffer; + + while( count-- ) + { + *dest = 128; + + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Zero8( void *destinationBuffer, signed int destinationStride, + unsigned int count ) +{ + unsigned char *dest = (unsigned char*)destinationBuffer; + + while( count-- ) + { + *dest = 0; + + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Zero16( void *destinationBuffer, signed int destinationStride, + unsigned int count ) +{ + PaUint16 *dest = (PaUint16 *)destinationBuffer; + + while( count-- ) + { + *dest = 0; + + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Zero24( void *destinationBuffer, signed int destinationStride, + unsigned int count ) +{ + unsigned char *dest = (unsigned char*)destinationBuffer; + + while( count-- ) + { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Zero32( void *destinationBuffer, signed int destinationStride, + unsigned int count ) +{ + PaUint32 *dest = (PaUint32 *)destinationBuffer; + + while( count-- ) + { + *dest = 0; + + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +PaUtilZeroerTable paZeroers = { + ZeroU8, /* PaUtilZeroer *ZeroU8; */ + Zero8, /* PaUtilZeroer *Zero8; */ + Zero16, /* PaUtilZeroer *Zero16; */ + Zero24, /* PaUtilZeroer *Zero24; */ + Zero32, /* PaUtilZeroer *Zero32; */ +}; + +/* -------------------------------------------------------------------------- */ + +#endif /* PA_NO_STANDARD_ZEROERS */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_converters.h b/pjmedia/src/pjmedia/portaudio/pa_converters.h index a328d9a9e..831c9c641 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_converters.h +++ b/pjmedia/src/pjmedia/portaudio/pa_converters.h @@ -1,254 +1,254 @@ -#ifndef PA_CONVERTERS_H -#define PA_CONVERTERS_H -/* - * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $ - * Portable Audio I/O Library sample conversion mechanism - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Conversion functions used to convert buffers of samples from one - format to another. -*/ - - -#include "portaudio.h" /* for PaSampleFormat */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -struct PaUtilTriangularDitherGenerator; - - -/** Choose an available sample format which is most appropriate for - representing the requested format. If the requested format is not available - higher quality formats are considered before lower quality formates. - @param availableFormats A variable containing the logical OR of all available - formats. - @param format The desired format. - @return The most appropriate available format for representing the requested - format. -*/ -PaSampleFormat PaUtil_SelectClosestAvailableFormat( - PaSampleFormat availableFormats, PaSampleFormat format ); - - -/* high level conversions functions for use by implementations */ - - -/** The generic sample converter prototype. Sample converters convert count - samples from sourceBuffer to destinationBuffer. The actual type of the data - pointed to by these parameters varys for different converter functions. - @param destinationBuffer A pointer to the first sample of the destination. - @param destinationStride An offset between successive destination samples - expressed in samples (not bytes.) It may be negative. - @param sourceBuffer A pointer to the first sample of the source. - @param sourceStride An offset between successive source samples - expressed in samples (not bytes.) It may be negative. - @param count The number of samples to convert. - @param ditherState State information used to calculate dither. Converters - that do not perform dithering will ignore this parameter, in which case - NULL or invalid dither state may be passed. -*/ -typedef void PaUtilConverter( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ); - - -/** Find a sample converter function for the given source and destinations - formats and flags (clip and dither.) - @return - A pointer to a PaUtilConverter which will perform the requested - conversion, or NULL if the given format conversion is not supported. - For conversions where clipping or dithering is not necessary, the - clip and dither flags are ignored and a non-clipping or dithering - version is returned. - If the source and destination formats are the same, a function which - copies data of the appropriate size will be returned. -*/ -PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat, - PaSampleFormat destinationFormat, PaStreamFlags flags ); - - -/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to - destinationBuffer. The actual type of the data pointed to varys for - different zeroer functions. - @param destinationBuffer A pointer to the first sample of the destination. - @param destinationStride An offset between successive destination samples - expressed in samples (not bytes.) It may be negative. - @param count The number of samples to zero. -*/ -typedef void PaUtilZeroer( - void *destinationBuffer, signed int destinationStride, unsigned int count ); - - -/** Find a buffer zeroer function for the given destination format. - @return - A pointer to a PaUtilZeroer which will perform the requested - zeroing. -*/ -PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat ); - -/*----------------------------------------------------------------------------*/ -/* low level functions and data structures which may be used for - substituting conversion functions */ - - -/** The type used to store all sample conversion functions. - @see paConverters; -*/ -typedef struct{ - PaUtilConverter *Float32_To_Int32; - PaUtilConverter *Float32_To_Int32_Dither; - PaUtilConverter *Float32_To_Int32_Clip; - PaUtilConverter *Float32_To_Int32_DitherClip; - - PaUtilConverter *Float32_To_Int24; - PaUtilConverter *Float32_To_Int24_Dither; - PaUtilConverter *Float32_To_Int24_Clip; - PaUtilConverter *Float32_To_Int24_DitherClip; - - PaUtilConverter *Float32_To_Int16; - PaUtilConverter *Float32_To_Int16_Dither; - PaUtilConverter *Float32_To_Int16_Clip; - PaUtilConverter *Float32_To_Int16_DitherClip; - - PaUtilConverter *Float32_To_Int8; - PaUtilConverter *Float32_To_Int8_Dither; - PaUtilConverter *Float32_To_Int8_Clip; - PaUtilConverter *Float32_To_Int8_DitherClip; - - PaUtilConverter *Float32_To_UInt8; - PaUtilConverter *Float32_To_UInt8_Dither; - PaUtilConverter *Float32_To_UInt8_Clip; - PaUtilConverter *Float32_To_UInt8_DitherClip; - - PaUtilConverter *Int32_To_Float32; - PaUtilConverter *Int32_To_Int24; - PaUtilConverter *Int32_To_Int24_Dither; - PaUtilConverter *Int32_To_Int16; - PaUtilConverter *Int32_To_Int16_Dither; - PaUtilConverter *Int32_To_Int8; - PaUtilConverter *Int32_To_Int8_Dither; - PaUtilConverter *Int32_To_UInt8; - PaUtilConverter *Int32_To_UInt8_Dither; - - PaUtilConverter *Int24_To_Float32; - PaUtilConverter *Int24_To_Int32; - PaUtilConverter *Int24_To_Int16; - PaUtilConverter *Int24_To_Int16_Dither; - PaUtilConverter *Int24_To_Int8; - PaUtilConverter *Int24_To_Int8_Dither; - PaUtilConverter *Int24_To_UInt8; - PaUtilConverter *Int24_To_UInt8_Dither; - - PaUtilConverter *Int16_To_Float32; - PaUtilConverter *Int16_To_Int32; - PaUtilConverter *Int16_To_Int24; - PaUtilConverter *Int16_To_Int8; - PaUtilConverter *Int16_To_Int8_Dither; - PaUtilConverter *Int16_To_UInt8; - PaUtilConverter *Int16_To_UInt8_Dither; - - PaUtilConverter *Int8_To_Float32; - PaUtilConverter *Int8_To_Int32; - PaUtilConverter *Int8_To_Int24; - PaUtilConverter *Int8_To_Int16; - PaUtilConverter *Int8_To_UInt8; - - PaUtilConverter *UInt8_To_Float32; - PaUtilConverter *UInt8_To_Int32; - PaUtilConverter *UInt8_To_Int24; - PaUtilConverter *UInt8_To_Int16; - PaUtilConverter *UInt8_To_Int8; - - PaUtilConverter *Copy_8_To_8; /* copy without any conversion */ - PaUtilConverter *Copy_16_To_16; /* copy without any conversion */ - PaUtilConverter *Copy_24_To_24; /* copy without any conversion */ - PaUtilConverter *Copy_32_To_32; /* copy without any conversion */ -} PaUtilConverterTable; - - -/** A table of pointers to all required converter functions. - PaUtil_SelectConverter() uses this table to lookup the appropriate - conversion functions. The fields of this structure are initialized - with default conversion functions. Fields may be NULL, indicating that - no conversion function is available. User code may substitue optimised - conversion functions by assigning different function pointers to - these fields. - - @note - If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined, - PortAudio's standard converters will not be compiled, and all fields - of this structure will be initialized to NULL. In such cases, users - should supply their own conversion functions if the require PortAudio - to open a stream that requires sample conversion. - - @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter -*/ -extern PaUtilConverterTable paConverters; - - -/** The type used to store all buffer zeroing functions. - @see paZeroers; -*/ -typedef struct{ - PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */ - PaUtilZeroer *Zero8; - PaUtilZeroer *Zero16; - PaUtilZeroer *Zero24; - PaUtilZeroer *Zero32; -} PaUtilZeroerTable; - - -/** A table of pointers to all required zeroer functions. - PaUtil_SelectZeroer() uses this table to lookup the appropriate - conversion functions. The fields of this structure are initialized - with default conversion functions. User code may substitue optimised - conversion functions by assigning different function pointers to - these fields. - - @note - If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined, - PortAudio's standard zeroers will not be compiled, and all fields - of this structure will be initialized to NULL. In such cases, users - should supply their own zeroing functions for the sample sizes which - they intend to use. - - @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer -*/ -extern PaUtilZeroerTable paZeroers; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_CONVERTERS_H */ +#ifndef PA_CONVERTERS_H +#define PA_CONVERTERS_H +/* + * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $ + * Portable Audio I/O Library sample conversion mechanism + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Conversion functions used to convert buffers of samples from one + format to another. +*/ + + +#include "portaudio.h" /* for PaSampleFormat */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +struct PaUtilTriangularDitherGenerator; + + +/** Choose an available sample format which is most appropriate for + representing the requested format. If the requested format is not available + higher quality formats are considered before lower quality formates. + @param availableFormats A variable containing the logical OR of all available + formats. + @param format The desired format. + @return The most appropriate available format for representing the requested + format. +*/ +PaSampleFormat PaUtil_SelectClosestAvailableFormat( + PaSampleFormat availableFormats, PaSampleFormat format ); + + +/* high level conversions functions for use by implementations */ + + +/** The generic sample converter prototype. Sample converters convert count + samples from sourceBuffer to destinationBuffer. The actual type of the data + pointed to by these parameters varys for different converter functions. + @param destinationBuffer A pointer to the first sample of the destination. + @param destinationStride An offset between successive destination samples + expressed in samples (not bytes.) It may be negative. + @param sourceBuffer A pointer to the first sample of the source. + @param sourceStride An offset between successive source samples + expressed in samples (not bytes.) It may be negative. + @param count The number of samples to convert. + @param ditherState State information used to calculate dither. Converters + that do not perform dithering will ignore this parameter, in which case + NULL or invalid dither state may be passed. +*/ +typedef void PaUtilConverter( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ); + + +/** Find a sample converter function for the given source and destinations + formats and flags (clip and dither.) + @return + A pointer to a PaUtilConverter which will perform the requested + conversion, or NULL if the given format conversion is not supported. + For conversions where clipping or dithering is not necessary, the + clip and dither flags are ignored and a non-clipping or dithering + version is returned. + If the source and destination formats are the same, a function which + copies data of the appropriate size will be returned. +*/ +PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat, + PaSampleFormat destinationFormat, PaStreamFlags flags ); + + +/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to + destinationBuffer. The actual type of the data pointed to varys for + different zeroer functions. + @param destinationBuffer A pointer to the first sample of the destination. + @param destinationStride An offset between successive destination samples + expressed in samples (not bytes.) It may be negative. + @param count The number of samples to zero. +*/ +typedef void PaUtilZeroer( + void *destinationBuffer, signed int destinationStride, unsigned int count ); + + +/** Find a buffer zeroer function for the given destination format. + @return + A pointer to a PaUtilZeroer which will perform the requested + zeroing. +*/ +PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat ); + +/*----------------------------------------------------------------------------*/ +/* low level functions and data structures which may be used for + substituting conversion functions */ + + +/** The type used to store all sample conversion functions. + @see paConverters; +*/ +typedef struct{ + PaUtilConverter *Float32_To_Int32; + PaUtilConverter *Float32_To_Int32_Dither; + PaUtilConverter *Float32_To_Int32_Clip; + PaUtilConverter *Float32_To_Int32_DitherClip; + + PaUtilConverter *Float32_To_Int24; + PaUtilConverter *Float32_To_Int24_Dither; + PaUtilConverter *Float32_To_Int24_Clip; + PaUtilConverter *Float32_To_Int24_DitherClip; + + PaUtilConverter *Float32_To_Int16; + PaUtilConverter *Float32_To_Int16_Dither; + PaUtilConverter *Float32_To_Int16_Clip; + PaUtilConverter *Float32_To_Int16_DitherClip; + + PaUtilConverter *Float32_To_Int8; + PaUtilConverter *Float32_To_Int8_Dither; + PaUtilConverter *Float32_To_Int8_Clip; + PaUtilConverter *Float32_To_Int8_DitherClip; + + PaUtilConverter *Float32_To_UInt8; + PaUtilConverter *Float32_To_UInt8_Dither; + PaUtilConverter *Float32_To_UInt8_Clip; + PaUtilConverter *Float32_To_UInt8_DitherClip; + + PaUtilConverter *Int32_To_Float32; + PaUtilConverter *Int32_To_Int24; + PaUtilConverter *Int32_To_Int24_Dither; + PaUtilConverter *Int32_To_Int16; + PaUtilConverter *Int32_To_Int16_Dither; + PaUtilConverter *Int32_To_Int8; + PaUtilConverter *Int32_To_Int8_Dither; + PaUtilConverter *Int32_To_UInt8; + PaUtilConverter *Int32_To_UInt8_Dither; + + PaUtilConverter *Int24_To_Float32; + PaUtilConverter *Int24_To_Int32; + PaUtilConverter *Int24_To_Int16; + PaUtilConverter *Int24_To_Int16_Dither; + PaUtilConverter *Int24_To_Int8; + PaUtilConverter *Int24_To_Int8_Dither; + PaUtilConverter *Int24_To_UInt8; + PaUtilConverter *Int24_To_UInt8_Dither; + + PaUtilConverter *Int16_To_Float32; + PaUtilConverter *Int16_To_Int32; + PaUtilConverter *Int16_To_Int24; + PaUtilConverter *Int16_To_Int8; + PaUtilConverter *Int16_To_Int8_Dither; + PaUtilConverter *Int16_To_UInt8; + PaUtilConverter *Int16_To_UInt8_Dither; + + PaUtilConverter *Int8_To_Float32; + PaUtilConverter *Int8_To_Int32; + PaUtilConverter *Int8_To_Int24; + PaUtilConverter *Int8_To_Int16; + PaUtilConverter *Int8_To_UInt8; + + PaUtilConverter *UInt8_To_Float32; + PaUtilConverter *UInt8_To_Int32; + PaUtilConverter *UInt8_To_Int24; + PaUtilConverter *UInt8_To_Int16; + PaUtilConverter *UInt8_To_Int8; + + PaUtilConverter *Copy_8_To_8; /* copy without any conversion */ + PaUtilConverter *Copy_16_To_16; /* copy without any conversion */ + PaUtilConverter *Copy_24_To_24; /* copy without any conversion */ + PaUtilConverter *Copy_32_To_32; /* copy without any conversion */ +} PaUtilConverterTable; + + +/** A table of pointers to all required converter functions. + PaUtil_SelectConverter() uses this table to lookup the appropriate + conversion functions. The fields of this structure are initialized + with default conversion functions. Fields may be NULL, indicating that + no conversion function is available. User code may substitue optimised + conversion functions by assigning different function pointers to + these fields. + + @note + If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined, + PortAudio's standard converters will not be compiled, and all fields + of this structure will be initialized to NULL. In such cases, users + should supply their own conversion functions if the require PortAudio + to open a stream that requires sample conversion. + + @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter +*/ +extern PaUtilConverterTable paConverters; + + +/** The type used to store all buffer zeroing functions. + @see paZeroers; +*/ +typedef struct{ + PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */ + PaUtilZeroer *Zero8; + PaUtilZeroer *Zero16; + PaUtilZeroer *Zero24; + PaUtilZeroer *Zero32; +} PaUtilZeroerTable; + + +/** A table of pointers to all required zeroer functions. + PaUtil_SelectZeroer() uses this table to lookup the appropriate + conversion functions. The fields of this structure are initialized + with default conversion functions. User code may substitue optimised + conversion functions by assigning different function pointers to + these fields. + + @note + If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined, + PortAudio's standard zeroers will not be compiled, and all fields + of this structure will be initialized to NULL. In such cases, users + should supply their own zeroing functions for the sample sizes which + they intend to use. + + @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer +*/ +extern PaUtilZeroerTable paZeroers; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_CONVERTERS_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_cpuload.c b/pjmedia/src/pjmedia/portaudio/pa_cpuload.c index f12ecbc7b..e70fbf4ec 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.c +++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.c @@ -1,96 +1,96 @@ -/* - * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $ - * Portable Audio I/O Library CPU Load measurement functions - * Portable CPU load measurement facility. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 2002 Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Functions to assist in measuring the CPU utilization of a callback - stream. Used to implement the Pa_GetStreamCpuLoad() function. - - @todo Dynamically calculate the coefficients used to smooth the CPU Load - Measurements over time to provide a uniform characterisation of CPU Load - independent of rate at which PaUtil_BeginCpuLoadMeasurement / - PaUtil_EndCpuLoadMeasurement are called. -*/ - - -#include "pa_cpuload.h" - -#include - -#include "pa_util.h" /* for PaUtil_GetTime() */ - - -void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ) -{ - assert( sampleRate > 0 ); - - measurer->samplingPeriod = 1. / sampleRate; - measurer->averageLoad = 0.; -} - -void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer ) -{ - measurer->averageLoad = 0.; -} - -void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ) -{ - measurer->measurementStartTime = PaUtil_GetTime(); -} - - -void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ) -{ - double measurementEndTime, secondsFor100Percent, measuredLoad; - - if( framesProcessed > 0 ){ - measurementEndTime = PaUtil_GetTime(); - - assert( framesProcessed > 0 ); - secondsFor100Percent = framesProcessed * measurer->samplingPeriod; - - measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent; - - /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */ - /** FIXME @todo these coefficients shouldn't be hardwired */ -#define LOWPASS_COEFFICIENT_0 (0.9) -#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) - - measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) + - (LOWPASS_COEFFICIENT_1 * measuredLoad); - } -} - - -double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ) -{ - return measurer->averageLoad; -} +/* + * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $ + * Portable Audio I/O Library CPU Load measurement functions + * Portable CPU load measurement facility. + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 2002 Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Functions to assist in measuring the CPU utilization of a callback + stream. Used to implement the Pa_GetStreamCpuLoad() function. + + @todo Dynamically calculate the coefficients used to smooth the CPU Load + Measurements over time to provide a uniform characterisation of CPU Load + independent of rate at which PaUtil_BeginCpuLoadMeasurement / + PaUtil_EndCpuLoadMeasurement are called. +*/ + + +#include "pa_cpuload.h" + +#include + +#include "pa_util.h" /* for PaUtil_GetTime() */ + + +void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ) +{ + assert( sampleRate > 0 ); + + measurer->samplingPeriod = 1. / sampleRate; + measurer->averageLoad = 0.; +} + +void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer ) +{ + measurer->averageLoad = 0.; +} + +void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ) +{ + measurer->measurementStartTime = PaUtil_GetTime(); +} + + +void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ) +{ + double measurementEndTime, secondsFor100Percent, measuredLoad; + + if( framesProcessed > 0 ){ + measurementEndTime = PaUtil_GetTime(); + + assert( framesProcessed > 0 ); + secondsFor100Percent = framesProcessed * measurer->samplingPeriod; + + measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent; + + /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */ + /** FIXME @todo these coefficients shouldn't be hardwired */ +#define LOWPASS_COEFFICIENT_0 (0.9) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + + measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) + + (LOWPASS_COEFFICIENT_1 * measuredLoad); + } +} + + +double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ) +{ + return measurer->averageLoad; +} diff --git a/pjmedia/src/pjmedia/portaudio/pa_cpuload.h b/pjmedia/src/pjmedia/portaudio/pa_cpuload.h index 61420e460..f77d91995 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.h +++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.h @@ -1,63 +1,63 @@ -#ifndef PA_CPULOAD_H -#define PA_CPULOAD_H -/* - * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 rossbencina Exp $ - * Portable Audio I/O Library CPU Load measurement functions - * Portable CPU load measurement facility. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 2002 Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Functions to assist in measuring the CPU utilization of a callback - stream. Used to implement the Pa_GetStreamCpuLoad() function. -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -typedef struct { - double samplingPeriod; - double measurementStartTime; - double averageLoad; -} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */ - -void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ); -void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ); -void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ); -void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer ); -double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_CPULOAD_H */ +#ifndef PA_CPULOAD_H +#define PA_CPULOAD_H +/* + * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 rossbencina Exp $ + * Portable Audio I/O Library CPU Load measurement functions + * Portable CPU load measurement facility. + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 2002 Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Functions to assist in measuring the CPU utilization of a callback + stream. Used to implement the Pa_GetStreamCpuLoad() function. +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct { + double samplingPeriod; + double measurementStartTime; + double averageLoad; +} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */ + +void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ); +void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ); +void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ); +void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer ); +double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_CPULOAD_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_dither.c b/pjmedia/src/pjmedia/portaudio/pa_dither.c index 5181a8b9b..0600db625 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_dither.c +++ b/pjmedia/src/pjmedia/portaudio/pa_dither.c @@ -1,204 +1,204 @@ -/* - * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $ - * Portable Audio I/O Library triangular dither generator - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Functions for generating dither noise -*/ - - -#include "pa_dither.h" -#include "pa_types.h" - -#define PA_DITHER_BITS_ (15) - - -void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state ) -{ - state->previous = 0; - state->randSeed1 = 22222; - state->randSeed2 = 5555555; -} - - -signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state ) -{ - signed long current, highPass; - - /* Generate two random numbers. */ - state->randSeed1 = (state->randSeed1 * 196314165) + 907633515; - state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; - - /* Generate triangular distribution about 0. - * Shift before adding to prevent overflow which would skew the distribution. - * Also shift an extra bit for the high pass filter. - */ -#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1) - current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + - (((signed long)state->randSeed2)>>DITHER_SHIFT_); - - /* High pass filter to reduce audibility. */ - highPass = current - state->previous; - state->previous = current; - return highPass; -} - - -/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ -#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<randSeed1 = (state->randSeed1 * 196314165) + 907633515; - state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; - - /* Generate triangular distribution about 0. - * Shift before adding to prevent overflow which would skew the distribution. - * Also shift an extra bit for the high pass filter. - */ -#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1) - current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + - (((signed long)state->randSeed2)>>DITHER_SHIFT_); - - /* High pass filter to reduce audibility. */ - highPass = current - state->previous; - state->previous = current; - return ((float)highPass) * const_float_dither_scale_; -} - - -/* -The following alternate dither algorithms (from musicdsp.org) could be -considered -*/ - -/*Noise shaped dither (March 2000) -------------------- - -This is a simple implementation of highpass triangular-PDF dither with -2nd-order noise shaping, for use when truncating floating point audio -data to fixed point. - -The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz -sample rate) compared to triangular-PDF dither. The code below assumes -input data is in the range +1 to -1 and doesn't check for overloads! - -To save time when generating dither for multiple channels you can do -things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again. - - - - int r1, r2; //rectangular-PDF random numbers - float s1, s2; //error feedback buffers - float s = 0.5f; //set to 0.0f for no noise shaping - float w = pow(2.0,bits-1); //word length (usually bits=16) - float wi= 1.0f/w; - float d = wi / RAND_MAX; //dither amplitude (2 lsb) - float o = wi * 0.5f; //remove dc offset - float in, tmp; - int out; - - -//for each sample... - - r2=r1; //can make HP-TRI dither by - r1=rand(); //subtracting previous rand() - - in += s * (s1 + s1 - s2); //error feedback - tmp = in + o + d * (float)(r1 - r2); //dc offset and dither - - out = (int)(w * tmp); //truncate downwards - if(tmp<0.0f) out--; //this is faster than floor() - - s2 = s1; - s1 = in - wi * (float)out; //error - - - --- -paul.kellett@maxim.abel.co.uk -http://www.maxim.abel.co.uk -*/ - - -/* -16-to-8-bit first-order dither - -Type : First order error feedforward dithering code -References : Posted by Jon Watte - -Notes : -This is about as simple a dithering algorithm as you can implement, but it's -likely to sound better than just truncating to N bits. - -Note that you might not want to carry forward the full difference for infinity. -It's probably likely that the worst performance hit comes from the saturation -conditionals, which can be avoided with appropriate instructions on many DSPs -and integer SIMD type instructions, or CMOV. - -Last, if sound quality is paramount (such as when going from > 16 bits to 16 -bits) you probably want to use a higher-order dither function found elsewhere -on this site. - - -Code : -// This code will down-convert and dither a 16-bit signed short -// mono signal into an 8-bit unsigned char signal, using a first -// order forward-feeding error term dither. - -#define uchar unsigned char - -void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory ) -{ - int m = *memory; - while( count-- > 0 ) { - int i = *input++; - i += m; - int j = i + 32768 - 128; - uchar o; - if( j < 0 ) { - o = 0; - } - else if( j > 65535 ) { - o = 255; - } - else { - o = (uchar)((j>>8)&0xff); - } - m = ((j-32768+128)-i); - *output++ = o; - } - *memory = m; -} -*/ +/* + * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $ + * Portable Audio I/O Library triangular dither generator + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Functions for generating dither noise +*/ + + +#include "pa_dither.h" +#include "pa_types.h" + +#define PA_DITHER_BITS_ (15) + + +void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state ) +{ + state->previous = 0; + state->randSeed1 = 22222; + state->randSeed2 = 5555555; +} + + +signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state ) +{ + signed long current, highPass; + + /* Generate two random numbers. */ + state->randSeed1 = (state->randSeed1 * 196314165) + 907633515; + state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; + + /* Generate triangular distribution about 0. + * Shift before adding to prevent overflow which would skew the distribution. + * Also shift an extra bit for the high pass filter. + */ +#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1) + current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + + (((signed long)state->randSeed2)>>DITHER_SHIFT_); + + /* High pass filter to reduce audibility. */ + highPass = current - state->previous; + state->previous = current; + return highPass; +} + + +/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ +#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<randSeed1 = (state->randSeed1 * 196314165) + 907633515; + state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; + + /* Generate triangular distribution about 0. + * Shift before adding to prevent overflow which would skew the distribution. + * Also shift an extra bit for the high pass filter. + */ +#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1) + current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + + (((signed long)state->randSeed2)>>DITHER_SHIFT_); + + /* High pass filter to reduce audibility. */ + highPass = current - state->previous; + state->previous = current; + return ((float)highPass) * const_float_dither_scale_; +} + + +/* +The following alternate dither algorithms (from musicdsp.org) could be +considered +*/ + +/*Noise shaped dither (March 2000) +------------------- + +This is a simple implementation of highpass triangular-PDF dither with +2nd-order noise shaping, for use when truncating floating point audio +data to fixed point. + +The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz +sample rate) compared to triangular-PDF dither. The code below assumes +input data is in the range +1 to -1 and doesn't check for overloads! + +To save time when generating dither for multiple channels you can do +things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again. + + + + int r1, r2; //rectangular-PDF random numbers + float s1, s2; //error feedback buffers + float s = 0.5f; //set to 0.0f for no noise shaping + float w = pow(2.0,bits-1); //word length (usually bits=16) + float wi= 1.0f/w; + float d = wi / RAND_MAX; //dither amplitude (2 lsb) + float o = wi * 0.5f; //remove dc offset + float in, tmp; + int out; + + +//for each sample... + + r2=r1; //can make HP-TRI dither by + r1=rand(); //subtracting previous rand() + + in += s * (s1 + s1 - s2); //error feedback + tmp = in + o + d * (float)(r1 - r2); //dc offset and dither + + out = (int)(w * tmp); //truncate downwards + if(tmp<0.0f) out--; //this is faster than floor() + + s2 = s1; + s1 = in - wi * (float)out; //error + + + +-- +paul.kellett@maxim.abel.co.uk +http://www.maxim.abel.co.uk +*/ + + +/* +16-to-8-bit first-order dither + +Type : First order error feedforward dithering code +References : Posted by Jon Watte + +Notes : +This is about as simple a dithering algorithm as you can implement, but it's +likely to sound better than just truncating to N bits. + +Note that you might not want to carry forward the full difference for infinity. +It's probably likely that the worst performance hit comes from the saturation +conditionals, which can be avoided with appropriate instructions on many DSPs +and integer SIMD type instructions, or CMOV. + +Last, if sound quality is paramount (such as when going from > 16 bits to 16 +bits) you probably want to use a higher-order dither function found elsewhere +on this site. + + +Code : +// This code will down-convert and dither a 16-bit signed short +// mono signal into an 8-bit unsigned char signal, using a first +// order forward-feeding error term dither. + +#define uchar unsigned char + +void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory ) +{ + int m = *memory; + while( count-- > 0 ) { + int i = *input++; + i += m; + int j = i + 32768 - 128; + uchar o; + if( j < 0 ) { + o = 0; + } + else if( j > 65535 ) { + o = 255; + } + else { + o = (uchar)((j>>8)&0xff); + } + m = ((j-32768+128)-i); + *output++ = o; + } + *memory = m; +} +*/ diff --git a/pjmedia/src/pjmedia/portaudio/pa_dither.h b/pjmedia/src/pjmedia/portaudio/pa_dither.h index a76c8b6b0..70369e187 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_dither.h +++ b/pjmedia/src/pjmedia/portaudio/pa_dither.h @@ -1,91 +1,91 @@ -#ifndef PA_DITHER_H -#define PA_DITHER_H -/* - * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 rossbencina Exp $ - * Portable Audio I/O Library triangular dither generator - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Functions for generating dither noise -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** @brief State needed to generate a dither signal */ -typedef struct PaUtilTriangularDitherGenerator{ - unsigned long previous; - unsigned long randSeed1; - unsigned long randSeed2; -} PaUtilTriangularDitherGenerator; - - -/** @brief Initialize dither state */ -void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState ); - - -/** - @brief Calculate 2 LSB dither signal with a triangular distribution. - Ranged for adding to a 1 bit right-shifted 32 bit integer - prior to >>15. eg: -
-    signed long in = *
-    signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
-    signed short out = (signed short)(((in>>1) + dither) >> 15);
-
- @return - A signed long with a range of +32767 to -32768 -*/ -signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); - - -/** - @brief Calculate 2 LSB dither signal with a triangular distribution. - Ranged for adding to a pre-scaled float. -
-    float in = *
-    float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
-    // use smaller scaler to prevent overflow when we add the dither
-    signed short out = (signed short)(in*(32766.0f) + dither );
-
- @return - A float with a range of -2.0 to +1.99999. -*/ -float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_DITHER_H */ +#ifndef PA_DITHER_H +#define PA_DITHER_H +/* + * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 rossbencina Exp $ + * Portable Audio I/O Library triangular dither generator + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Functions for generating dither noise +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** @brief State needed to generate a dither signal */ +typedef struct PaUtilTriangularDitherGenerator{ + unsigned long previous; + unsigned long randSeed1; + unsigned long randSeed2; +} PaUtilTriangularDitherGenerator; + + +/** @brief Initialize dither state */ +void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState ); + + +/** + @brief Calculate 2 LSB dither signal with a triangular distribution. + Ranged for adding to a 1 bit right-shifted 32 bit integer + prior to >>15. eg: +
+    signed long in = *
+    signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
+    signed short out = (signed short)(((in>>1) + dither) >> 15);
+
+ @return + A signed long with a range of +32767 to -32768 +*/ +signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); + + +/** + @brief Calculate 2 LSB dither signal with a triangular distribution. + Ranged for adding to a pre-scaled float. +
+    float in = *
+    float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
+    // use smaller scaler to prevent overflow when we add the dither
+    signed short out = (signed short)(in*(32766.0f) + dither );
+
+ @return + A float with a range of -2.0 to +1.99999. +*/ +float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_DITHER_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_endianness.h b/pjmedia/src/pjmedia/portaudio/pa_endianness.h index 6faf6ec24..052bced60 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_endianness.h +++ b/pjmedia/src/pjmedia/portaudio/pa_endianness.h @@ -1,111 +1,111 @@ -#ifndef PA_ENDIANNESS_H -#define PA_ENDIANNESS_H -/* - * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $ - * Portable Audio I/O Library current platform endianness macros - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Configure endianness symbols for the target processor. - - Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols - to be defined. The one that is defined reflects the endianness of the target - platform and may be used to implement conditional compilation of byte-order - dependent code. - - If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt - is made to override that setting. This may be useful if you have a better way - of determining the platform's endianness. The autoconf mechanism uses this for - example. - - A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time - and runtime endiannes and raise an assertion if they don't match. -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN) - /* endianness define has been set externally, such as by autoconf */ - - #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN) - #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please - #endif - -#else - /* endianness define has not been set externally */ - - /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */ - - #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) - - #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */ - - #else - -#endif - - #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN) - /* - If the following error is raised, you either need to modify the code above - to automatically determine the endianness from other symbols defined on your - platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally. - */ - #error pa_endianness.h was unable to automatically determine the endianness of the target platform - #endif - -#endif - -/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness, - and raises an assertion if they don't match. must be included in - the context in which this macro is used. -*/ -#if defined(PA_LITTLE_ENDIAN) - #define PA_VALIDATE_ENDIANNESS \ - { \ - const long nativeOne = 1; \ - assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \ - } -#elif defined(PA_BIG_ENDIAN) - #define PA_VALIDATE_ENDIANNESS \ - { \ - const long nativeOne = 1; \ - assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \ - } -#endif - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_ENDIANNESS_H */ +#ifndef PA_ENDIANNESS_H +#define PA_ENDIANNESS_H +/* + * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $ + * Portable Audio I/O Library current platform endianness macros + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Configure endianness symbols for the target processor. + + Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols + to be defined. The one that is defined reflects the endianness of the target + platform and may be used to implement conditional compilation of byte-order + dependent code. + + If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt + is made to override that setting. This may be useful if you have a better way + of determining the platform's endianness. The autoconf mechanism uses this for + example. + + A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time + and runtime endiannes and raise an assertion if they don't match. +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN) + /* endianness define has been set externally, such as by autoconf */ + + #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN) + #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please + #endif + +#else + /* endianness define has not been set externally */ + + /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */ + + #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + + #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */ + + #else + +#endif + + #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN) + /* + If the following error is raised, you either need to modify the code above + to automatically determine the endianness from other symbols defined on your + platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally. + */ + #error pa_endianness.h was unable to automatically determine the endianness of the target platform + #endif + +#endif + +/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness, + and raises an assertion if they don't match. must be included in + the context in which this macro is used. +*/ +#if defined(PA_LITTLE_ENDIAN) + #define PA_VALIDATE_ENDIANNESS \ + { \ + const long nativeOne = 1; \ + assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \ + } +#elif defined(PA_BIG_ENDIAN) + #define PA_VALIDATE_ENDIANNESS \ + { \ + const long nativeOne = 1; \ + assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \ + } +#endif + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_ENDIANNESS_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_front.c b/pjmedia/src/pjmedia/portaudio/pa_front.c index fb42b49af..3a267bbc0 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_front.c +++ b/pjmedia/src/pjmedia/portaudio/pa_front.c @@ -1,1976 +1,1976 @@ -/* - * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $ - * Portable Audio I/O Library Multi-Host API front end - * Validate function parameters and manage multiple host APIs. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/* doxygen index page */ -/** @mainpage - -PortAudio is an open-source cross-platform ‘C’ library for audio input -and output. It is designed to simplify the porting of audio applications -between various platforms, and also to simplify the development of audio -software in general by hiding the complexities of device interfacing. - -See the PortAudio website for further information http://www.portaudio.com/ - -This documentation pertains to PortAudio V19, API version 2.0 which is -currently under development. API version 2.0 differs in a number of ways from -previous versions, please consult the enhancement proposals for further details: -http://www.portaudio.com/docs/proposals/index.html - -This documentation is under construction. Things you might be interested in -include: - -- The PortAudio API 2.0, as documented in portaudio.h - -- The TODO List - -Feel free to pick an item off TODO list and fix/implement it. You may want to -enquire about status on the PortAudio mailing list first. -*/ - - -/** @file - @brief Implements public PortAudio API, checks some errors, forwards to - host API implementations. - - Implements the functions defined in the PortAudio API, checks for - some parameter and state inconsistencies and forwards API requests to - specific Host API implementations (via the interface declared in - pa_hostapi.h), and Streams (via the interface declared in pa_stream.h). - - This file handles initialization and termination of Host API - implementations via initializers stored in the paHostApiInitializers - global variable. - - Some utility functions declared in pa_util.h are implemented in this file. - - All PortAudio API functions can be conditionally compiled with logging code. - To compile with logging, define the PA_LOG_API_CALLS precompiler symbol. - - @todo Consider adding host API specific error text in Pa_GetErrorText() for - paUnanticipatedHostError - - @todo Consider adding a new error code for when (inputParameters == NULL) - && (outputParameters == NULL) - - @todo review whether Pa_CloseStream() should call the interface's - CloseStream function if aborting the stream returns an error code. - - @todo Create new error codes if a NULL buffer pointer, or a - zero frame count is passed to Pa_ReadStream or Pa_WriteStream. -*/ - - -#include -#include -#include -#include -#include /* needed by PA_VALIDATE_ENDIANNESS */ - -#include "portaudio.h" -#include "pa_util.h" -#include "pa_endianness.h" -#include "pa_types.h" -#include "pa_hostapi.h" -#include "pa_stream.h" - -#include "pa_trace.h" - - -#define PA_VERSION_ 1899 -#define PA_VERSION_TEXT_ "PortAudio V19-devel" - - - -/* #define PA_LOG_API_CALLS */ - -/* - The basic format for log messages is described below. If you need to - add any log messages, please follow this format. - - Function entry (void function): - - "FunctionName called.\n" - - Function entry (non void function): - - "FunctionName called:\n" - "\tParam1Type param1: param1Value\n" - "\tParam2Type param2: param2Value\n" (etc...) - - - Function exit (no return value): - - "FunctionName returned.\n" - - Function exit (simple return value): - - "FunctionName returned:\n" - "\tReturnType: returnValue\n\n" - - If the return type is an error code, the error text is displayed in () - - If the return type is not an error code, but has taken a special value - because an error occurred, then the reason for the error is shown in [] - - If the return type is a struct ptr, the struct is dumped. - - See the code below for examples -*/ - - -int Pa_GetVersion( void ) -{ - return PA_VERSION_; -} - - -const char* Pa_GetVersionText( void ) -{ - return PA_VERSION_TEXT_; -} - - - -#define PA_LAST_HOST_ERROR_TEXT_LENGTH_ 1024 - -static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0}; - -static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ }; - - -void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, - const char *errorText ) -{ - lastHostErrorInfo_.hostApiType = hostApiType; - lastHostErrorInfo_.errorCode = errorCode; - - strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ ); -} - - -void PaUtil_DebugPrint( const char *format, ... ) -{ - va_list ap; - - va_start( ap, format ); - vfprintf( stderr, format, ap ); - va_end( ap ); - - fflush( stderr ); -} - - -static PaUtilHostApiRepresentation **hostApis_ = 0; -static int hostApisCount_ = 0; -static int initializationCount_ = 0; -static int deviceCount_ = 0; - -PaUtilStreamRepresentation *firstOpenStream_ = NULL; - - -#define PA_IS_INITIALISED_ (initializationCount_ != 0) - - -static int CountHostApiInitializers( void ) -{ - int result = 0; - - while( paHostApiInitializers[ result ] != 0 ) - ++result; - return result; -} - - -static void TerminateHostApis( void ) -{ - /* terminate in reverse order from initialization */ - - while( hostApisCount_ > 0 ) - { - --hostApisCount_; - hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] ); - } - hostApisCount_ = 0; - deviceCount_ = 0; - - if( hostApis_ != 0 ) - PaUtil_FreeMemory( hostApis_ ); - hostApis_ = 0; -} - - -static PaError InitializeHostApis( void ) -{ - PaError result = paNoError; - int i, initializerCount, baseDeviceIndex; - - initializerCount = CountHostApiInitializers(); - - hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory( - sizeof(PaUtilHostApiRepresentation*) * initializerCount ); - if( !hostApis_ ) - { - result = paInsufficientMemory; - goto error; - } - - hostApisCount_ = 0; - deviceCount_ = 0; - baseDeviceIndex = 0; - - for( i=0; i< initializerCount; ++i ) - { - hostApis_[hostApisCount_] = NULL; - result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ ); - if( result != paNoError ) - goto error; - - if( hostApis_[hostApisCount_] ) - { - - hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex; - - if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice ) - hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex; - - if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice ) - hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex; - - baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount; - deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount; - - ++hostApisCount_; - } - } - - return result; - -error: - TerminateHostApis(); - return result; -} - - -/* - FindHostApi() finds the index of the host api to which - belongs and returns it. if is - non-null, the host specific device index is returned in it. - returns -1 if is out of range. - -*/ -static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex ) -{ - int i=0; - - if( !PA_IS_INITIALISED_ ) - return -1; - - if( device < 0 ) - return -1; - - while( i < hostApisCount_ - && device >= hostApis_[i]->info.deviceCount ) - { - - device -= hostApis_[i]->info.deviceCount; - ++i; - } - - if( i >= hostApisCount_ ) - return -1; - - if( hostSpecificDeviceIndex ) - *hostSpecificDeviceIndex = device; - - return i; -} - - -static void AddOpenStream( PaStream* stream ) -{ - ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_; - firstOpenStream_ = (PaUtilStreamRepresentation*)stream; -} - - -static void RemoveOpenStream( PaStream* stream ) -{ - PaUtilStreamRepresentation *previous = NULL; - PaUtilStreamRepresentation *current = firstOpenStream_; - - while( current != NULL ) - { - if( ((PaStream*)current) == stream ) - { - if( previous == NULL ) - { - firstOpenStream_ = current->nextOpenStream; - } - else - { - previous->nextOpenStream = current->nextOpenStream; - } - return; - } - else - { - previous = current; - current = current->nextOpenStream; - } - } -} - - -static void CloseOpenStreams( void ) -{ - /* we call Pa_CloseStream() here to ensure that the same destruction - logic is used for automatically closed streams */ - - while( firstOpenStream_ != NULL ) - Pa_CloseStream( firstOpenStream_ ); -} - - -PaError Pa_Initialize( void ) -{ - PaError result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint( "Pa_Initialize called.\n" ); -#endif - - if( PA_IS_INITIALISED_ ) - { - ++initializationCount_; - result = paNoError; - } - else - { - PA_VALIDATE_TYPE_SIZES; - PA_VALIDATE_ENDIANNESS; - - PaUtil_InitializeClock(); - PaUtil_ResetTraceMessages(); - - result = InitializeHostApis(); - if( result == paNoError ) - ++initializationCount_; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint( "Pa_Initialize returned:\n" ); - PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_Terminate( void ) -{ - PaError result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_Terminate called.\n" ); -#endif - - if( PA_IS_INITIALISED_ ) - { - if( --initializationCount_ == 0 ) - { - CloseOpenStreams(); - - TerminateHostApis(); - - PaUtil_DumpTraceMessages(); - } - result = paNoError; - } - else - { - result= paNotInitialized; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_Terminate returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ) -{ - return &lastHostErrorInfo_; -} - - -const char *Pa_GetErrorText( PaError errorCode ) -{ - const char *result; - - switch( errorCode ) - { - case paNoError: result = "Success"; break; - case paNotInitialized: result = "PortAudio not initialized"; break; - /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */ - case paUnanticipatedHostError: result = "Unanticipated host error"; break; - case paInvalidChannelCount: result = "Invalid number of channels"; break; - case paInvalidSampleRate: result = "Invalid sample rate"; break; - case paInvalidDevice: result = "Invalid device"; break; - case paInvalidFlag: result = "Invalid flag"; break; - case paSampleFormatNotSupported: result = "Sample format not supported"; break; - case paBadIODeviceCombination: result = "Illegal combination of I/O devices"; break; - case paInsufficientMemory: result = "Insufficient memory"; break; - case paBufferTooBig: result = "Buffer too big"; break; - case paBufferTooSmall: result = "Buffer too small"; break; - case paNullCallback: result = "No callback routine specified"; break; - case paBadStreamPtr: result = "Invalid stream pointer"; break; - case paTimedOut: result = "Wait timed out"; break; - case paInternalError: result = "Internal PortAudio error"; break; - case paDeviceUnavailable: result = "Device unavailable"; break; - case paIncompatibleHostApiSpecificStreamInfo: result = "Incompatible host API specific stream info"; break; - case paStreamIsStopped: result = "Stream is stopped"; break; - case paStreamIsNotStopped: result = "Stream is not stopped"; break; - case paInputOverflowed: result = "Input overflowed"; break; - case paOutputUnderflowed: result = "Output underflowed"; break; - case paHostApiNotFound: result = "Host API not found"; break; - case paInvalidHostApi: result = "Invalid host API"; break; - case paCanNotReadFromACallbackStream: result = "Can't read from a callback stream"; break; - case paCanNotWriteToACallbackStream: result = "Can't write to a callback stream"; break; - case paCanNotReadFromAnOutputOnlyStream: result = "Can't read from an output only stream"; break; - case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; break; - default: result = "Illegal error number"; break; - } - return result; -} - - -PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ) -{ - PaHostApiIndex result; - int i; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" ); - PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = paHostApiNotFound; - - for( i=0; i < hostApisCount_; ++i ) - { - if( hostApis_[i]->info.type == type ) - { - result = i; - break; - } - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result ); -#endif - - return result; -} - - -PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi, - PaHostApiTypeId type ) -{ - PaError result; - int i; - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = paHostApiNotFound; - - for( i=0; i < hostApisCount_; ++i ) - { - if( hostApis_[i]->info.type == type ) - { - *hostApi = hostApis_[i]; - result = paNoError; - break; - } - } - } - - return result; -} - - -PaError PaUtil_DeviceIndexToHostApiDeviceIndex( - PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi ) -{ - PaError result; - PaDeviceIndex x; - - x = device - hostApi->privatePaFrontInfo.baseDeviceIndex; - - if( x < 0 || x >= hostApi->info.deviceCount ) - { - result = paInvalidDevice; - } - else - { - *hostApiDevice = x; - result = paNoError; - } - - return result; -} - - -PaHostApiIndex Pa_GetHostApiCount( void ) -{ - int result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = hostApisCount_; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result ); -#endif - - return result; -} - - -PaHostApiIndex Pa_GetDefaultHostApi( void ) -{ - int result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = paDefaultHostApiIndex; - - /* internal consistency check: make sure that the default host api - index is within range */ - - if( result < 0 || result >= hostApisCount_ ) - { - result = paInternalError; - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result ); -#endif - - return result; -} - - -const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi ) -{ - PaHostApiInfo *info; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" ); - PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - info = NULL; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); - PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" ); -#endif - - } - else if( hostApi < 0 || hostApi >= hostApisCount_ ) - { - info = NULL; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); - PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" ); -#endif - - } - else - { - info = &hostApis_[hostApi]->info; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); - PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info ); - PaUtil_DebugPrint("\t{" ); - PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion ); - PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type ); - PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name ); - PaUtil_DebugPrint("\t}\n\n" ); -#endif - - } - - return info; -} - - -PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex ) -{ - PaDeviceIndex result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" ); - PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi ); - PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - if( hostApi < 0 || hostApi >= hostApisCount_ ) - { - result = paInvalidHostApi; - } - else - { - if( hostApiDeviceIndex < 0 || - hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount ) - { - result = paInvalidDevice; - } - else - { - result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex; - } - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); -#endif - - return result; -} - - -PaDeviceIndex Pa_GetDeviceCount( void ) -{ - PaDeviceIndex result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - } - else - { - result = deviceCount_; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" ); - if( result < 0 ) - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); - else - PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); -#endif - - return result; -} - - -PaDeviceIndex Pa_GetDefaultInputDevice( void ) -{ - PaHostApiIndex hostApi; - PaDeviceIndex result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" ); -#endif - - hostApi = Pa_GetDefaultHostApi(); - if( hostApi < 0 ) - { - result = paNoDevice; - } - else - { - result = hostApis_[hostApi]->info.defaultInputDevice; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" ); - PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); -#endif - - return result; -} - - -PaDeviceIndex Pa_GetDefaultOutputDevice( void ) -{ - PaHostApiIndex hostApi; - PaDeviceIndex result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" ); -#endif - - hostApi = Pa_GetDefaultHostApi(); - if( hostApi < 0 ) - { - result = paNoDevice; - } - else - { - result = hostApis_[hostApi]->info.defaultOutputDevice; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" ); - PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); -#endif - - return result; -} - - -const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ) -{ - int hostSpecificDeviceIndex; - int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex ); - PaDeviceInfo *result; - - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" ); - PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device ); -#endif - - if( hostApiIndex < 0 ) - { - result = NULL; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" ); - PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" ); -#endif - - } - else - { - result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ]; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" ); - PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result ); - PaUtil_DebugPrint("\t{\n" ); - - PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion ); - PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name ); - PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi ); - PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels ); - PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels ); - PaUtil_DebugPrint("\t}\n\n" ); -#endif - - } - - return result; -} - - -/* - SampleFormatIsValid() returns 1 if sampleFormat is a sample format - defined in portaudio.h, or 0 otherwise. -*/ -static int SampleFormatIsValid( PaSampleFormat format ) -{ - switch( format & ~paNonInterleaved ) - { - case paFloat32: return 1; - case paInt16: return 1; - case paInt32: return 1; - case paInt24: return 1; - case paInt8: return 1; - case paUInt8: return 1; - case paCustomFormat: return 1; - default: return 0; - } -} - -/* - NOTE: make sure this validation list is kept syncronised with the one in - pa_hostapi.h - - ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream() - conform to the expected values as described below. This function is - also designed to be used with the proposed Pa_IsFormatSupported() function. - - There are basically two types of validation that could be performed: - Generic conformance validation, and device capability mismatch - validation. This function performs only generic conformance validation. - Validation that would require knowledge of device capabilities is - not performed because of potentially complex relationships between - combinations of parameters - for example, even if the sampleRate - seems ok, it might not be for a duplex stream - we have no way of - checking this in an API-neutral way, so we don't try. - - On success the function returns PaNoError and fills in hostApi, - hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure - the function returns an error code indicating the first encountered - parameter error. - - - If ValidateOpenStreamParameters() returns paNoError, the following - assertions are guaranteed to be true. - - - at least one of inputParameters & outputParmeters is valid (not NULL) - - - if inputParameters & outputParmeters are both valid, that - inputParameters->device & outputParmeters->device both use the same host api - - PaDeviceIndex inputParameters->device - - is within range (0 to Pa_GetDeviceCount-1) Or: - - is paUseHostApiSpecificDeviceSpecification and - inputParameters->hostApiSpecificStreamInfo is non-NULL and refers - to a valid host api - - int inputParameters->channelCount - - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0 - - upper bound is NOT validated against device capabilities - - PaSampleFormat inputParameters->sampleFormat - - is one of the sample formats defined in portaudio.h - - void *inputParameters->hostApiSpecificStreamInfo - - if supplied its hostApi field matches the input device's host Api - - PaDeviceIndex outputParmeters->device - - is within range (0 to Pa_GetDeviceCount-1) - - int outputParmeters->channelCount - - if inputDevice is valid, channelCount is > 0 - - upper bound is NOT validated against device capabilities - - PaSampleFormat outputParmeters->sampleFormat - - is one of the sample formats defined in portaudio.h - - void *outputParmeters->hostApiSpecificStreamInfo - - if supplied its hostApi field matches the output device's host Api - - double sampleRate - - is not an 'absurd' rate (less than 1000. or greater than 200000.) - - sampleRate is NOT validated against device capabilities - - PaStreamFlags streamFlags - - unused platform neutral flags are zero - - paNeverDropInput is only used for full-duplex callback streams with - variable buffer size (paFramesPerBufferUnspecified) -*/ -static PaError ValidateOpenStreamParameters( - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - PaUtilHostApiRepresentation **hostApi, - PaDeviceIndex *hostApiInputDevice, - PaDeviceIndex *hostApiOutputDevice ) -{ - int inputHostApiIndex = -1, /* Surpress uninitialised var warnings: compiler does */ - outputHostApiIndex = -1; /* not see that if inputParameters and outputParame- */ - /* ters are both nonzero, these indices are set. */ - - if( (inputParameters == NULL) && (outputParameters == NULL) ) - { - return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */ - } - else - { - if( inputParameters == NULL ) - { - *hostApiInputDevice = paNoDevice; - } - else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - { - if( inputParameters->hostApiSpecificStreamInfo ) - { - inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( - ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType ); - - if( inputHostApiIndex != -1 ) - { - *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification; - *hostApi = hostApis_[inputHostApiIndex]; - } - else - { - return paInvalidDevice; - } - } - else - { - return paInvalidDevice; - } - } - else - { - if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ ) - return paInvalidDevice; - - inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice ); - if( inputHostApiIndex < 0 ) - return paInternalError; - - *hostApi = hostApis_[inputHostApiIndex]; - - if( inputParameters->channelCount <= 0 ) - return paInvalidChannelCount; - - if( !SampleFormatIsValid( inputParameters->sampleFormat ) ) - return paSampleFormatNotSupported; - - if( inputParameters->hostApiSpecificStreamInfo != NULL ) - { - if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType - != (*hostApi)->info.type ) - return paIncompatibleHostApiSpecificStreamInfo; - } - } - - if( outputParameters == NULL ) - { - *hostApiOutputDevice = paNoDevice; - } - else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - { - if( outputParameters->hostApiSpecificStreamInfo ) - { - outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( - ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType ); - - if( outputHostApiIndex != -1 ) - { - *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification; - *hostApi = hostApis_[outputHostApiIndex]; - } - else - { - return paInvalidDevice; - } - } - else - { - return paInvalidDevice; - } - } - else - { - if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ ) - return paInvalidDevice; - - outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice ); - if( outputHostApiIndex < 0 ) - return paInternalError; - - *hostApi = hostApis_[outputHostApiIndex]; - - if( outputParameters->channelCount <= 0 ) - return paInvalidChannelCount; - - if( !SampleFormatIsValid( outputParameters->sampleFormat ) ) - return paSampleFormatNotSupported; - - if( outputParameters->hostApiSpecificStreamInfo != NULL ) - { - if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType - != (*hostApi)->info.type ) - return paIncompatibleHostApiSpecificStreamInfo; - } - } - - if( (inputParameters != NULL) && (outputParameters != NULL) ) - { - /* ensure that both devices use the same API */ - if( inputHostApiIndex != outputHostApiIndex ) - return paBadIODeviceCombination; - } - } - - - /* Check for absurd sample rates. */ - if( (sampleRate < 1000.0) || (sampleRate > 200000.0) ) - return paInvalidSampleRate; - - if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 ) - return paInvalidFlag; - - if( streamFlags & paNeverDropInput ) - { - /* must be a callback stream */ - if( !streamCallback ) - return paInvalidFlag; - - /* must be a full duplex stream */ - if( (inputParameters == NULL) || (outputParameters == NULL) ) - return paInvalidFlag; - - /* must use paFramesPerBufferUnspecified */ - if( framesPerBuffer != paFramesPerBufferUnspecified ) - return paInvalidFlag; - } - - return paNoError; -} - - -PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaError result; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiInputDevice, hostApiOutputDevice; - PaStreamParameters hostApiInputParameters, hostApiOutputParameters; - PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; - - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" ); - - if( inputParameters == NULL ){ - PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters ); - PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device ); - PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount ); - PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat ); - PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency ); - PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo ); - } - - if( outputParameters == NULL ){ - PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters ); - PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device ); - PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount ); - PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat ); - PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency ); - PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo ); - } - - PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - return result; - } - - result = ValidateOpenStreamParameters( inputParameters, - outputParameters, - sampleRate, 0, paNoFlag, 0, - &hostApi, - &hostApiInputDevice, - &hostApiOutputDevice ); - if( result != paNoError ) - { -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - return result; - } - - - if( inputParameters ) - { - hostApiInputParameters.device = hostApiInputDevice; - hostApiInputParameters.channelCount = inputParameters->channelCount; - hostApiInputParameters.sampleFormat = inputParameters->sampleFormat; - hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency; - hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo; - hostApiInputParametersPtr = &hostApiInputParameters; - } - else - { - hostApiInputParametersPtr = NULL; - } - - if( outputParameters ) - { - hostApiOutputParameters.device = hostApiOutputDevice; - hostApiOutputParameters.channelCount = outputParameters->channelCount; - hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat; - hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency; - hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo; - hostApiOutputParametersPtr = &hostApiOutputParameters; - } - else - { - hostApiOutputParametersPtr = NULL; - } - - result = hostApi->IsFormatSupported( hostApi, - hostApiInputParametersPtr, hostApiOutputParametersPtr, - sampleRate ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); - if( result == paFormatIsSupported ) - PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" ); - else - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_OpenStream( PaStream** stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiInputDevice, hostApiOutputDevice; - PaStreamParameters hostApiInputParameters, hostApiOutputParameters; - PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; - - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenStream called:\n" ); - PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream ); - - if( inputParameters == NULL ){ - PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters ); - PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device ); - PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount ); - PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat ); - PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency ); - PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo ); - } - - if( outputParameters == NULL ){ - PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" ); - }else{ - PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters ); - PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device ); - PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount ); - PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat ); - PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency ); - PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo ); - } - - PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); - PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer ); - PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags ); - PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback ); - PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData ); -#endif - - if( !PA_IS_INITIALISED_ ) - { - result = paNotInitialized; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); - PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - return result; - } - - /* Check for parameter errors. - NOTE: make sure this validation list is kept syncronised with the one - in pa_hostapi.h - */ - - if( stream == NULL ) - { - result = paBadStreamPtr; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); - PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - return result; - } - - result = ValidateOpenStreamParameters( inputParameters, - outputParameters, - sampleRate, framesPerBuffer, - streamFlags, streamCallback, - &hostApi, - &hostApiInputDevice, - &hostApiOutputDevice ); - if( result != paNoError ) - { -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); - PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - return result; - } - - - if( inputParameters ) - { - hostApiInputParameters.device = hostApiInputDevice; - hostApiInputParameters.channelCount = inputParameters->channelCount; - hostApiInputParameters.sampleFormat = inputParameters->sampleFormat; - hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency; - hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo; - hostApiInputParametersPtr = &hostApiInputParameters; - } - else - { - hostApiInputParametersPtr = NULL; - } - - if( outputParameters ) - { - hostApiOutputParameters.device = hostApiOutputDevice; - hostApiOutputParameters.channelCount = outputParameters->channelCount; - hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat; - hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency; - hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo; - hostApiOutputParametersPtr = &hostApiOutputParameters; - } - else - { - hostApiOutputParametersPtr = NULL; - } - - result = hostApi->OpenStream( hostApi, stream, - hostApiInputParametersPtr, hostApiOutputParametersPtr, - sampleRate, framesPerBuffer, streamFlags, streamCallback, userData ); - - if( result == paNoError ) - AddOpenStream( *stream ); - - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); - PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_OpenDefaultStream( PaStream** stream, - int inputChannelCount, - int outputChannelCount, - PaSampleFormat sampleFormat, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result; - PaStreamParameters hostApiInputParameters, hostApiOutputParameters; - PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" ); - PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream ); - PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount ); - PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount ); - PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat ); - PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); - PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer ); - PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback ); - PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData ); -#endif - - - if( inputChannelCount > 0 ) - { - hostApiInputParameters.device = Pa_GetDefaultInputDevice(); - hostApiInputParameters.channelCount = inputChannelCount; - hostApiInputParameters.sampleFormat = sampleFormat; - /* defaultHighInputLatency is used below instead of - defaultLowInputLatency because it is more important for the default - stream to work reliably than it is for it to work with the lowest - latency. - */ - hostApiInputParameters.suggestedLatency = - Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency; - hostApiInputParameters.hostApiSpecificStreamInfo = NULL; - hostApiInputParametersPtr = &hostApiInputParameters; - } - else - { - hostApiInputParametersPtr = NULL; - } - - if( outputChannelCount > 0 ) - { - hostApiOutputParameters.device = Pa_GetDefaultOutputDevice(); - hostApiOutputParameters.channelCount = outputChannelCount; - hostApiOutputParameters.sampleFormat = sampleFormat; - /* defaultHighOutputLatency is used below instead of - defaultLowOutputLatency because it is more important for the default - stream to work reliably than it is for it to work with the lowest - latency. - */ - hostApiOutputParameters.suggestedLatency = - Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency; - hostApiOutputParameters.hostApiSpecificStreamInfo = NULL; - hostApiOutputParametersPtr = &hostApiOutputParameters; - } - else - { - hostApiOutputParametersPtr = NULL; - } - - - result = Pa_OpenStream( - stream, hostApiInputParametersPtr, hostApiOutputParametersPtr, - sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" ); - PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError PaUtil_ValidateStreamPointer( PaStream* stream ) -{ - if( !PA_IS_INITIALISED_ ) return paNotInitialized; - - if( stream == NULL ) return paBadStreamPtr; - - if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC ) - return paBadStreamPtr; - - return paNoError; -} - - -PaError Pa_CloseStream( PaStream* stream ) -{ - PaUtilStreamInterface *interface; - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_CloseStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - /* always remove the open stream from our list, even if this function - eventually returns an error. Otherwise CloseOpenStreams() will - get stuck in an infinite loop */ - RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */ - - if( result == paNoError ) - { - interface = PA_STREAM_INTERFACE(stream); - - /* abort the stream if it isn't stopped */ - result = interface->IsStopped( stream ); - if( result == 1 ) - result = paNoError; - else if( result == 0 ) - result = interface->Abort( stream ); - - if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? */ - result = interface->Close( stream ); - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_CloseStream returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); - PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback ); -#endif - - if( result == paNoError ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = paStreamIsNotStopped ; - } - if( result == 1 ) - { - PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback; - result = paNoError; - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; - -} - - -PaError Pa_StartStream( PaStream *stream ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_StartStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = paStreamIsNotStopped ; - } - else if( result == 1 ) - { - result = PA_STREAM_INTERFACE(stream)->Start( stream ); - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_StartStream returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_StopStream( PaStream *stream ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_StopStream called\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Stop( stream ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_StopStream returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_AbortStream( PaStream *stream ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_AbortStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Abort( stream ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_AbortStream returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_IsStreamStopped( PaStream *stream ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_IsStreamActive( PaStream *stream ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_IsStreamActive called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - result = PA_STREAM_INTERFACE(stream)->IsActive( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ) -{ - PaError error = PaUtil_ValidateStreamPointer( stream ); - const PaStreamInfo *result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( error != paNoError ) - { - result = 0; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" ); - PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) ); -#endif - - } - else - { - result = &PA_STREAM_REP( stream )->streamInfo; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" ); - PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result ); - PaUtil_DebugPrint("\t{" ); - - PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion ); - PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency ); - PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency ); - PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate ); - PaUtil_DebugPrint("\t}\n\n" ); -#endif - - } - - return result; -} - - -PaTime Pa_GetStreamTime( PaStream *stream ) -{ - PaError error = PaUtil_ValidateStreamPointer( stream ); - PaTime result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamTime called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( error != paNoError ) - { - result = 0; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" ); - PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) ); -#endif - - } - else - { - result = PA_STREAM_INTERFACE(stream)->GetTime( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" ); - PaUtil_DebugPrint("\tPaTime: %g\n\n", result ); -#endif - - } - - return result; -} - - -double Pa_GetStreamCpuLoad( PaStream* stream ) -{ - PaError error = PaUtil_ValidateStreamPointer( stream ); - double result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( error != paNoError ) - { - - result = 0.0; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" ); - PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) ); -#endif - - } - else - { - result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" ); - PaUtil_DebugPrint("\tdouble: %g\n\n", result ); -#endif - - } - - return result; -} - - -PaError Pa_ReadStream( PaStream* stream, - void *buffer, - unsigned long frames ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_ReadStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - if( frames == 0 ) - { - result = paInternalError; /** @todo should return a different error code */ - } - else if( buffer == 0 ) - { - result = paInternalError; /** @todo should return a different error code */ - } - else - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_ReadStream returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - - -PaError Pa_WriteStream( PaStream* stream, - const void *buffer, - unsigned long frames ) -{ - PaError result = PaUtil_ValidateStreamPointer( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_WriteStream called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( result == paNoError ) - { - if( frames == 0 ) - { - result = paInternalError; /** @todo should return a different error code */ - } - else if( buffer == 0 ) - { - result = paInternalError; /** @todo should return a different error code */ - } - else - { - result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); - if( result == 0 ) - { - result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames ); - } - else if( result == 1 ) - { - result = paStreamIsStopped; - } - } - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_WriteStream returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return result; -} - -signed long Pa_GetStreamReadAvailable( PaStream* stream ) -{ - PaError error = PaUtil_ValidateStreamPointer( stream ); - signed long result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( error != paNoError ) - { - result = 0; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" ); - PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) ); -#endif - - } - else - { - result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - } - - return result; -} - - -signed long Pa_GetStreamWriteAvailable( PaStream* stream ) -{ - PaError error = PaUtil_ValidateStreamPointer( stream ); - signed long result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" ); - PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); -#endif - - if( error != paNoError ) - { - result = 0; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" ); - PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) ); -#endif - - } - else - { - result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream ); - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" ); - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - } - - return result; -} - - -PaError Pa_GetSampleSize( PaSampleFormat format ) -{ - int result; - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetSampleSize called:\n" ); - PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format ); -#endif - - switch( format & ~paNonInterleaved ) - { - - case paUInt8: - case paInt8: - result = 1; - break; - - case paInt16: - result = 2; - break; - - case paInt24: - result = 3; - break; - - case paFloat32: - case paInt32: - result = 4; - break; - - default: - result = paSampleFormatNotSupported; - break; - } - -#ifdef PA_LOG_API_CALLS - PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" ); - if( result > 0 ) - PaUtil_DebugPrint("\tint: %d\n\n", result ); - else - PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); -#endif - - return (PaError) result; -} - +/* + * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $ + * Portable Audio I/O Library Multi-Host API front end + * Validate function parameters and manage multiple host APIs. + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/* doxygen index page */ +/** @mainpage + +PortAudio is an open-source cross-platform †CË library for audio input +and output. It is designed to simplify the porting of audio applications +between various platforms, and also to simplify the development of audio +software in general by hiding the complexities of device interfacing. + +See the PortAudio website for further information http://www.portaudio.com/ + +This documentation pertains to PortAudio V19, API version 2.0 which is +currently under development. API version 2.0 differs in a number of ways from +previous versions, please consult the enhancement proposals for further details: +http://www.portaudio.com/docs/proposals/index.html + +This documentation is under construction. Things you might be interested in +include: + +- The PortAudio API 2.0, as documented in portaudio.h + +- The TODO List + +Feel free to pick an item off TODO list and fix/implement it. You may want to +enquire about status on the PortAudio mailing list first. +*/ + + +/** @file + @brief Implements public PortAudio API, checks some errors, forwards to + host API implementations. + + Implements the functions defined in the PortAudio API, checks for + some parameter and state inconsistencies and forwards API requests to + specific Host API implementations (via the interface declared in + pa_hostapi.h), and Streams (via the interface declared in pa_stream.h). + + This file handles initialization and termination of Host API + implementations via initializers stored in the paHostApiInitializers + global variable. + + Some utility functions declared in pa_util.h are implemented in this file. + + All PortAudio API functions can be conditionally compiled with logging code. + To compile with logging, define the PA_LOG_API_CALLS precompiler symbol. + + @todo Consider adding host API specific error text in Pa_GetErrorText() for + paUnanticipatedHostError + + @todo Consider adding a new error code for when (inputParameters == NULL) + && (outputParameters == NULL) + + @todo review whether Pa_CloseStream() should call the interface's + CloseStream function if aborting the stream returns an error code. + + @todo Create new error codes if a NULL buffer pointer, or a + zero frame count is passed to Pa_ReadStream or Pa_WriteStream. +*/ + + +#include +#include +#include +#include +#include /* needed by PA_VALIDATE_ENDIANNESS */ + +#include "portaudio.h" +#include "pa_util.h" +#include "pa_endianness.h" +#include "pa_types.h" +#include "pa_hostapi.h" +#include "pa_stream.h" + +#include "pa_trace.h" + + +#define PA_VERSION_ 1899 +#define PA_VERSION_TEXT_ "PortAudio V19-devel" + + + +/* #define PA_LOG_API_CALLS */ + +/* + The basic format for log messages is described below. If you need to + add any log messages, please follow this format. + + Function entry (void function): + + "FunctionName called.\n" + + Function entry (non void function): + + "FunctionName called:\n" + "\tParam1Type param1: param1Value\n" + "\tParam2Type param2: param2Value\n" (etc...) + + + Function exit (no return value): + + "FunctionName returned.\n" + + Function exit (simple return value): + + "FunctionName returned:\n" + "\tReturnType: returnValue\n\n" + + If the return type is an error code, the error text is displayed in () + + If the return type is not an error code, but has taken a special value + because an error occurred, then the reason for the error is shown in [] + + If the return type is a struct ptr, the struct is dumped. + + See the code below for examples +*/ + + +int Pa_GetVersion( void ) +{ + return PA_VERSION_; +} + + +const char* Pa_GetVersionText( void ) +{ + return PA_VERSION_TEXT_; +} + + + +#define PA_LAST_HOST_ERROR_TEXT_LENGTH_ 1024 + +static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0}; + +static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ }; + + +void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, + const char *errorText ) +{ + lastHostErrorInfo_.hostApiType = hostApiType; + lastHostErrorInfo_.errorCode = errorCode; + + strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ ); +} + + +void PaUtil_DebugPrint( const char *format, ... ) +{ + va_list ap; + + va_start( ap, format ); + vfprintf( stderr, format, ap ); + va_end( ap ); + + fflush( stderr ); +} + + +static PaUtilHostApiRepresentation **hostApis_ = 0; +static int hostApisCount_ = 0; +static int initializationCount_ = 0; +static int deviceCount_ = 0; + +PaUtilStreamRepresentation *firstOpenStream_ = NULL; + + +#define PA_IS_INITIALISED_ (initializationCount_ != 0) + + +static int CountHostApiInitializers( void ) +{ + int result = 0; + + while( paHostApiInitializers[ result ] != 0 ) + ++result; + return result; +} + + +static void TerminateHostApis( void ) +{ + /* terminate in reverse order from initialization */ + + while( hostApisCount_ > 0 ) + { + --hostApisCount_; + hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] ); + } + hostApisCount_ = 0; + deviceCount_ = 0; + + if( hostApis_ != 0 ) + PaUtil_FreeMemory( hostApis_ ); + hostApis_ = 0; +} + + +static PaError InitializeHostApis( void ) +{ + PaError result = paNoError; + int i, initializerCount, baseDeviceIndex; + + initializerCount = CountHostApiInitializers(); + + hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory( + sizeof(PaUtilHostApiRepresentation*) * initializerCount ); + if( !hostApis_ ) + { + result = paInsufficientMemory; + goto error; + } + + hostApisCount_ = 0; + deviceCount_ = 0; + baseDeviceIndex = 0; + + for( i=0; i< initializerCount; ++i ) + { + hostApis_[hostApisCount_] = NULL; + result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ ); + if( result != paNoError ) + goto error; + + if( hostApis_[hostApisCount_] ) + { + + hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex; + + if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice ) + hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex; + + if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice ) + hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex; + + baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount; + deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount; + + ++hostApisCount_; + } + } + + return result; + +error: + TerminateHostApis(); + return result; +} + + +/* + FindHostApi() finds the index of the host api to which + belongs and returns it. if is + non-null, the host specific device index is returned in it. + returns -1 if is out of range. + +*/ +static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex ) +{ + int i=0; + + if( !PA_IS_INITIALISED_ ) + return -1; + + if( device < 0 ) + return -1; + + while( i < hostApisCount_ + && device >= hostApis_[i]->info.deviceCount ) + { + + device -= hostApis_[i]->info.deviceCount; + ++i; + } + + if( i >= hostApisCount_ ) + return -1; + + if( hostSpecificDeviceIndex ) + *hostSpecificDeviceIndex = device; + + return i; +} + + +static void AddOpenStream( PaStream* stream ) +{ + ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_; + firstOpenStream_ = (PaUtilStreamRepresentation*)stream; +} + + +static void RemoveOpenStream( PaStream* stream ) +{ + PaUtilStreamRepresentation *previous = NULL; + PaUtilStreamRepresentation *current = firstOpenStream_; + + while( current != NULL ) + { + if( ((PaStream*)current) == stream ) + { + if( previous == NULL ) + { + firstOpenStream_ = current->nextOpenStream; + } + else + { + previous->nextOpenStream = current->nextOpenStream; + } + return; + } + else + { + previous = current; + current = current->nextOpenStream; + } + } +} + + +static void CloseOpenStreams( void ) +{ + /* we call Pa_CloseStream() here to ensure that the same destruction + logic is used for automatically closed streams */ + + while( firstOpenStream_ != NULL ) + Pa_CloseStream( firstOpenStream_ ); +} + + +PaError Pa_Initialize( void ) +{ + PaError result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint( "Pa_Initialize called.\n" ); +#endif + + if( PA_IS_INITIALISED_ ) + { + ++initializationCount_; + result = paNoError; + } + else + { + PA_VALIDATE_TYPE_SIZES; + PA_VALIDATE_ENDIANNESS; + + PaUtil_InitializeClock(); + PaUtil_ResetTraceMessages(); + + result = InitializeHostApis(); + if( result == paNoError ) + ++initializationCount_; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint( "Pa_Initialize returned:\n" ); + PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_Terminate( void ) +{ + PaError result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_Terminate called.\n" ); +#endif + + if( PA_IS_INITIALISED_ ) + { + if( --initializationCount_ == 0 ) + { + CloseOpenStreams(); + + TerminateHostApis(); + + PaUtil_DumpTraceMessages(); + } + result = paNoError; + } + else + { + result= paNotInitialized; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_Terminate returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ) +{ + return &lastHostErrorInfo_; +} + + +const char *Pa_GetErrorText( PaError errorCode ) +{ + const char *result; + + switch( errorCode ) + { + case paNoError: result = "Success"; break; + case paNotInitialized: result = "PortAudio not initialized"; break; + /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */ + case paUnanticipatedHostError: result = "Unanticipated host error"; break; + case paInvalidChannelCount: result = "Invalid number of channels"; break; + case paInvalidSampleRate: result = "Invalid sample rate"; break; + case paInvalidDevice: result = "Invalid device"; break; + case paInvalidFlag: result = "Invalid flag"; break; + case paSampleFormatNotSupported: result = "Sample format not supported"; break; + case paBadIODeviceCombination: result = "Illegal combination of I/O devices"; break; + case paInsufficientMemory: result = "Insufficient memory"; break; + case paBufferTooBig: result = "Buffer too big"; break; + case paBufferTooSmall: result = "Buffer too small"; break; + case paNullCallback: result = "No callback routine specified"; break; + case paBadStreamPtr: result = "Invalid stream pointer"; break; + case paTimedOut: result = "Wait timed out"; break; + case paInternalError: result = "Internal PortAudio error"; break; + case paDeviceUnavailable: result = "Device unavailable"; break; + case paIncompatibleHostApiSpecificStreamInfo: result = "Incompatible host API specific stream info"; break; + case paStreamIsStopped: result = "Stream is stopped"; break; + case paStreamIsNotStopped: result = "Stream is not stopped"; break; + case paInputOverflowed: result = "Input overflowed"; break; + case paOutputUnderflowed: result = "Output underflowed"; break; + case paHostApiNotFound: result = "Host API not found"; break; + case paInvalidHostApi: result = "Invalid host API"; break; + case paCanNotReadFromACallbackStream: result = "Can't read from a callback stream"; break; + case paCanNotWriteToACallbackStream: result = "Can't write to a callback stream"; break; + case paCanNotReadFromAnOutputOnlyStream: result = "Can't read from an output only stream"; break; + case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; break; + default: result = "Illegal error number"; break; + } + return result; +} + + +PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ) +{ + PaHostApiIndex result; + int i; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" ); + PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + } + else + { + result = paHostApiNotFound; + + for( i=0; i < hostApisCount_; ++i ) + { + if( hostApis_[i]->info.type == type ) + { + result = i; + break; + } + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" ); + if( result < 0 ) + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); + else + PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result ); +#endif + + return result; +} + + +PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi, + PaHostApiTypeId type ) +{ + PaError result; + int i; + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + } + else + { + result = paHostApiNotFound; + + for( i=0; i < hostApisCount_; ++i ) + { + if( hostApis_[i]->info.type == type ) + { + *hostApi = hostApis_[i]; + result = paNoError; + break; + } + } + } + + return result; +} + + +PaError PaUtil_DeviceIndexToHostApiDeviceIndex( + PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi ) +{ + PaError result; + PaDeviceIndex x; + + x = device - hostApi->privatePaFrontInfo.baseDeviceIndex; + + if( x < 0 || x >= hostApi->info.deviceCount ) + { + result = paInvalidDevice; + } + else + { + *hostApiDevice = x; + result = paNoError; + } + + return result; +} + + +PaHostApiIndex Pa_GetHostApiCount( void ) +{ + int result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + } + else + { + result = hostApisCount_; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" ); + if( result < 0 ) + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); + else + PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result ); +#endif + + return result; +} + + +PaHostApiIndex Pa_GetDefaultHostApi( void ) +{ + int result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + } + else + { + result = paDefaultHostApiIndex; + + /* internal consistency check: make sure that the default host api + index is within range */ + + if( result < 0 || result >= hostApisCount_ ) + { + result = paInternalError; + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" ); + if( result < 0 ) + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); + else + PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result ); +#endif + + return result; +} + + +const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi ) +{ + PaHostApiInfo *info; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + info = NULL; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" ); +#endif + + } + else if( hostApi < 0 || hostApi >= hostApisCount_ ) + { + info = NULL; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" ); +#endif + + } + else + { + info = &hostApis_[hostApi]->info; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info ); + PaUtil_DebugPrint("\t{" ); + PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion ); + PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type ); + PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name ); + PaUtil_DebugPrint("\t}\n\n" ); +#endif + + } + + return info; +} + + +PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex ) +{ + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi ); + PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + } + else + { + if( hostApi < 0 || hostApi >= hostApisCount_ ) + { + result = paInvalidHostApi; + } + else + { + if( hostApiDeviceIndex < 0 || + hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount ) + { + result = paInvalidDevice; + } + else + { + result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex; + } + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" ); + if( result < 0 ) + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); + else + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + + return result; +} + + +PaDeviceIndex Pa_GetDeviceCount( void ) +{ + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + } + else + { + result = deviceCount_; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" ); + if( result < 0 ) + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); + else + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + + return result; +} + + +PaDeviceIndex Pa_GetDefaultInputDevice( void ) +{ + PaHostApiIndex hostApi; + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" ); +#endif + + hostApi = Pa_GetDefaultHostApi(); + if( hostApi < 0 ) + { + result = paNoDevice; + } + else + { + result = hostApis_[hostApi]->info.defaultInputDevice; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + + return result; +} + + +PaDeviceIndex Pa_GetDefaultOutputDevice( void ) +{ + PaHostApiIndex hostApi; + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" ); +#endif + + hostApi = Pa_GetDefaultHostApi(); + if( hostApi < 0 ) + { + result = paNoDevice; + } + else + { + result = hostApis_[hostApi]->info.defaultOutputDevice; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + + return result; +} + + +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ) +{ + int hostSpecificDeviceIndex; + int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex ); + PaDeviceInfo *result; + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device ); +#endif + + if( hostApiIndex < 0 ) + { + result = NULL; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" ); +#endif + + } + else + { + result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ]; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result ); + PaUtil_DebugPrint("\t{\n" ); + + PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion ); + PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name ); + PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi ); + PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels ); + PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels ); + PaUtil_DebugPrint("\t}\n\n" ); +#endif + + } + + return result; +} + + +/* + SampleFormatIsValid() returns 1 if sampleFormat is a sample format + defined in portaudio.h, or 0 otherwise. +*/ +static int SampleFormatIsValid( PaSampleFormat format ) +{ + switch( format & ~paNonInterleaved ) + { + case paFloat32: return 1; + case paInt16: return 1; + case paInt32: return 1; + case paInt24: return 1; + case paInt8: return 1; + case paUInt8: return 1; + case paCustomFormat: return 1; + default: return 0; + } +} + +/* + NOTE: make sure this validation list is kept syncronised with the one in + pa_hostapi.h + + ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream() + conform to the expected values as described below. This function is + also designed to be used with the proposed Pa_IsFormatSupported() function. + + There are basically two types of validation that could be performed: + Generic conformance validation, and device capability mismatch + validation. This function performs only generic conformance validation. + Validation that would require knowledge of device capabilities is + not performed because of potentially complex relationships between + combinations of parameters - for example, even if the sampleRate + seems ok, it might not be for a duplex stream - we have no way of + checking this in an API-neutral way, so we don't try. + + On success the function returns PaNoError and fills in hostApi, + hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure + the function returns an error code indicating the first encountered + parameter error. + + + If ValidateOpenStreamParameters() returns paNoError, the following + assertions are guaranteed to be true. + + - at least one of inputParameters & outputParmeters is valid (not NULL) + + - if inputParameters & outputParmeters are both valid, that + inputParameters->device & outputParmeters->device both use the same host api + + PaDeviceIndex inputParameters->device + - is within range (0 to Pa_GetDeviceCount-1) Or: + - is paUseHostApiSpecificDeviceSpecification and + inputParameters->hostApiSpecificStreamInfo is non-NULL and refers + to a valid host api + + int inputParameters->channelCount + - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat inputParameters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *inputParameters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the input device's host Api + + PaDeviceIndex outputParmeters->device + - is within range (0 to Pa_GetDeviceCount-1) + + int outputParmeters->channelCount + - if inputDevice is valid, channelCount is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat outputParmeters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *outputParmeters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the output device's host Api + + double sampleRate + - is not an 'absurd' rate (less than 1000. or greater than 200000.) + - sampleRate is NOT validated against device capabilities + + PaStreamFlags streamFlags + - unused platform neutral flags are zero + - paNeverDropInput is only used for full-duplex callback streams with + variable buffer size (paFramesPerBufferUnspecified) +*/ +static PaError ValidateOpenStreamParameters( + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + PaUtilHostApiRepresentation **hostApi, + PaDeviceIndex *hostApiInputDevice, + PaDeviceIndex *hostApiOutputDevice ) +{ + int inputHostApiIndex = -1, /* Surpress uninitialised var warnings: compiler does */ + outputHostApiIndex = -1; /* not see that if inputParameters and outputParame- */ + /* ters are both nonzero, these indices are set. */ + + if( (inputParameters == NULL) && (outputParameters == NULL) ) + { + return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */ + } + else + { + if( inputParameters == NULL ) + { + *hostApiInputDevice = paNoDevice; + } + else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + if( inputParameters->hostApiSpecificStreamInfo ) + { + inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( + ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType ); + + if( inputHostApiIndex != -1 ) + { + *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification; + *hostApi = hostApis_[inputHostApiIndex]; + } + else + { + return paInvalidDevice; + } + } + else + { + return paInvalidDevice; + } + } + else + { + if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ ) + return paInvalidDevice; + + inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice ); + if( inputHostApiIndex < 0 ) + return paInternalError; + + *hostApi = hostApis_[inputHostApiIndex]; + + if( inputParameters->channelCount <= 0 ) + return paInvalidChannelCount; + + if( !SampleFormatIsValid( inputParameters->sampleFormat ) ) + return paSampleFormatNotSupported; + + if( inputParameters->hostApiSpecificStreamInfo != NULL ) + { + if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType + != (*hostApi)->info.type ) + return paIncompatibleHostApiSpecificStreamInfo; + } + } + + if( outputParameters == NULL ) + { + *hostApiOutputDevice = paNoDevice; + } + else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + if( outputParameters->hostApiSpecificStreamInfo ) + { + outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( + ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType ); + + if( outputHostApiIndex != -1 ) + { + *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification; + *hostApi = hostApis_[outputHostApiIndex]; + } + else + { + return paInvalidDevice; + } + } + else + { + return paInvalidDevice; + } + } + else + { + if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ ) + return paInvalidDevice; + + outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice ); + if( outputHostApiIndex < 0 ) + return paInternalError; + + *hostApi = hostApis_[outputHostApiIndex]; + + if( outputParameters->channelCount <= 0 ) + return paInvalidChannelCount; + + if( !SampleFormatIsValid( outputParameters->sampleFormat ) ) + return paSampleFormatNotSupported; + + if( outputParameters->hostApiSpecificStreamInfo != NULL ) + { + if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType + != (*hostApi)->info.type ) + return paIncompatibleHostApiSpecificStreamInfo; + } + } + + if( (inputParameters != NULL) && (outputParameters != NULL) ) + { + /* ensure that both devices use the same API */ + if( inputHostApiIndex != outputHostApiIndex ) + return paBadIODeviceCombination; + } + } + + + /* Check for absurd sample rates. */ + if( (sampleRate < 1000.0) || (sampleRate > 200000.0) ) + return paInvalidSampleRate; + + if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 ) + return paInvalidFlag; + + if( streamFlags & paNeverDropInput ) + { + /* must be a callback stream */ + if( !streamCallback ) + return paInvalidFlag; + + /* must be a full duplex stream */ + if( (inputParameters == NULL) || (outputParameters == NULL) ) + return paInvalidFlag; + + /* must use paFramesPerBufferUnspecified */ + if( framesPerBuffer != paFramesPerBufferUnspecified ) + return paInvalidFlag; + } + + return paNoError; +} + + +PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + PaError result; + PaUtilHostApiRepresentation *hostApi; + PaDeviceIndex hostApiInputDevice, hostApiOutputDevice; + PaStreamParameters hostApiInputParameters, hostApiOutputParameters; + PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" ); + + if( inputParameters == NULL ){ + PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device ); + PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo ); + } + + if( outputParameters == NULL ){ + PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device ); + PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo ); + } + + PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + result = ValidateOpenStreamParameters( inputParameters, + outputParameters, + sampleRate, 0, paNoFlag, 0, + &hostApi, + &hostApiInputDevice, + &hostApiOutputDevice ); + if( result != paNoError ) + { +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + + if( inputParameters ) + { + hostApiInputParameters.device = hostApiInputDevice; + hostApiInputParameters.channelCount = inputParameters->channelCount; + hostApiInputParameters.sampleFormat = inputParameters->sampleFormat; + hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency; + hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo; + hostApiInputParametersPtr = &hostApiInputParameters; + } + else + { + hostApiInputParametersPtr = NULL; + } + + if( outputParameters ) + { + hostApiOutputParameters.device = hostApiOutputDevice; + hostApiOutputParameters.channelCount = outputParameters->channelCount; + hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat; + hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency; + hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo; + hostApiOutputParametersPtr = &hostApiOutputParameters; + } + else + { + hostApiOutputParametersPtr = NULL; + } + + result = hostApi->IsFormatSupported( hostApi, + hostApiInputParametersPtr, hostApiOutputParametersPtr, + sampleRate ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + if( result == paFormatIsSupported ) + PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" ); + else + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_OpenStream( PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result; + PaUtilHostApiRepresentation *hostApi; + PaDeviceIndex hostApiInputDevice, hostApiOutputDevice; + PaStreamParameters hostApiInputParameters, hostApiOutputParameters; + PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream called:\n" ); + PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream ); + + if( inputParameters == NULL ){ + PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device ); + PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo ); + } + + if( outputParameters == NULL ){ + PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device ); + PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo ); + } + + PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); + PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer ); + PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags ); + PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback ); + PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + /* Check for parameter errors. + NOTE: make sure this validation list is kept syncronised with the one + in pa_hostapi.h + */ + + if( stream == NULL ) + { + result = paBadStreamPtr; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + result = ValidateOpenStreamParameters( inputParameters, + outputParameters, + sampleRate, framesPerBuffer, + streamFlags, streamCallback, + &hostApi, + &hostApiInputDevice, + &hostApiOutputDevice ); + if( result != paNoError ) + { +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + + if( inputParameters ) + { + hostApiInputParameters.device = hostApiInputDevice; + hostApiInputParameters.channelCount = inputParameters->channelCount; + hostApiInputParameters.sampleFormat = inputParameters->sampleFormat; + hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency; + hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo; + hostApiInputParametersPtr = &hostApiInputParameters; + } + else + { + hostApiInputParametersPtr = NULL; + } + + if( outputParameters ) + { + hostApiOutputParameters.device = hostApiOutputDevice; + hostApiOutputParameters.channelCount = outputParameters->channelCount; + hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat; + hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency; + hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo; + hostApiOutputParametersPtr = &hostApiOutputParameters; + } + else + { + hostApiOutputParametersPtr = NULL; + } + + result = hostApi->OpenStream( hostApi, stream, + hostApiInputParametersPtr, hostApiOutputParametersPtr, + sampleRate, framesPerBuffer, streamFlags, streamCallback, userData ); + + if( result == paNoError ) + AddOpenStream( *stream ); + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_OpenDefaultStream( PaStream** stream, + int inputChannelCount, + int outputChannelCount, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result; + PaStreamParameters hostApiInputParameters, hostApiOutputParameters; + PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" ); + PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream ); + PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount ); + PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount ); + PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat ); + PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); + PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer ); + PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback ); + PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData ); +#endif + + + if( inputChannelCount > 0 ) + { + hostApiInputParameters.device = Pa_GetDefaultInputDevice(); + hostApiInputParameters.channelCount = inputChannelCount; + hostApiInputParameters.sampleFormat = sampleFormat; + /* defaultHighInputLatency is used below instead of + defaultLowInputLatency because it is more important for the default + stream to work reliably than it is for it to work with the lowest + latency. + */ + hostApiInputParameters.suggestedLatency = + Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency; + hostApiInputParameters.hostApiSpecificStreamInfo = NULL; + hostApiInputParametersPtr = &hostApiInputParameters; + } + else + { + hostApiInputParametersPtr = NULL; + } + + if( outputChannelCount > 0 ) + { + hostApiOutputParameters.device = Pa_GetDefaultOutputDevice(); + hostApiOutputParameters.channelCount = outputChannelCount; + hostApiOutputParameters.sampleFormat = sampleFormat; + /* defaultHighOutputLatency is used below instead of + defaultLowOutputLatency because it is more important for the default + stream to work reliably than it is for it to work with the lowest + latency. + */ + hostApiOutputParameters.suggestedLatency = + Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency; + hostApiOutputParameters.hostApiSpecificStreamInfo = NULL; + hostApiOutputParametersPtr = &hostApiOutputParameters; + } + else + { + hostApiOutputParametersPtr = NULL; + } + + + result = Pa_OpenStream( + stream, hostApiInputParametersPtr, hostApiOutputParametersPtr, + sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError PaUtil_ValidateStreamPointer( PaStream* stream ) +{ + if( !PA_IS_INITIALISED_ ) return paNotInitialized; + + if( stream == NULL ) return paBadStreamPtr; + + if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC ) + return paBadStreamPtr; + + return paNoError; +} + + +PaError Pa_CloseStream( PaStream* stream ) +{ + PaUtilStreamInterface *interface; + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CloseStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + /* always remove the open stream from our list, even if this function + eventually returns an error. Otherwise CloseOpenStreams() will + get stuck in an infinite loop */ + RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */ + + if( result == paNoError ) + { + interface = PA_STREAM_INTERFACE(stream); + + /* abort the stream if it isn't stopped */ + result = interface->IsStopped( stream ); + if( result == 1 ) + result = paNoError; + else if( result == 0 ) + result = interface->Abort( stream ); + + if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? */ + result = interface->Close( stream ); + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CloseStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); + PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback ); +#endif + + if( result == paNoError ) + { + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + if( result == 0 ) + { + result = paStreamIsNotStopped ; + } + if( result == 1 ) + { + PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback; + result = paNoError; + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; + +} + + +PaError Pa_StartStream( PaStream *stream ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StartStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + if( result == 0 ) + { + result = paStreamIsNotStopped ; + } + else if( result == 1 ) + { + result = PA_STREAM_INTERFACE(stream)->Start( stream ); + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StartStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_StopStream( PaStream *stream ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StopStream called\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + if( result == 0 ) + { + result = PA_STREAM_INTERFACE(stream)->Stop( stream ); + } + else if( result == 1 ) + { + result = paStreamIsStopped; + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StopStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_AbortStream( PaStream *stream ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_AbortStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + if( result == 0 ) + { + result = PA_STREAM_INTERFACE(stream)->Abort( stream ); + } + else if( result == 1 ) + { + result = paStreamIsStopped; + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_AbortStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_IsStreamStopped( PaStream *stream ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_IsStreamActive( PaStream *stream ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamActive called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + result = PA_STREAM_INTERFACE(stream)->IsActive( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ) +{ + PaError error = PaUtil_ValidateStreamPointer( stream ); + const PaStreamInfo *result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" ); + PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = &PA_STREAM_REP( stream )->streamInfo; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" ); + PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result ); + PaUtil_DebugPrint("\t{" ); + + PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion ); + PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency ); + PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency ); + PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate ); + PaUtil_DebugPrint("\t}\n\n" ); +#endif + + } + + return result; +} + + +PaTime Pa_GetStreamTime( PaStream *stream ) +{ + PaError error = PaUtil_ValidateStreamPointer( stream ); + PaTime result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamTime called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" ); + PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetTime( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" ); + PaUtil_DebugPrint("\tPaTime: %g\n\n", result ); +#endif + + } + + return result; +} + + +double Pa_GetStreamCpuLoad( PaStream* stream ) +{ + PaError error = PaUtil_ValidateStreamPointer( stream ); + double result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + + result = 0.0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" ); + PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" ); + PaUtil_DebugPrint("\tdouble: %g\n\n", result ); +#endif + + } + + return result; +} + + +PaError Pa_ReadStream( PaStream* stream, + void *buffer, + unsigned long frames ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_ReadStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + if( frames == 0 ) + { + result = paInternalError; /** @todo should return a different error code */ + } + else if( buffer == 0 ) + { + result = paInternalError; /** @todo should return a different error code */ + } + else + { + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + if( result == 0 ) + { + result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames ); + } + else if( result == 1 ) + { + result = paStreamIsStopped; + } + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_ReadStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_WriteStream( PaStream* stream, + const void *buffer, + unsigned long frames ) +{ + PaError result = PaUtil_ValidateStreamPointer( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_WriteStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + if( frames == 0 ) + { + result = paInternalError; /** @todo should return a different error code */ + } + else if( buffer == 0 ) + { + result = paInternalError; /** @todo should return a different error code */ + } + else + { + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + if( result == 0 ) + { + result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames ); + } + else if( result == 1 ) + { + result = paStreamIsStopped; + } + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_WriteStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + +signed long Pa_GetStreamReadAvailable( PaStream* stream ) +{ + PaError error = PaUtil_ValidateStreamPointer( stream ); + signed long result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" ); + PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + } + + return result; +} + + +signed long Pa_GetStreamWriteAvailable( PaStream* stream ) +{ + PaError error = PaUtil_ValidateStreamPointer( stream ); + signed long result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" ); + PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + } + + return result; +} + + +PaError Pa_GetSampleSize( PaSampleFormat format ) +{ + int result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetSampleSize called:\n" ); + PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format ); +#endif + + switch( format & ~paNonInterleaved ) + { + + case paUInt8: + case paInt8: + result = 1; + break; + + case paInt16: + result = 2; + break; + + case paInt24: + result = 3; + break; + + case paFloat32: + case paInt32: + result = 4; + break; + + default: + result = paSampleFormatNotSupported; + break; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" ); + if( result > 0 ) + PaUtil_DebugPrint("\tint: %d\n\n", result ); + else + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return (PaError) result; +} + diff --git a/pjmedia/src/pjmedia/portaudio/pa_hostapi.h b/pjmedia/src/pjmedia/portaudio/pa_hostapi.h index a71ff3198..d05507067 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_hostapi.h +++ b/pjmedia/src/pjmedia/portaudio/pa_hostapi.h @@ -1,244 +1,244 @@ -#ifndef PA_HOSTAPI_H -#define PA_HOSTAPI_H -/* - * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $ - * Portable Audio I/O Library - * host api representation - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Interface used by pa_front to virtualize functions which operate on - host APIs. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** **FOR THE USE OF pa_front.c ONLY** - Do NOT use fields in this structure, they my change at any time. - Use functions defined in pa_util.h if you think you need functionality - which can be derived from here. -*/ -typedef struct PaUtilPrivatePaFrontHostApiInfo { - - - unsigned long baseDeviceIndex; -}PaUtilPrivatePaFrontHostApiInfo; - - -/** The common header for all data structures whose pointers are passed through - the hostApiSpecificStreamInfo field of the PaStreamParameters structure. - Note that in order to keep the public PortAudio interface clean, this structure - is not used explicitly when declaring hostApiSpecificStreamInfo data structures. - However, some code in pa_front depends on the first 3 members being equivalent - with this structure. - @see PaStreamParameters -*/ -typedef struct PaUtilHostApiSpecificStreamInfoHeader -{ - unsigned long size; /**< size of whole structure including this header */ - PaHostApiTypeId hostApiType; /**< host API for which this data is intended */ - unsigned long version; /**< structure version */ -} PaUtilHostApiSpecificStreamInfoHeader; - - - -/** A structure representing the interface to a host API. Contains both - concrete data and pointers to functions which implement the interface. -*/ -typedef struct PaUtilHostApiRepresentation { - PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo; - - /** The host api implementation should populate the info field. In the - case of info.defaultInputDevice and info.defaultOutputDevice the - values stored should be 0 based indices within the host api's own - device index range (0 to deviceCount). These values will be converted - to global device indices by pa_front after PaUtilHostApiInitializer() - returns. - */ - PaHostApiInfo info; - - PaDeviceInfo** deviceInfos; - - /** - (*Terminate)() is guaranteed to be called with a valid - parameter, which was previously returned from the same implementation's - initializer. - */ - void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi ); - - /** - The inputParameters and outputParameters pointers should not be saved - as they will not remain valid after OpenStream is called. - - - The following guarantees are made about parameters to (*OpenStream)(): - - [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be - kept in sync with the one for ValidateOpenStreamParameters and - Pa_OpenStream in pa_front.c] - - PaHostApiRepresentation *hostApi - - is valid for this implementation - - PaStream** stream - - is non-null - - - at least one of inputParameters & outputParmeters is valid (not NULL) - - - if inputParameters & outputParmeters are both valid, that - inputParameters->device & outputParmeters->device both use the same host api - - PaDeviceIndex inputParameters->device - - is within range (0 to Pa_CountDevices-1) Or: - - is paUseHostApiSpecificDeviceSpecification and - inputParameters->hostApiSpecificStreamInfo is non-NULL and refers - to a valid host api - - int inputParameters->numChannels - - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0 - - upper bound is NOT validated against device capabilities - - PaSampleFormat inputParameters->sampleFormat - - is one of the sample formats defined in portaudio.h - - void *inputParameters->hostApiSpecificStreamInfo - - if supplied its hostApi field matches the input device's host Api - - PaDeviceIndex outputParmeters->device - - is within range (0 to Pa_CountDevices-1) - - int outputParmeters->numChannels - - if inputDevice is valid, numInputChannels is > 0 - - upper bound is NOT validated against device capabilities - - PaSampleFormat outputParmeters->sampleFormat - - is one of the sample formats defined in portaudio.h - - void *outputParmeters->hostApiSpecificStreamInfo - - if supplied its hostApi field matches the output device's host Api - - double sampleRate - - is not an 'absurd' rate (less than 1000. or greater than 200000.) - - sampleRate is NOT validated against device capabilities - - PaStreamFlags streamFlags - - unused platform neutral flags are zero - - paNeverDropInput is only used for full-duplex callback streams - with variable buffer size (paFramesPerBufferUnspecified) - - [*END PA FRONT VALIDATIONS*] - - - The following validations MUST be performed by (*OpenStream)(): - - - check that input device can support numInputChannels - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - if inputStreamInfo is supplied, validate its contents, - or return an error if no inputStreamInfo is expected - - - check that output device can support numOutputChannels - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - if outputStreamInfo is supplied, validate its contents, - or return an error if no outputStreamInfo is expected - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - - - alter sampleRate to a close allowable rate if necessary - - - validate inputLatency and outputLatency - - - validate any platform specific flags, if flags are supplied they - must be valid. - */ - PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi, - PaStream** stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerCallback, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); - - - PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -} PaUtilHostApiRepresentation; - - -/** Prototype for the initialization function which must be implemented by every - host API. - - @see paHostApiInitializers -*/ -typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex ); - - -/** paHostApiInitializers is a NULL-terminated array of host API initialization - functions. These functions are called by pa_front to initialize the host APIs - when the client calls Pa_Initialize(). - - There is a platform specific file which defines paHostApiInitializers for that - platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example. -*/ -extern PaUtilHostApiInitializer *paHostApiInitializers[]; - - -/** The index of the default host API in the paHostApiInitializers array. - - There is a platform specific file which defines paDefaultHostApiIndex for that - platform, see pa_win/pa_win_hostapis.c for example. -*/ -extern int paDefaultHostApiIndex; - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_HOSTAPI_H */ +#ifndef PA_HOSTAPI_H +#define PA_HOSTAPI_H +/* + * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $ + * Portable Audio I/O Library + * host api representation + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Interface used by pa_front to virtualize functions which operate on + host APIs. +*/ + + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** **FOR THE USE OF pa_front.c ONLY** + Do NOT use fields in this structure, they my change at any time. + Use functions defined in pa_util.h if you think you need functionality + which can be derived from here. +*/ +typedef struct PaUtilPrivatePaFrontHostApiInfo { + + + unsigned long baseDeviceIndex; +}PaUtilPrivatePaFrontHostApiInfo; + + +/** The common header for all data structures whose pointers are passed through + the hostApiSpecificStreamInfo field of the PaStreamParameters structure. + Note that in order to keep the public PortAudio interface clean, this structure + is not used explicitly when declaring hostApiSpecificStreamInfo data structures. + However, some code in pa_front depends on the first 3 members being equivalent + with this structure. + @see PaStreamParameters +*/ +typedef struct PaUtilHostApiSpecificStreamInfoHeader +{ + unsigned long size; /**< size of whole structure including this header */ + PaHostApiTypeId hostApiType; /**< host API for which this data is intended */ + unsigned long version; /**< structure version */ +} PaUtilHostApiSpecificStreamInfoHeader; + + + +/** A structure representing the interface to a host API. Contains both + concrete data and pointers to functions which implement the interface. +*/ +typedef struct PaUtilHostApiRepresentation { + PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo; + + /** The host api implementation should populate the info field. In the + case of info.defaultInputDevice and info.defaultOutputDevice the + values stored should be 0 based indices within the host api's own + device index range (0 to deviceCount). These values will be converted + to global device indices by pa_front after PaUtilHostApiInitializer() + returns. + */ + PaHostApiInfo info; + + PaDeviceInfo** deviceInfos; + + /** + (*Terminate)() is guaranteed to be called with a valid + parameter, which was previously returned from the same implementation's + initializer. + */ + void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi ); + + /** + The inputParameters and outputParameters pointers should not be saved + as they will not remain valid after OpenStream is called. + + + The following guarantees are made about parameters to (*OpenStream)(): + + [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be + kept in sync with the one for ValidateOpenStreamParameters and + Pa_OpenStream in pa_front.c] + + PaHostApiRepresentation *hostApi + - is valid for this implementation + + PaStream** stream + - is non-null + + - at least one of inputParameters & outputParmeters is valid (not NULL) + + - if inputParameters & outputParmeters are both valid, that + inputParameters->device & outputParmeters->device both use the same host api + + PaDeviceIndex inputParameters->device + - is within range (0 to Pa_CountDevices-1) Or: + - is paUseHostApiSpecificDeviceSpecification and + inputParameters->hostApiSpecificStreamInfo is non-NULL and refers + to a valid host api + + int inputParameters->numChannels + - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat inputParameters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *inputParameters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the input device's host Api + + PaDeviceIndex outputParmeters->device + - is within range (0 to Pa_CountDevices-1) + + int outputParmeters->numChannels + - if inputDevice is valid, numInputChannels is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat outputParmeters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *outputParmeters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the output device's host Api + + double sampleRate + - is not an 'absurd' rate (less than 1000. or greater than 200000.) + - sampleRate is NOT validated against device capabilities + + PaStreamFlags streamFlags + - unused platform neutral flags are zero + - paNeverDropInput is only used for full-duplex callback streams + with variable buffer size (paFramesPerBufferUnspecified) + + [*END PA FRONT VALIDATIONS*] + + + The following validations MUST be performed by (*OpenStream)(): + + - check that input device can support numInputChannels + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if inputStreamInfo is supplied, validate its contents, + or return an error if no inputStreamInfo is expected + + - check that output device can support numOutputChannels + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if outputStreamInfo is supplied, validate its contents, + or return an error if no outputStreamInfo is expected + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + + - alter sampleRate to a close allowable rate if necessary + + - validate inputLatency and outputLatency + + - validate any platform specific flags, if flags are supplied they + must be valid. + */ + PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi, + PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerCallback, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); + + + PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +} PaUtilHostApiRepresentation; + + +/** Prototype for the initialization function which must be implemented by every + host API. + + @see paHostApiInitializers +*/ +typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex ); + + +/** paHostApiInitializers is a NULL-terminated array of host API initialization + functions. These functions are called by pa_front to initialize the host APIs + when the client calls Pa_Initialize(). + + There is a platform specific file which defines paHostApiInitializers for that + platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example. +*/ +extern PaUtilHostApiInitializer *paHostApiInitializers[]; + + +/** The index of the default host API in the paHostApiInitializers array. + + There is a platform specific file which defines paDefaultHostApiIndex for that + platform, see pa_win/pa_win_hostapis.c for example. +*/ +extern int paDefaultHostApiIndex; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_HOSTAPI_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c index 8f115d0e4..3b0471631 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c +++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c @@ -1,3272 +1,3272 @@ -/* - * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * ALSA implementation by Joshua Haberman and Arve Knudsen - * - * Copyright (c) 2002 Joshua Haberman - * Copyright (c) 2005 Arve Knudsen - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -#define ALSA_PCM_NEW_HW_PARAMS_API -#define ALSA_PCM_NEW_SW_PARAMS_API -#include -#undef ALSA_PCM_NEW_HW_PARAMS_API -#undef ALSA_PCM_NEW_SW_PARAMS_API - -#include -#include /* strlen() */ -#include -#include -#include -#include -#include -#include -#include /* For sig_atomic_t */ - -#include "portaudio.h" -#include "pa_util.h" -#include "pa_unix_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "pa_linux_alsa.h" - -/* Check return value of ALSA function, and map it to PaError */ -#define ENSURE_(expr, code) \ - do { \ - if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \ - { \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \ - { \ - PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \ - } \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (code); \ - goto error; \ - } \ - } while( 0 ); - -#define ASSERT_CALL_(expr, success) \ - aErr_ = (expr); \ - assert( aErr_ == success ); - -static int aErr_; /* Used with ENSURE_ */ -static pthread_t callbackThread_; - -typedef enum -{ - StreamDirection_In, - StreamDirection_Out -} StreamDirection; - -/* Threading utility struct */ -typedef struct PaAlsaThreading -{ - pthread_t watchdogThread; - pthread_t callbackThread; - int watchdogRunning; - int rtSched; - int rtPrio; - int useWatchdog; - unsigned long throttledSleepTime; - volatile PaTime callbackTime; - volatile PaTime callbackCpuTime; - PaUtilCpuLoadMeasurer *cpuLoadMeasurer; -} PaAlsaThreading; - -typedef struct -{ - PaSampleFormat hostSampleFormat; - unsigned long framesPerBuffer; - int numUserChannels, numHostChannels; - int userInterleaved, hostInterleaved; - - snd_pcm_t *pcm; - snd_pcm_uframes_t bufferSize; - snd_pcm_format_t nativeFormat; - unsigned int nfds; - int ready; /* Marked ready from poll */ - void **userBuffers; - snd_pcm_uframes_t offset; - StreamDirection streamDir; - - snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */ -} PaAlsaStreamComponent; - -/* Implementation specific stream structure */ -typedef struct PaAlsaStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - PaAlsaThreading threading; - - unsigned long framesPerUserBuffer; - - int primeBuffers; - int callbackMode; /* bool: are we running in callback mode? */ - int pcmsSynced; /* Have we successfully synced pcms */ - - /* the callback thread uses these to poll the sound device(s), waiting - * for data to be ready/available */ - struct pollfd *pfds; - int pollTimeout; - - /* Used in communication between threads */ - volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */ - volatile sig_atomic_t callbackAbort; /* Drop frames? */ - volatile sig_atomic_t callbackStop; /* Signal a stop */ - volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */ - pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */ - pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */ - pthread_cond_t startCond; /* Wait untill audio is started in callback thread */ - - int neverDropInput; - - PaTime underrun; - PaTime overrun; - - PaAlsaStreamComponent capture, playback; -} -PaAlsaStream; - -/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct PaAlsaHostApiRepresentation -{ - PaUtilHostApiRepresentation commonHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - PaHostApiIndex hostApiIndex; -} -PaAlsaHostApiRepresentation; - -typedef struct PaAlsaDeviceInfo -{ - PaDeviceInfo commonDeviceInfo; - char *alsaName; - int isPlug; - int minInputChannels; - int minOutputChannels; -} -PaAlsaDeviceInfo; - -/* Threading utilities */ - -static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm ) -{ - th->watchdogRunning = 0; - th->rtSched = 0; - th->callbackTime = 0; - th->callbackCpuTime = 0; - th->useWatchdog = 1; - th->throttledSleepTime = 0; - th->cpuLoadMeasurer = clm; - - th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2 - + sched_get_priority_min( SCHED_FIFO ); -} - -static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult ) -{ - PaError result = paNoError; - void *pret; - - if( exitResult ) - *exitResult = paNoError; - if( watchdogExitResult ) - *watchdogExitResult = paNoError; - - if( th->watchdogRunning ) - { - pthread_cancel( th->watchdogThread ); - ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 ); - - if( pret && pret != PTHREAD_CANCELED ) - { - if( watchdogExitResult ) - *watchdogExitResult = *(PaError *) pret; - free( pret ); - } - } - - /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ - /* TODO: Make join time out */ - if( !wait ) - pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */ - ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 ); - - if( pret && pret != PTHREAD_CANCELED ) - { - if( exitResult ) - *exitResult = *(PaError *) pret; - free( pret ); - } - - return result; -} - -static void OnWatchdogExit( void *userData ) -{ - PaAlsaThreading *th = (PaAlsaThreading *) userData; - struct sched_param spm = { 0 }; - assert( th ); - - ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */ - PA_DEBUG(( "Watchdog exiting\n" )); -} - -static PaError BoostPriority( PaAlsaThreading *th ) -{ - PaError result = paNoError; - struct sched_param spm = { 0 }; - spm.sched_priority = th->rtPrio; - - assert( th ); - - if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 ) - { - PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */ - PA_DEBUG(( "Failed bumping priority\n" )); - result = 0; - } - else - result = 1; /* Success */ -error: - return result; -} - -static void *WatchdogFunc( void *userData ) -{ - PaError result = paNoError, *pres = NULL; - int err; - PaAlsaThreading *th = (PaAlsaThreading *) userData; - unsigned intervalMsec = 500; - const PaTime maxSeconds = 3.; /* Max seconds between callbacks */ - PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed; - double cpuLoad, avgCpuLoad = 0.; - int throttled = 0; - - assert( th ); - - pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */ - - /* Boost priority of callback thread */ - PA_ENSURE( result = BoostPriority( th ) ); - if( !result ) - { - pthread_exit( NULL ); /* Boost failed, might as well exit */ - } - - cpuTimeThen = th->callbackCpuTime; - { - int policy; - struct sched_param spm = { 0 }; - pthread_getschedparam( pthread_self(), &policy, &spm ); - PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority )); - } - - while( 1 ) - { - double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff; - - /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */ - pthread_testcancel(); - Pa_Sleep( intervalMsec ); - pthread_testcancel(); - - if( PaUtil_GetTime() - th->callbackTime > maxSeconds ) - { - PA_DEBUG(( "Watchdog: Terminating callback thread\n" )); - /* Tell thread to terminate */ - err = pthread_kill( th->callbackThread, SIGKILL ); - pthread_exit( NULL ); - } - - PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) )); - - /* Check if we should throttle, or unthrottle :P */ - cpuTimeNow = th->callbackCpuTime; - cpuTimeElapsed = cpuTimeNow - cpuTimeThen; - cpuTimeThen = cpuTimeNow; - - timeNow = PaUtil_GetTime(); - timeElapsed = timeNow - timeThen; - timeThen = timeNow; - cpuLoad = cpuTimeElapsed / timeElapsed; - avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1; - /* - if( throttled ) - PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed )); - */ - if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 ) - { - static int policy; - static struct sched_param spm = { 0 }; - static const struct sched_param defaultSpm = { 0 }; - PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority )); - - pthread_getschedparam( th->callbackThread, &policy, &spm ); - if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) ) - { - throttled = 1; - } - else - PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) )); - - /* Give other processes a go, before raising priority again */ - PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime )); - Pa_Sleep( th->throttledSleepTime ); - - /* Reset callback priority */ - if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 ) - { - PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) )); - } - - if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 ) - intervalMsec = 50; - else - intervalMsec = 100; - - /* - lowpassCoeff = .97; - lowpassCoeff1 = .99999 - lowpassCoeff; - */ - } - else if( throttled && avgCpuLoad < .8 ) - { - intervalMsec = 500; - throttled = 0; - - /* - lowpassCoeff = .9; - lowpassCoeff1 = .99999 - lowpassCoeff; - */ - } - } - - pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */ - -error: - /* Shouldn't get here in the normal case */ - - /* Pass on error code */ - pres = malloc( sizeof (PaError) ); - *pres = result; - - pthread_exit( pres ); -} - -static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s ) -{ - PaError result = paNoError; - pthread_attr_t attr; - int started = 0; - -#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1) - if( th->rtSched ) - { - if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 ) - { - int savedErrno = errno; /* In case errno gets overwritten */ - assert( savedErrno != EINVAL ); /* Most likely a programmer error */ - PA_UNLESS( (savedErrno == EPERM), paInternalError ); - PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ )); - } - else - PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ )); - } -#endif - - PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); - /* Priority relative to other processes */ - PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); - - PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError ); - started = 1; - - if( th->rtSched ) - { - if( th->useWatchdog ) - { - int err; - struct sched_param wdSpm = { 0 }; - /* Launch watchdog, watchdog sets callback thread priority */ - int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) ); - wdSpm.sched_priority = prio; - - PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); - PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError ); - PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); - PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError ); - PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError ); - if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) ) - { - PA_UNLESS( err == EPERM, paInternalError ); - /* Permission error, go on without realtime privileges */ - PA_DEBUG(( "Failed bumping priority\n" )); - } - else - { - int policy; - th->watchdogRunning = 1; - ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 ); - /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */ - if( wdSpm.sched_priority != prio ) - { - PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority )); - PA_ENSURE( paInternalError ); - } - } - } - else - PA_ENSURE( BoostPriority( th ) ); - } - -end: - return result; -error: - if( started ) - KillCallbackThread( th, 0, NULL, NULL ); - - goto end; -} - -static void CallbackUpdate( PaAlsaThreading *th ) -{ - th->callbackTime = PaUtil_GetTime(); - th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer ); -} - -/* prototypes for functions declared in this file */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *callback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi ); -static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ); -static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ); - -/* Callback prototypes */ -static void *CallbackThreadFunc( void *userData ); - -/* Blocking prototypes */ -static signed long GetStreamReadAvailable( PaStream* s ); -static signed long GetStreamWriteAvailable( PaStream* s ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); - - -static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device ) -{ - return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device]; -} - -PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaAlsaHostApiRepresentation *alsaHostApi = NULL; - - PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory( - sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory ); - PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - alsaHostApi->hostApiIndex = hostApiIndex; - - *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paALSA; - (*hostApi)->info.name = "ALSA"; - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PA_ENSURE( BuildDeviceList( alsaHostApi ) ); - - PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface, - CloseStream, StartStream, - StopStream, AbortStream, - IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, - GetStreamReadAvailable, - GetStreamWriteAvailable ); - - return result; - -error: - if( alsaHostApi ) - { - if( alsaHostApi->allocations ) - { - PaUtil_FreeAllAllocations( alsaHostApi->allocations ); - PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); - } - - PaUtil_FreeMemory( alsaHostApi ); - } - - return result; -} - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; - - assert( hostApi ); - - if( alsaHostApi->allocations ) - { - PaUtil_FreeAllAllocations( alsaHostApi->allocations ); - PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); - } - - PaUtil_FreeMemory( alsaHostApi ); - snd_config_update_free_global(); -} - -/*! Determine max channels and default latencies. - * - * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for - * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero, - * and a suitable result returned. The device is closed before returning. - */ -static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency, - double *defaultHighLatency, double *defaultSampleRate, int isPlug ) -{ - PaError result = paNoError; - snd_pcm_hw_params_t *hwParams; - snd_pcm_uframes_t lowLatency = 512, highLatency = 2048; - unsigned int minChans, maxChans; - double defaultSr = *defaultSampleRate; - - assert( pcm ); - - ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError ); - - snd_pcm_hw_params_alloca( &hwParams ); - snd_pcm_hw_params_any( pcm, hwParams ); - - if( defaultSr >= 0 ) - { - /* Could be that the device opened in one mode supports samplerates that the other mode wont have, - * so try again .. */ - if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 ) - { - defaultSr = -1.; - PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ )); - } - } - - if( defaultSr < 0. ) /* Default sample rate not set */ - { - unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */ - ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError ); - ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError ); - } - - ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError ); - assert( maxChans <= INT_MAX ); - assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called, - resulting in zeroed values */ - - /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */ - if( isPlug && maxChans > 128 ) - { - maxChans = 128; - PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans )); - } - - /* TWEAKME: - * - * Giving values for default min and max latency is not - * straightforward. Here are our objectives: - * - * * for low latency, we want to give the lowest value - * that will work reliably. This varies based on the - * sound card, kernel, CPU, etc. I think it is better - * to give sub-optimal latency than to give a number - * too low and cause dropouts. My conservative - * estimate at this point is to base it on 4096-sample - * latency at 44.1 kHz, which gives a latency of 23ms. - * * for high latency we want to give a large enough - * value that dropouts are basically impossible. This - * doesn't really require as much tweaking, since - * providing too large a number will just cause us to - * select the nearest setting that will work at stream - * config time. - */ - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); - - /* Have to reset hwParams, to set new buffer size */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError ); - - *minChannels = (int)minChans; - *maxChannels = (int)maxChans; - *defaultSampleRate = defaultSr; - *defaultLowLatency = (double) lowLatency / *defaultSampleRate; - *defaultHighLatency = (double) highLatency / *defaultSampleRate; - -end: - snd_pcm_close( pcm ); - return result; - -error: - goto end; -} - -/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate - * wether input/output is available) */ -static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo ) -{ - deviceInfo->structVersion = -1; - deviceInfo->name = NULL; - deviceInfo->hostApi = -1; - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - deviceInfo->defaultLowInputLatency = -1.; - deviceInfo->defaultLowOutputLatency = -1.; - deviceInfo->defaultHighInputLatency = -1.; - deviceInfo->defaultHighOutputLatency = -1.; - deviceInfo->defaultSampleRate = -1.; -} - -/* Helper struct */ -typedef struct -{ - char *alsaName; - char *name; - int isPlug; - int hasPlayback; - int hasCapture; -} DeviceNames; - -static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi, - char **dst, - const char *src) -{ - PaError result = paNoError; - int len = strlen( src ) + 1; - - /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */ - - PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - strncpy( *dst, src, len ); - -error: - return result; -} - -/* Disregard standard plugins - * XXX: Might want to make the "default" plugin available, if we can make it work - */ -static int IgnorePlugin( const char *pluginId ) -{ -#define numIgnored 10 - static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee", - "file", "null", "shm"}; - int i; - - for( i = 0; i < numIgnored; ++i ) - { - if( !strcmp( pluginId, ignoredPlugins[i] ) ) - { - return 1; - } - } - - return 0; -} - -/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */ -static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) -{ - PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep; - PaAlsaDeviceInfo *deviceInfoArray; - int cardIdx = -1, devIdx = 0; - snd_ctl_card_info_t *cardInfo; - PaError result = paNoError; - size_t numDeviceNames = 0, maxDeviceNames = 1, i; - DeviceNames *deviceNames = NULL; - snd_config_t *topNode = NULL; - snd_pcm_info_t *pcmInfo; - int res; - int blocking = SND_PCM_NONBLOCK; - char alsaCardName[50]; - if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) ) - blocking = 0; - - /* These two will be set to the first working input and output device, respectively */ - commonApi->info.defaultInputDevice = paNoDevice; - commonApi->info.defaultOutputDevice = paNoDevice; - - /* count the devices by enumerating all the card numbers */ - - /* snd_card_next() modifies the integer passed to it to be: - * the index of the first card if the parameter is -1 - * the index of the next card if the parameter is the index of a card - * -1 if there are no more cards - * - * The function itself returns 0 if it succeeded. */ - cardIdx = -1; - snd_ctl_card_info_alloca( &cardInfo ); - snd_pcm_info_alloca( &pcmInfo ); - while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) - { - char *cardName; - int devIdx = -1; - snd_ctl_t *ctl; - char buf[50]; - - snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); - - /* Acquire name of card */ - if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) - continue; /* Unable to open card :( */ - snd_ctl_card_info( ctl, cardInfo ); - - PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) ); - - while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) - { - char *alsaDeviceName, *deviceName; - size_t len; - int hasPlayback = 0, hasCapture = 0; - snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx ); - - /* Obtain info about this particular device */ - snd_pcm_info_set_device( pcmInfo, devIdx ); - snd_pcm_info_set_subdevice( pcmInfo, 0 ); - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - hasCapture = 1; - - snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); - if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - hasPlayback = 1; - - if( !hasPlayback && !hasCapture ) - { - continue; /* Error */ - } - - /* The length of the string written by snprintf plus terminating 0 */ - len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1; - PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - snprintf( deviceName, len, "%s: %s (%s)", cardName, - snd_pcm_info_get_name( pcmInfo ), buf ); - - ++numDeviceNames; - if( !deviceNames || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), - paInsufficientMemory ); - } - - PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); - - deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName; - deviceNames[ numDeviceNames - 1 ].name = deviceName; - deviceNames[ numDeviceNames - 1 ].isPlug = 0; - deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback; - deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture; - } - snd_ctl_close( ctl ); - } - - /* Iterate over plugin devices */ - snd_config_update(); - if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 ) - { - snd_config_iterator_t i, next; - - snd_config_for_each( i, next, topNode ) - { - const char *tpStr = NULL, *idStr = NULL; - char *alsaDeviceName, *deviceName; - snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL; - if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND ) - continue; - - ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError ); - ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError ); - - ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError ); - if( IgnorePlugin( idStr ) ) - { - PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr )); - continue; - } - - PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr )); - - PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 6 ), paInsufficientMemory ); - strcpy( alsaDeviceName, idStr ); - PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 1 ), paInsufficientMemory ); - strcpy( deviceName, idStr ); - - ++numDeviceNames; - if( !deviceNames || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), - paInsufficientMemory ); - } - - deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName; - deviceNames[numDeviceNames - 1].name = deviceName; - deviceNames[numDeviceNames - 1].isPlug = 1; - deviceNames[numDeviceNames - 1].hasPlayback = 1; - deviceNames[numDeviceNames - 1].hasCapture = 1; - } - } - else - PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) )); - - /* allocate deviceInfo memory based on the number of devices */ - PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); - - /* allocate all device info structs in a contiguous block */ - PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); - - /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name), - * it's ignored. - */ - /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */ - for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) - { - snd_pcm_t *pcm; - PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx]; - PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo; - - /* Zero fields */ - InitializeDeviceInfo( commonDeviceInfo ); - - /* to determine device capabilities, we must open the device and query the - * hardware parameter configuration space */ - - /* Query capture */ - if( deviceNames[i].hasCapture && - snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 ) - { - if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels, - &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency, - &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError ) - continue; /* Error */ - } - - /* Query playback */ - if( deviceNames[i].hasPlayback && - snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 ) - { - if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels, - &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency, - &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError ) - continue; /* Error */ - } - - commonDeviceInfo->structVersion = 2; - commonDeviceInfo->hostApi = alsaApi->hostApiIndex; - commonDeviceInfo->name = deviceNames[i].name; - deviceInfo->alsaName = deviceNames[i].alsaName; - deviceInfo->isPlug = deviceNames[i].isPlug; - - /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object. - * Should now be safe to add device info, unless the device supports neither capture nor playback - */ - if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 ) - { - if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = devIdx; - if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = devIdx; - - commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo; - } - } - free( deviceNames ); - - commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */ - -end: - return result; - -error: - goto end; /* No particular action */ -} - -/* Check against known device capabilities */ -static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode ) -{ - PaError result = paNoError; - int maxChans; - const PaAlsaDeviceInfo *deviceInfo = NULL; - assert( parameters ); - - if( parameters->device != paUseHostApiSpecificDeviceSpecification ) - { - assert( parameters->device < hostApi->info.deviceCount ); - PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination ); - deviceInfo = GetDeviceInfo( hostApi, parameters->device ); - } - else - { - const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo; - - PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice ); - PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1, - paIncompatibleHostApiSpecificStreamInfo ); - - return paNoError; /* Skip further checking */ - } - - assert( deviceInfo ); - assert( parameters->hostApiSpecificStreamInfo == NULL ); - maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels : - deviceInfo->commonDeviceInfo.maxOutputChannels); - PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount ); - -error: - return result; -} - -/* Given an open stream, what sample formats are available? */ - -static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm ) -{ - PaSampleFormat available = 0; - snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); - - snd_pcm_hw_params_any( pcm, hwParams ); - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) - available |= paFloat32; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) - available |= paInt32; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0) - available |= paInt24; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) - available |= paInt16; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) - available |= paUInt8; - - if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) - available |= paInt8; - - return available; -} - -static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) -{ - switch( paFormat ) - { - case paFloat32: - return SND_PCM_FORMAT_FLOAT; - - case paInt16: - return SND_PCM_FORMAT_S16; - - case paInt24: - return SND_PCM_FORMAT_S24; - - case paInt32: - return SND_PCM_FORMAT_S32; - - case paInt8: - return SND_PCM_FORMAT_S8; - - case paUInt8: - return SND_PCM_FORMAT_U8; - - default: - return SND_PCM_FORMAT_UNKNOWN; - } -} - -/** Open an ALSA pcm handle. - * - * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a - * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin - * device. - */ -static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection - streamDir, snd_pcm_t **pcm ) -{ - PaError result = paNoError; - int ret; - const char *deviceName = alloca( 50 ); - const PaAlsaDeviceInfo *deviceInfo = NULL; - PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo; - - if( !streamInfo ) - { - int usePlug = 0; - deviceInfo = GetDeviceInfo( hostApi, params->device ); - - /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */ - if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) ) - usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) ); - if( usePlug ) - snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName ); - else - deviceName = deviceInfo->alsaName; - } - else - deviceName = streamInfo->deviceString; - - if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK )) < 0 ) - { - *pcm = NULL; /* Not to be closed */ - ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination ); - } - ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError ); - -end: - return result; - -error: - goto end; -} - -static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters, - double sampleRate, StreamDirection streamDir ) -{ - PaError result = paNoError; - snd_pcm_t *pcm = NULL; - PaSampleFormat availableFormats; - /* We are able to adapt to a number of channels less than what the device supports */ - unsigned int numHostChannels; - PaSampleFormat hostFormat; - snd_pcm_hw_params_t *hwParams; - snd_pcm_hw_params_alloca( &hwParams ); - - if( !parameters->hostApiSpecificStreamInfo ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device ); - numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ? - devInfo->minInputChannels : devInfo->minOutputChannels ); - } - else - numHostChannels = parameters->channelCount; - - PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) ); - - snd_pcm_hw_params_any( pcm, hwParams ); - - if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 ) - { - result = paInvalidSampleRate; - goto error; - } - - if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 ) - { - result = paInvalidChannelCount; - goto error; - } - - /* See if we can find a best possible match */ - availableFormats = GetAvailableFormats( pcm ); - PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) ); - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); - - ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError ); - -end: - if( pcm ) - snd_pcm_close( pcm ); - return result; - -error: - goto end; -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount = 0, outputChannelCount = 0; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaError result = paFormatIsSupported; - - if( inputParameters ) - { - PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); - - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - - if( outputParameters ) - { - PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); - - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - if( inputChannelCount ) - { - if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In )) - != paNoError ) - goto error; - } - if ( outputChannelCount ) - { - if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out )) - != paNoError ) - goto error; - } - - return paFormatIsSupported; - -error: - return result; -} - -static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi, - const PaStreamParameters *params, StreamDirection streamDir, int callbackMode ) -{ - PaError result = paNoError; - PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat; - assert( params->channelCount > 0 ); - - /* Make sure things have an initial value */ - memset( self, 0, sizeof (PaAlsaStreamComponent) ); - - if( NULL == params->hostApiSpecificStreamInfo ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device ); - self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels - : devInfo->minOutputChannels ); - } - else - { - /* We're blissfully unaware of the minimum channelCount */ - self->numHostChannels = params->channelCount; - } - - PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) ); - self->nfds = snd_pcm_poll_descriptors_count( self->pcm ); - hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ); - - self->hostSampleFormat = hostSampleFormat; - self->nativeFormat = Pa2AlsaFormat( hostSampleFormat ); - self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved); - self->numUserChannels = params->channelCount; - self->streamDir = streamDir; - - if( !callbackMode && !self->userInterleaved ) - { - /* Pre-allocate non-interleaved user provided buffers */ - PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ), - paInsufficientMemory ); - } - -error: - return result; -} - -static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self ) -{ - snd_pcm_close( self->pcm ); - if( self->userBuffers ) - PaUtil_FreeMemory( self->userBuffers ); -} - -/** Configure the associated ALSA pcm. - * - */ -static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long - framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency ) -{ - /* - int numPeriods; - - if( getenv("PA_NUMPERIODS") != NULL ) - numPeriods = atoi( getenv("PA_NUMPERIODS") ); - else - numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1; - - PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer )); - if( numPeriods <= 1 ) - numPeriods = 2; - */ - - /* Configuration consists of setting all of ALSA's parameters. - * These parameters come in two flavors: hardware parameters - * and software paramters. Hardware parameters will affect - * the way the device is initialized, software parameters - * affect the way ALSA interacts with me, the user-level client. - */ - - snd_pcm_hw_params_t *hwParams; - snd_pcm_sw_params_t *swParams; - PaError result = paNoError; - snd_pcm_access_t accessMode, alternateAccessMode; - unsigned int numPeriods, minPeriods = 2; - int dir = 0; - snd_pcm_t *pcm = self->pcm; - PaTime latency = params->suggestedLatency; - double sr = *sampleRate; - *returnedLatency = -1.; - - snd_pcm_hw_params_alloca( &hwParams ); - snd_pcm_sw_params_alloca( &swParams ); - - self->framesPerBuffer = framesPerHostBuffer; - - /* ... fill up the configuration space with all possibile - * combinations of parameters this device will accept */ - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - - ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError ); - - if( self->userInterleaved ) - { - accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; - alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - } - else - { - accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; - } - - /* If requested access mode fails, try alternate mode */ - if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 ) - { - ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError ); - /* Flip mode */ - self->hostInterleaved = !self->userInterleaved; - } - - ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError ); - - ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate ); - ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError ); - /* reject if there's no sample rate within 1% of the one requested */ - if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 ) - { - PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr )); - PA_ENSURE( paInvalidSampleRate ); - } - - ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount ); - - /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */ - dir = 0; - ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError ); - dir = 0; - ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError ); - - /* Find an acceptable number of periods */ - numPeriods = (latency * sr) / self->framesPerBuffer + 1; - dir = 0; - ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError ); - /* Minimum of periods should already be 2 */ - PA_UNLESS( numPeriods >= 2, paInternalError ); - - /* Set the parameters! */ - ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); - - /* Latency in seconds, one period is not counted as latency */ - latency = (numPeriods - 1) * self->framesPerBuffer / sr; - - /* Now software parameters... */ - ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError ); - - ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError ); - - /* Silence buffer in the case of underrun */ - if( !primeBuffers ) /* XXX: Make sense? */ - { - snd_pcm_uframes_t boundary; - ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError ); - } - - ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError ); - - /* Set the parameters! */ - ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError ); - - *sampleRate = sr; - *returnedLatency = latency; - -end: - return result; - -error: - goto end; /* No particular action */ -} - -static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams, - const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback, - PaStreamFlags streamFlags, void *userData ) -{ - PaError result = paNoError; - assert( self ); - - memset( self, 0, sizeof (PaAlsaStream) ); - - if( NULL != callback ) - { - PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, - &alsaApi->callbackStreamInterface, - callback, userData ); - self->callbackMode = 1; - } - else - { - PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, - &alsaApi->blockingStreamInterface, - NULL, userData ); - } - - self->framesPerUserBuffer = framesPerUserBuffer; - self->neverDropInput = streamFlags & paNeverDropInput; - /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */ - /* - if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback ) - self->primeBuffers = 1; - */ - memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) ); - memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) ); - if( inParams ) - PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) ); - if( outParams ) - PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) ); - - assert( self->capture.nfds || self->playback.nfds ); - - PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds + - self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory ); - - PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate ); - InitializeThreading( &self->threading, &self->cpuLoadMeasurer ); - ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 ); - ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 ); - ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 ); - -error: - return result; -} - -/** Free resources associated with stream, and eventually stream itself. - * - * Frees allocated memory, and terminates individual StreamComponents. - */ -static void PaAlsaStream_Terminate( PaAlsaStream *self ) -{ - assert( self ); - - if( self->capture.pcm ) - { - PaAlsaStreamComponent_Terminate( &self->capture ); - } - if( self->playback.pcm ) - { - PaAlsaStreamComponent_Terminate( &self->playback ); - } - - PaUtil_FreeMemory( self->pfds ); - ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 ); - ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 ); - ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 ); - - PaUtil_FreeMemory( self ); -} - -/** Calculate polling timeout - * - * @param frames Time to wait - * @return Polling timeout in milliseconds - */ -static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames ) -{ - assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 ); - /* Period in msecs, rounded up */ - return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate ); -} - -/** Set up ALSA stream parameters. - * - */ -static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters - *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency, - unsigned long *maxHostBufferSize ) -{ - PaError result = paNoError; - double realSr = sampleRate; - - if( self->capture.pcm ) - PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers, - self->callbackMode, &realSr, inputLatency ) ); - if( self->playback.pcm ) - PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers, - self->callbackMode, &realSr, outputLatency ) ); - - /* Should be exact now */ - self->streamRepresentation.streamInfo.sampleRate = realSr; - - /* this will cause the two streams to automatically start/stop/prepare in sync. - * We only need to execute these operations on one of the pair. - * A: We don't want to do this on a blocking stream. - */ - if( self->callbackMode && self->capture.pcm && self->playback.pcm ) - { - int err = snd_pcm_link( self->capture.pcm, self->playback.pcm ); - if( err >= 0 ) - self->pcmsSynced = 1; - else - PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) )); - } - - /* Frames per host buffer for the stream is set as a compromise between the two directions */ - framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX, - self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX ); - self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */ - - *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0, - self->playback.pcm ? self->playback.bufferSize : 0 ); - - /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */ - self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000); - - if( self->callbackMode ) - { - /* If the user expects a certain number of frames per callback we will either have to rely on block adaption - * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number - * of host buffer frames with what the user specified */ - if( self->framesPerUserBuffer != paFramesPerBufferUnspecified ) - { - /* self->alignFrames = 1; */ - - /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely - * on block adaption */ - /* - if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm && - self->capture.framesPerBuffer != self->playback.framesPerBuffer) ) - self->useBlockAdaption = 1; - else - self->alignFrames = 1; - */ - } - } - -error: - return result; -} - -/* We need to determine how many frames per host buffer to use. Our - * goals are to provide the best possible performance, but also to - * most closely honor the requested latency settings. Therefore this - * decision is based on: - * - * - the period sizes that playback and/or capture support. The - * host buffer size has to be one of these. - * - the number of periods that playback and/or capture support. - * - * We want to make period_size*(num_periods-1) to be as close as possible - * to latency*rate for both playback and capture. - * - * This is one of those blocks of code that will just take a lot of - * refinement to be any good. - * - * In the full-duplex case it is possible that the routine was unable - * to find a number of frames per buffer acceptable to both devices - * TODO: Implement an algorithm to find the value closest to acceptance - * by both devices, to minimize difference between period sizes? - */ -static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi ) -{ - PaError result = paNoError; - unsigned long framesPerBuffer = 0; - int numHostInputChannels = 0, numHostOutputChannels = 0; - - /* XXX: Clean this up */ - if( stream->capture.pcm ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device ); - numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels ); - } - if( stream->playback.pcm ) - { - const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device ); - numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels ); - } - - if( stream->capture.pcm && stream->playback.pcm ) - { - snd_pcm_uframes_t desiredLatency, e; - snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture, - optimalPeriodSize, periodSize; - int dir = 0; - unsigned int minPeriods = 2; - - snd_pcm_t *pcm; - snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture; - - snd_pcm_hw_params_alloca( &hwParamsPlayback ); - snd_pcm_hw_params_alloca( &hwParamsCapture ); - - /* Come up with a common desired latency */ - pcm = stream->playback.pcm; - snd_pcm_hw_params_any( pcm, hwParamsPlayback ); - ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate ); - ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ), - paBadIODeviceCombination ); - - ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); - - pcm = stream->capture.pcm; - ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError ); - ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination ); - ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ), - paBadIODeviceCombination ); - - ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); - - minPeriodSize = PA_MAX( minPlayback, minCapture ); - maxPeriodSize = PA_MIN( maxPlayback, maxCapture ); - - desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency ) - * sampleRate); - /* Clamp desiredLatency */ - { - snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX; - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError ); - maxBufferSize = PA_MIN( maxBufferSize, tmp ); - - desiredLatency = PA_MIN( desiredLatency, maxBufferSize ); - } - - /* Find the closest power of 2 */ - e = ilogb( minPeriodSize ); - if( minPeriodSize & (minPeriodSize - 1) ) - e += 1; - periodSize = (snd_pcm_uframes_t) pow( 2, e ); - - while( periodSize <= maxPeriodSize ) - { - if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 && - snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 ) - break; /* Ok! */ - - periodSize *= 2; - } - - /* 4 periods considered optimal */ - optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); - optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); - - /* Find the closest power of 2 */ - e = ilogb( optimalPeriodSize ); - if( optimalPeriodSize & (optimalPeriodSize - 1) ) - e += 1; - optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e ); - - while( optimalPeriodSize >= periodSize ) - { - pcm = stream->playback.pcm; - if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 ) - continue; - - pcm = stream->capture.pcm; - if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 ) - break; - - optimalPeriodSize /= 2; - } - - if( optimalPeriodSize > periodSize ) - periodSize = optimalPeriodSize; - - if( periodSize <= maxPeriodSize ) - { - /* Looks good */ - framesPerBuffer = periodSize; - } - else - { - /* Unable to find a common period size, oh well */ - optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); - optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); - - /* ConfigureStream should find individual period sizes acceptable for each device */ - framesPerBuffer = optimalPeriodSize; - /* PA_ENSURE( paBadIODeviceCombination ); */ - } - } - else /* half-duplex is a slightly simpler case */ - { - unsigned long bufferSize, channels; - snd_pcm_t *pcm; - snd_pcm_hw_params_t *hwParams; - - snd_pcm_hw_params_alloca( &hwParams ); - - if( stream->capture.pcm ) - { - pcm = stream->capture.pcm; - bufferSize = inputParameters->suggestedLatency * sampleRate; - channels = numHostInputChannels; - } - else - { - pcm = stream->playback.pcm; - bufferSize = outputParameters->suggestedLatency * sampleRate; - channels = numHostOutputChannels; - } - - ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate ); - ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination ); - - ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); - - /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period), - finding a combination of period/buffer size which best fits these constraints */ - framesPerBuffer = bufferSize / 4; - bufferSize += framesPerBuffer; /* One period doesn't count as latency */ - ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError ); - ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError ); - } - - PA_UNLESS( framesPerBuffer != 0, paInternalError ); - *determinedFrames = framesPerBuffer; - -error: - return result; -} - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *callback, - void *userData ) -{ - PaError result = paNoError; - PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; - PaAlsaStream *stream = NULL; - PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0; - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; - int numInputChannels = 0, numOutputChannels = 0; - PaTime inputLatency, outputLatency; - unsigned long framesPerHostBuffer; - PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize; - unsigned long maxHostBufferSize; /* Upper bound of host buffer size */ - - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; - - if( inputParameters ) - { - PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); - - numInputChannels = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - if( outputParameters ) - { - PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); - - numOutputChannels = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - /* XXX: Why do we support this anyway? */ - if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL ) - { - PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ )); - framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") ); - } - framesPerHostBuffer = framesPerBuffer; - - PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory ); - PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate, - framesPerBuffer, callback, streamFlags, userData ) ); - - /* If the number of frames per buffer is unspecified, we have to come up with - * one. This is both a blessing and a curse: a blessing because we can optimize - * the number to best meet the requirements, but a curse because that's really - * hard to do well. For this reason we also support an interface where the user - * specifies these by setting environment variables. */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) - { - PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer, - hostApi ) ); - } - - PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer, - &inputLatency, &outputLatency, &maxHostBufferSize ) ); - hostInputSampleFormat = stream->capture.hostSampleFormat; - hostOutputSampleFormat = stream->playback.hostSampleFormat; - - if( framesPerHostBuffer != framesPerBuffer ) - { - PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ )); - } - - PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - numInputChannels, inputSampleFormat, hostInputSampleFormat, - numOutputChannels, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize, - hostBufferSizeMode, callback, userData ) ); - - /* Ok, buffer processor is initialized, now we can deduce it's latency */ - if( numInputChannels > 0 ) - stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency( - &stream->bufferProcessor ); - if( numOutputChannels > 0 ) - stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency( - &stream->bufferProcessor ); - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - PaAlsaStream_Terminate( stream ); - - return result; -} - -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - - PaAlsaStream_Terminate( stream ); - - return result; -} - -static void SilenceBuffer( PaAlsaStream *stream ) -{ - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset; - - snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames ); - snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat ); - snd_pcm_mmap_commit( stream->playback.pcm, offset, frames ); -} - -/** Start/prepare pcm(s) for streaming. - * - * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply - * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and - * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will - * be started automatically as the user writes to output. - * - * The capture pcm, however, will simply be prepared and started. - * - * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode) - */ -static PaError AlsaStart( PaAlsaStream *stream, int priming ) -{ - PaError result = paNoError; - - if( stream->playback.pcm ) - { - if( stream->callbackMode ) - { - if( !priming ) - { - /* Buffer isn't primed, so prepare and silence */ - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - SilenceBuffer( stream ); - } - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); - } - else - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - } - if( stream->capture.pcm && !stream->pcmsSynced ) - { - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); - /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */ - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); - } - -end: - return result; -error: - goto end; -} - -/** Utility function for determining if pcms are in running state. - * - */ -static int IsRunning( PaAlsaStream *stream ) -{ - int result = 0; - - ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */ - if( stream->capture.pcm ) - { - snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm ); - - if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN - || capture_state == SND_PCM_STATE_DRAINING ) - { - result = 1; - goto end; - } - } - - if( stream->playback.pcm ) - { - snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm ); - - if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN - || playback_state == SND_PCM_STATE_DRAINING ) - { - result = 1; - goto end; - } - } - -end: - ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 ); - - return result; -} - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - int streamStarted = 0; /* So we can know wether we need to take the stream down */ - - /* Ready the processor */ - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - /* Set now, so we can test for activity further down */ - stream->isActive = 1; - - if( stream->callbackMode ) - { - int res = 0; - PaTime pt = PaUtil_GetTime(); - struct timespec ts; - - PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) ); - streamStarted = 1; - - /* Wait for stream to be started */ - ts.tv_sec = (time_t) floor( pt + 1 ); - ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000); - - /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking - * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely - * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */ - ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 ); - - /* Due to possible spurious wakeups, we enclose in a loop */ - while( !IsRunning( stream ) && IsStreamActive( s ) && !res ) - { - res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts ); - } - ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 ); - - PA_UNLESS( !res || res == ETIMEDOUT, paInternalError ); - PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt )); - - if( res == ETIMEDOUT ) - { - PA_ENSURE( paTimedOut ); - } - } - else - { - PA_ENSURE( AlsaStart( stream, 0 ) ); - streamStarted = 1; - } - -end: - return result; -error: - if( streamStarted ) - AbortStream( stream ); - stream->isActive = 0; - - goto end; -} - -static PaError AlsaStop( PaAlsaStream *stream, int abort ) -{ - PaError result = paNoError; - - if( abort ) - { - if( stream->playback.pcm ) - ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError ); - if( stream->capture.pcm && !stream->pcmsSynced ) - ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError ); - - PA_DEBUG(( "Dropped frames\n" )); - } - else - { - if( stream->playback.pcm ) - ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError ); - if( stream->capture.pcm && !stream->pcmsSynced ) - ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError ); - } - -end: - return result; -error: - goto end; -} - -/** Stop or abort stream. - * - * If a stream is in callback mode we will have to inspect wether the background thread has - * finished, or we will have to take it out. In either case we join the thread before - * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish - * buffers (drain) - * - * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function - */ -static PaError RealStop( PaAlsaStream *stream, int abort ) -{ - PaError result = paNoError; - - /* First deal with the callback thread, cancelling and/or joining - * it if necessary - */ - if( stream->callbackMode ) - { - PaError threadRes, watchdogRes; - stream->callbackAbort = abort; - - if( !abort ) - { - PA_DEBUG(( "Stopping callback\n" )); - stream->callbackStop = 1; - } - PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) ); - if( threadRes != paNoError ) - PA_DEBUG(( "Callback thread returned: %d\n", threadRes )); - if( watchdogRes != paNoError ) - PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes )); - - stream->callbackStop = 0; /* The deed is done */ - stream->callback_finished = 0; - } - else - { - PA_ENSURE( AlsaStop( stream, abort ) ); - } - - stream->isActive = 0; - -end: - return result; - -error: - goto end; -} - -static PaError StopStream( PaStream *s ) -{ - return RealStop( (PaAlsaStream *) s, 0 ); -} - -static PaError AbortStream( PaStream *s ) -{ - return RealStop( (PaAlsaStream * ) s, 1 ); -} - -/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback - * returning !paContinue is not considered) - * - */ -static PaError IsStreamStopped( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream *)s; - - /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */ - return !IsStreamActive( s ) && !stream->callback_finished; -} - -static PaError IsStreamActive( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - return stream->isActive; -} - -static PaTime GetStreamTime( PaStream *s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - - snd_timestamp_t timestamp; - snd_pcm_status_t *status; - snd_pcm_status_alloca( &status ); - - /* TODO: what if we have both? does it really matter? */ - - /* TODO: if running in callback mode, this will mean - * libasound routines are being called from multiple threads. - * need to verify that libasound is thread-safe. */ - - if( stream->capture.pcm ) - { - snd_pcm_status( stream->capture.pcm, status ); - } - else if( stream->playback.pcm ) - { - snd_pcm_status( stream->playback.pcm, status ); - } - - snd_pcm_status_get_tstamp( status, ×tamp ); - return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0; -} - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaAlsaStream *stream = (PaAlsaStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - -static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ) -{ - unsigned long approx = (unsigned long) sampleRate; - int dir = 0; - double fraction = sampleRate - approx; - - assert( pcm && hwParams ); - - if( fraction > 0.0 ) - { - if( fraction > 0.5 ) - { - ++approx; - dir = -1; - } - else - dir = 1; - } - - return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ); -} - -/* Return exact sample rate in param sampleRate */ -static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ) -{ - unsigned int num, den; - int err; - - assert( hwParams ); - - err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den ); - *sampleRate = (double) num / den; - - return err; -} - -/* Utility functions for blocking/callback interfaces */ - -/* Atomic restart of stream (we don't want the intermediate state visible) */ -static PaError AlsaRestart( PaAlsaStream *stream ) -{ - PaError result = paNoError; - - ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); - PA_ENSURE( AlsaStop( stream, 0 ) ); - PA_ENSURE( AlsaStart( stream, 0 ) ); - - PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ )); - -error: - ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 ); - return result; -} - -/** Recover from xrun state. - * - */ -static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self ) -{ - PaError result = paNoError; - snd_pcm_status_t *st; - PaTime now = PaUtil_GetTime(); - snd_timestamp_t t; - - snd_pcm_status_alloca( &st ); - - if( self->playback.pcm ) - { - snd_pcm_status( self->playback.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) - { - snd_pcm_status_get_trigger_tstamp( st, &t ); - self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); - } - } - if( self->capture.pcm ) - { - snd_pcm_status( self->capture.pcm, st ); - if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) - { - snd_pcm_status_get_trigger_tstamp( st, &t ); - self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); - } - } - - PA_ENSURE( AlsaRestart( self ) ); - -end: - return result; -error: - goto end; -} - -/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout. - * - */ -static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll ) -{ - PaError result = paNoError; - snd_pcm_sframes_t delay, margin; - int err; - const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL; - - *continuePoll = 1; - - if( StreamDirection_In == streamDir ) - { - component = &stream->capture; - otherComponent = &stream->playback; - } - else - { - component = &stream->playback; - otherComponent = &stream->capture; - } - - /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */ - if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 ) - { - if( err == -EPIPE ) - { - /* Xrun */ - *continuePoll = 0; - goto error; - } - - ENSURE_( err, paUnanticipatedHostError ); - } - - if( StreamDirection_Out == streamDir ) - { - /* Number of eligible frames before capture overrun */ - delay = otherComponent->bufferSize - delay; - } - margin = delay - otherComponent->framesPerBuffer / 2; - - if( margin < 0 ) - { - PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" )); - *continuePoll = 0; - } - else if( margin < otherComponent->framesPerBuffer ) - { - *pollTimeout = CalculatePollTimeout( stream, margin ); - PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n", - __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout )); - } - -error: - return result; -} - -/* Callback interface */ - -static void OnExit( void *data ) -{ - PaAlsaStream *stream = (PaAlsaStream *) data; - - assert( data ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */ - AlsaStop( stream, stream->callbackAbort ); - stream->callbackAbort = 0; /* Clear state */ - - PA_DEBUG(( "OnExit: Stoppage\n" )); - - /* Eventually notify user all buffers have played */ - if( stream->streamRepresentation.streamFinishedCallback ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - stream->isActive = 0; -} - -static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo ) -{ - snd_pcm_status_t *capture_status, *playback_status; - snd_timestamp_t capture_timestamp, playback_timestamp; - PaTime capture_time = 0., playback_time = 0.; - - snd_pcm_status_alloca( &capture_status ); - snd_pcm_status_alloca( &playback_status ); - - if( stream->capture.pcm ) - { - snd_pcm_sframes_t capture_delay; - - snd_pcm_status( stream->capture.pcm, capture_status ); - snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); - - capture_time = capture_timestamp.tv_sec + - ((PaTime)capture_timestamp.tv_usec / 1000000.0); - timeInfo->currentTime = capture_time; - - capture_delay = snd_pcm_status_get_delay( capture_status ); - timeInfo->inputBufferAdcTime = timeInfo->currentTime - - (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; - } - if( stream->playback.pcm ) - { - snd_pcm_sframes_t playback_delay; - - snd_pcm_status( stream->playback.pcm, playback_status ); - snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); - - playback_time = playback_timestamp.tv_sec + - ((PaTime)playback_timestamp.tv_usec / 1000000.0); - - if( stream->capture.pcm ) /* Full duplex */ - { - /* Hmm, we have both a playback and a capture timestamp. - * Hopefully they are the same... */ - if( fabs( capture_time - playback_time ) > 0.01 ) - PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time))); - } - else - timeInfo->currentTime = playback_time; - - playback_delay = snd_pcm_status_get_delay( playback_status ); - timeInfo->outputBufferDacTime = timeInfo->currentTime + - (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; - } -} - -/** Called after buffer processing is finished. - * - * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime. - * - * @param numFrames The number of frames that has been processed - * @param xrun Return whether an xrun has occurred - */ -static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun ) -{ - PaError result = paNoError; - int res; - - /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed - * afterwards - */ - if( !self->ready ) - goto end; - - res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames ); - if( res == -EPIPE || res == -ESTRPIPE ) - { - *xrun = 1; - } - else - { - ENSURE_( res, paUnanticipatedHostError ); - } - -end: -error: - return result; -} - -/* Extract buffer from channel area */ -static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset ) -{ - return (unsigned char *) area->addr + (area->first + offset * area->step) / 8; -} - -/** Do necessary adaption between user and host channels. - * - @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and - duplicating mono information if host outputs come in pairs. - */ -static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames ) -{ - PaError result = paNoError; - unsigned char *p; - int i; - int unusedChans = self->numHostChannels - self->numUserChannels; - unsigned char *src, *dst; - int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0; - - assert( StreamDirection_Out == self->streamDir ); - - if( self->hostInterleaved ) - { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); - unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset ); - - /* Start after the last user channel */ - p = buffer + self->numUserChannels * swidth; - - if( convertMono ) - { - /* Convert the last user channel into stereo pair */ - src = buffer + (self->numUserChannels - 1) * swidth; - for( i = 0; i < numFrames; ++i ) - { - dst = src + swidth; - memcpy( dst, src, swidth ); - src += self->numHostChannels * swidth; - } - - /* Don't touch the channel we just wrote to */ - p += swidth; - --unusedChans; - } - - if( unusedChans > 0 ) - { - /* Silence unused output channels */ - for( i = 0; i < numFrames; ++i ) - { - memset( p, 0, swidth * unusedChans ); - p += self->numHostChannels * swidth; - } - } - } - else - { - /* We extract the last user channel */ - if( convertMono ) - { - ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas + - (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError ); - --unusedChans; - } - if( unusedChans > 0 ) - { - snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames, - self->nativeFormat ); - } - } - -error: - return result; -} - -static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - int xrun = 0; - - if( self->capture.pcm ) - { - PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) ); - } - if( self->playback.pcm ) - { - if( self->playback.numHostChannels > self->playback.numUserChannels ) - PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) ); - PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) ); - } - -error: - *xrunOccurred = xrun; - return result; -} - -/** Update the number of available frames. - * - */ -static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm ); - *xrunOccurred = 0; - - if( -EPIPE == framesAvail ) - { - *xrunOccurred = 1; - framesAvail = 0; - } - else - ENSURE_( framesAvail, paUnanticipatedHostError ); - - *numFrames = framesAvail; - -error: - return result; -} - -/** Fill in pollfd objects. - */ -static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds ) -{ - PaError result = paNoError; - int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds ); - assert( ret == self->nfds ); - - self->ready = 0; - - return result; -} - -/** Examine results from poll(). - * - * @param pfds pollfds to inspect - * @param shouldPoll Should we continue to poll - * @param xrun Has an xrun occurred - */ -static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun ) -{ - PaError result = paNoError; - unsigned short revents; - - ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError ); - if( revents != 0 ) - { - if( revents & POLLERR ) - { - *xrun = 1; - } - else - self->ready = 1; - - *shouldPoll = 0; - } - -error: - return result; -} - -/** Return the number of available frames for this stream. - * - * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore - * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback. - * - * @param queryCapture Check available for capture - * @param queryPlayback Check available for playback - * @param available The returned number of frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long - *available, int *xrunOccurred ) -{ - PaError result = paNoError; - unsigned long captureFrames, playbackFrames; - *xrunOccurred = 0; - - assert( queryCapture || queryPlayback ); - - if( queryCapture ) - { - assert( self->capture.pcm ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) ); - if( *xrunOccurred ) - goto end; - } - if( queryPlayback ) - { - assert( self->playback.pcm ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) ); - if( *xrunOccurred ) - goto end; - } - - if( queryCapture && queryPlayback ) - { - *available = PA_MIN( captureFrames, playbackFrames ); - } - else if( queryCapture ) - { - *available = captureFrames; - } - else - { - *available = playbackFrames; - } - -end: -error: - return result; -} - -/** Wait for and report available buffer space from ALSA. - * - * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more. - * Both of these operations can uncover xrun conditions. - * - * @concern Xruns Both polling and querying available frames can report an xrun condition. - * - * @param framesAvail Return the number of available frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred ) -{ - PaError result = paNoError; - int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL; - int pollTimeout = self->pollTimeout; - int xrun = 0; - - assert( self ); - assert( framesAvail ); - - if( !self->callbackMode ) - { - /* In blocking mode we will only wait if necessary */ - PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL, - framesAvail, &xrun ) ); - if( xrun ) - { - goto end; - } - - if( *framesAvail > 0 ) - { - /* Mark pcms ready from poll */ - if( self->capture.pcm ) - self->capture.ready = 1; - if( self->playback.pcm ) - self->playback.ready = 1; - - goto end; - } - } - - while( pollPlayback || pollCapture ) - { - int totalFds = 0; - struct pollfd *capturePfds = NULL, *playbackPfds = NULL; - - pthread_testcancel(); - - if( pollCapture ) - { - capturePfds = self->pfds; - PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) ); - totalFds += self->capture.nfds; - } - if( pollPlayback ) - { - playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0); - PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) ); - totalFds += self->playback.nfds; - } - - if( poll( self->pfds, totalFds, pollTimeout ) < 0 ) - { - /* XXX: Depend on preprocessor condition? */ - if( errno == EINTR ) { /* gdb */ - continue; - } - - /* TODO: Add macro for checking system calls */ - PA_ENSURE( paInternalError ); - } - - /* check the return status of our pfds */ - if( pollCapture ) - { - PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) ); - } - if( pollPlayback ) - { - PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) ); - } - if( xrun ) - { - break; - } - - /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two. - * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will - * stop polling. - */ - if( self->capture.pcm && self->playback.pcm ) - { - if( pollCapture && !pollPlayback ) - { - PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) ); - } - else if( pollPlayback && !pollCapture ) - { - PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) ); - } - } - } - - if( !xrun ) - { - /* Get the number of available frames for the pcms that are marked ready. - * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for - * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput - * is not specified. - */ - int captureReady = self->capture.pcm ? self->capture.ready : 0, - playbackReady = self->playback.pcm ? self->playback.ready : 0; - PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) ); - - if( self->capture.pcm && self->playback.pcm ) - { - if( !self->playback.ready && !self->neverDropInput ) - { - /* TODO: Drop input */ - } - } - } - -end: -error: - if( xrun ) - { - /* Recover from the xrun state */ - PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); - *framesAvail = 0; - } - *xrunOccurred = xrun; - - return result; -} - -/** Register per-channel ALSA buffer information with buffer processor. - * - * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the - * number of host and user channels is taken into account. - * - * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames. - */ -static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, - unsigned long *numFrames, int *xrun ) -{ - PaError result = paNoError; - const snd_pcm_channel_area_t *areas, *area; - void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) = - StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel; - unsigned char *buffer, *p; - int i; - unsigned long framesAvail; - - /* This _must_ be called before mmap_begin */ - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) ); - if( *xrun ) - { - *numFrames = 0; - goto end; - } - - ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError ); - - if( self->hostInterleaved ) - { - int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); - - p = buffer = ExtractAddress( areas, self->offset ); - for( i = 0; i < self->numUserChannels; ++i ) - { - /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */ - setChannel( bp, i, p, self->numHostChannels ); - p += swidth; - } - } - else - { - for( i = 0; i < self->numUserChannels; ++i ) - { - area = areas + i; - buffer = ExtractAddress( area, self->offset ); - setChannel( bp, i, buffer, 1 ); - } - } - - /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */ - self->channelAreas = (snd_pcm_channel_area_t *)areas; - -end: -error: - return result; -} - -/** Initiate buffer processing. - * - * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set. - * - * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is - * calculated. - * - * @param numFrames On entrance the number of available frames, on exit the number of received frames - * @param xrunOccurred Return whether an xrun has occurred - */ -static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred ) -{ - PaError result = paNoError; - unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0; - int xrun = 0; - - /* Extract per-channel ALSA buffer pointers and register them with the buffer processor. - * It is possible that a direction is not marked ready however, because it is out of sync with the other. - */ - if( self->capture.pcm && self->capture.ready ) - { - captureFrames = *numFrames; - PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames, - &xrun ) ); - } - if( self->playback.pcm && self->playback.ready ) - { - playbackFrames = *numFrames; - PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames, - &xrun ) ); - } - if( xrun ) - { - /* Nothing more to do */ - assert( 0 == commonFrames ); - goto end; - } - - commonFrames = PA_MIN( captureFrames, playbackFrames ); - assert( commonFrames <= *numFrames ); - - /* Inform PortAudio of the number of frames we got. - * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on - * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply - * discard the excess input or call the callback with paOutputOverflow flagged. - */ - if( self->capture.pcm ) - { - if( self->capture.ready ) - { - PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames ); - } - else - { - /* We have input underflow */ - PaUtil_SetNoInput( &self->bufferProcessor ); - } - } - if( self->playback.pcm ) - { - if( self->playback.ready ) - { - PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames ); - } - else - { - /* We have output underflow, but keeping input data (paNeverDropInput) */ - /* assert( self->neverDropInput ); */ - PaUtil_SetNoOutput( &self->bufferProcessor ); - } - } - -end: - *numFrames = commonFrames; -error: - if( xrun ) - { - PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); - *numFrames = 0; - } - *xrunOccurred = xrun; - - return result; -} - -/** Callback thread's function. - * - * Roughly, the workflow can be described in the following way: The number of available frames that can be processed - * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount - * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with - * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can - * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected). - */ -static void *CallbackThreadFunc( void *userData ) -{ - PaError result = paNoError, *pres = NULL; - PaAlsaStream *stream = (PaAlsaStream*) userData; - PaStreamCallbackTimeInfo timeInfo = {0, 0, 0}; - snd_pcm_sframes_t startThreshold = 0; - int callbackResult = paContinue; - PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */ - int streamStarted = 0; - - assert( stream ); - - callbackThread_ = pthread_self(); - /* Execute OnExit when exiting */ - pthread_cleanup_push( &OnExit, stream ); - - /* Not implemented */ - assert( !stream->primeBuffers ); - - /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the - * stream is started immediately. The latter involves signaling the waiting main thread. - */ - if( stream->primeBuffers ) - { - snd_pcm_sframes_t avail; - - if( stream->playback.pcm ) - ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); - if( stream->capture.pcm && !stream->pcmsSynced ) - ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); - - /* We can't be certain that the whole ring buffer is available for priming, but there should be - * at least one period */ - avail = snd_pcm_avail_update( stream->playback.pcm ); - startThreshold = avail - (avail % stream->playback.framesPerBuffer); - assert( startThreshold >= stream->playback.framesPerBuffer ); - } - else - { - ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 ); - PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */ - ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 ); - ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 ); - - streamStarted = 1; - } - - while( 1 ) - { - unsigned long framesAvail, framesGot; - int xrun = 0; - - pthread_testcancel(); - - /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively - * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output). - */ - if( stream->callbackStop && paContinue == callbackResult ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - if( paContinue != callbackResult ) - { - stream->callbackAbort = (paAbort == callbackResult); - if( stream->callbackAbort || - /** @concern BlockAdaption Go on if adaption buffers are empty */ - PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - goto end; - - PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ )); - /* There is still buffered output that needs to be processed */ - } - - /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have - * a number of available frames. - */ - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - if( xrun ) - { - assert( 0 == framesAvail ); - continue; - - /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due - * to constant xruns, it might be desirable to notify the user of this. - */ - } - - /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the - * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller - * portions at a time than is available as a whole. Therefore we should be prepared to process several - * chunks successively. The buffers are passed to the PA buffer processor. - */ - while( framesAvail > 0 ) - { - xrun = 0; - - pthread_testcancel(); - - /** @concern Xruns Under/overflows are to be reported to the callback */ - if( stream->underrun > 0.0 ) - { - cbFlags |= paOutputUnderflow; - stream->underrun = 0.0; - } - if( stream->overrun > 0.0 ) - { - cbFlags |= paInputOverflow; - stream->overrun = 0.0; - } - if( stream->capture.pcm && stream->playback.pcm ) - { - /** @concern FullDuplex It's possible that only one direction is being processed to avoid an - * under- or overflow, this should be reported correspondingly */ - if( !stream->capture.ready ) - { - cbFlags |= paInputUnderflow; - PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ )); - } - else if( !stream->playback.ready ) - { - cbFlags |= paOutputOverflow; - PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ )); - } - } - - CallbackUpdate( &stream->threading ); - CalculateTimeInfo( stream, &timeInfo ); - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); - cbFlags = 0; - - /* CPU load measurement should include processing activivity external to the stream callback */ - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - framesGot = framesAvail; - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - framesAvail -= framesGot; - - if( framesGot > 0 ) - { - assert( !xrun ); - - PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - } - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot ); - - if( framesGot == 0 ) - { - if( !xrun ) - PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ )); - - /* Go back to polling for more frames */ - break; - - } - - if( paContinue != callbackResult ) - break; - } - } - - /* Match pthread_cleanup_push */ - pthread_cleanup_pop( 1 ); - -end: - pthread_exit( pres ); - -error: - /* Pass on error code */ - pres = malloc( sizeof (PaError) ); - *pres = result; - - goto end; -} - -/* Blocking interface */ - -static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long framesGot, framesAvail; - void *userBuffer; - snd_pcm_t *save = stream->playback.pcm; - - assert( stream ); - - PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream ); - - /* Disregard playback */ - stream->playback.pcm = NULL; - - if( stream->overrun > 0. ) - { - result = paInputOverflowed; - stream->overrun = 0.0; - } - - if( stream->capture.userInterleaved ) - userBuffer = buffer; - else - { - /* Copy channels into local array */ - userBuffer = stream->capture.userBuffers; - memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels ); - } - - /* Start stream if in prepared state */ - if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) - { - ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); - } - - while( frames > 0 ) - { - int xrun = 0; - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - framesGot = PA_MIN( framesAvail, frames ); - - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - frames -= framesGot; - } - } - -end: - stream->playback.pcm = save; - return result; -error: - goto end; -} - -static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames ) -{ - PaError result = paNoError; - signed long err; - PaAlsaStream *stream = (PaAlsaStream*)s; - snd_pcm_uframes_t framesGot, framesAvail; - const void *userBuffer; - snd_pcm_t *save = stream->capture.pcm; - - assert( stream ); - - PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream ); - - /* Disregard capture */ - stream->capture.pcm = NULL; - - if( stream->underrun > 0. ) - { - result = paOutputUnderflowed; - stream->underrun = 0.0; - } - - if( stream->playback.userInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->playback.userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels ); - } - - while( frames > 0 ) - { - int xrun = 0; - snd_pcm_uframes_t hwAvail; - - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - framesGot = PA_MIN( framesAvail, frames ); - - PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); - if( framesGot > 0 ) - { - framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot ); - PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); - frames -= framesGot; - } - - /* Frames residing in buffer */ - PA_ENSURE( err = GetStreamWriteAvailable( stream ) ); - framesAvail = err; - hwAvail = stream->playback.bufferSize - framesAvail; - - /* Start stream after one period of samples worth */ - if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED && - hwAvail >= stream->playback.framesPerBuffer ) - { - ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); - } - } - -end: - stream->capture.pcm = save; - return result; -error: - goto end; -} - -/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */ -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long avail; - int xrun; - - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); - if( xrun ) - { - PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); - if( xrun ) - PA_ENSURE( paInputOverflowed ); - } - - return (signed long)avail; - -error: - return result; -} - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaError result = paNoError; - PaAlsaStream *stream = (PaAlsaStream*)s; - unsigned long avail; - int xrun; - - PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) ); - if( xrun ) - { - snd_pcm_sframes_t savail; - - PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); - savail = snd_pcm_avail_update( stream->playback.pcm ); - - /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */ - ENSURE_( savail, paUnanticipatedHostError ); - - avail = (unsigned long) savail; - } - - return (signed long)avail; - -error: - return result; -} - -/* Extensions */ - -/* Initialize host api specific structure */ -void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ) -{ - info->size = sizeof (PaAlsaStreamInfo); - info->hostApiType = paALSA; - info->version = 1; - info->deviceString = NULL; -} - -void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ) -{ - PaAlsaStream *stream = (PaAlsaStream *) s; - stream->threading.rtSched = enable; -} - -void PaAlsa_EnableWatchdog( PaStream *s, int enable ) -{ - PaAlsaStream *stream = (PaAlsaStream *) s; - stream->threading.useWatchdog = enable; -} +/* + * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * ALSA implementation by Joshua Haberman and Arve Knudsen + * + * Copyright (c) 2002 Joshua Haberman + * Copyright (c) 2005 Arve Knudsen + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +#define ALSA_PCM_NEW_HW_PARAMS_API +#define ALSA_PCM_NEW_SW_PARAMS_API +#include +#undef ALSA_PCM_NEW_HW_PARAMS_API +#undef ALSA_PCM_NEW_SW_PARAMS_API + +#include +#include /* strlen() */ +#include +#include +#include +#include +#include +#include +#include /* For sig_atomic_t */ + +#include "portaudio.h" +#include "pa_util.h" +#include "pa_unix_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "pa_linux_alsa.h" + +/* Check return value of ALSA function, and map it to PaError */ +#define ENSURE_(expr, code) \ + do { \ + if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \ + { \ + /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ + if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \ + { \ + PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \ + } \ + PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + result = (code); \ + goto error; \ + } \ + } while( 0 ); + +#define ASSERT_CALL_(expr, success) \ + aErr_ = (expr); \ + assert( aErr_ == success ); + +static int aErr_; /* Used with ENSURE_ */ +static pthread_t callbackThread_; + +typedef enum +{ + StreamDirection_In, + StreamDirection_Out +} StreamDirection; + +/* Threading utility struct */ +typedef struct PaAlsaThreading +{ + pthread_t watchdogThread; + pthread_t callbackThread; + int watchdogRunning; + int rtSched; + int rtPrio; + int useWatchdog; + unsigned long throttledSleepTime; + volatile PaTime callbackTime; + volatile PaTime callbackCpuTime; + PaUtilCpuLoadMeasurer *cpuLoadMeasurer; +} PaAlsaThreading; + +typedef struct +{ + PaSampleFormat hostSampleFormat; + unsigned long framesPerBuffer; + int numUserChannels, numHostChannels; + int userInterleaved, hostInterleaved; + + snd_pcm_t *pcm; + snd_pcm_uframes_t bufferSize; + snd_pcm_format_t nativeFormat; + unsigned int nfds; + int ready; /* Marked ready from poll */ + void **userBuffers; + snd_pcm_uframes_t offset; + StreamDirection streamDir; + + snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */ +} PaAlsaStreamComponent; + +/* Implementation specific stream structure */ +typedef struct PaAlsaStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + PaAlsaThreading threading; + + unsigned long framesPerUserBuffer; + + int primeBuffers; + int callbackMode; /* bool: are we running in callback mode? */ + int pcmsSynced; /* Have we successfully synced pcms */ + + /* the callback thread uses these to poll the sound device(s), waiting + * for data to be ready/available */ + struct pollfd *pfds; + int pollTimeout; + + /* Used in communication between threads */ + volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */ + volatile sig_atomic_t callbackAbort; /* Drop frames? */ + volatile sig_atomic_t callbackStop; /* Signal a stop */ + volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */ + pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */ + pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */ + pthread_cond_t startCond; /* Wait untill audio is started in callback thread */ + + int neverDropInput; + + PaTime underrun; + PaTime overrun; + + PaAlsaStreamComponent capture, playback; +} +PaAlsaStream; + +/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct PaAlsaHostApiRepresentation +{ + PaUtilHostApiRepresentation commonHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + PaHostApiIndex hostApiIndex; +} +PaAlsaHostApiRepresentation; + +typedef struct PaAlsaDeviceInfo +{ + PaDeviceInfo commonDeviceInfo; + char *alsaName; + int isPlug; + int minInputChannels; + int minOutputChannels; +} +PaAlsaDeviceInfo; + +/* Threading utilities */ + +static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm ) +{ + th->watchdogRunning = 0; + th->rtSched = 0; + th->callbackTime = 0; + th->callbackCpuTime = 0; + th->useWatchdog = 1; + th->throttledSleepTime = 0; + th->cpuLoadMeasurer = clm; + + th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2 + + sched_get_priority_min( SCHED_FIFO ); +} + +static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult ) +{ + PaError result = paNoError; + void *pret; + + if( exitResult ) + *exitResult = paNoError; + if( watchdogExitResult ) + *watchdogExitResult = paNoError; + + if( th->watchdogRunning ) + { + pthread_cancel( th->watchdogThread ); + ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 ); + + if( pret && pret != PTHREAD_CANCELED ) + { + if( watchdogExitResult ) + *watchdogExitResult = *(PaError *) pret; + free( pret ); + } + } + + /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ + /* TODO: Make join time out */ + if( !wait ) + pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */ + ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 ); + + if( pret && pret != PTHREAD_CANCELED ) + { + if( exitResult ) + *exitResult = *(PaError *) pret; + free( pret ); + } + + return result; +} + +static void OnWatchdogExit( void *userData ) +{ + PaAlsaThreading *th = (PaAlsaThreading *) userData; + struct sched_param spm = { 0 }; + assert( th ); + + ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */ + PA_DEBUG(( "Watchdog exiting\n" )); +} + +static PaError BoostPriority( PaAlsaThreading *th ) +{ + PaError result = paNoError; + struct sched_param spm = { 0 }; + spm.sched_priority = th->rtPrio; + + assert( th ); + + if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 ) + { + PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */ + PA_DEBUG(( "Failed bumping priority\n" )); + result = 0; + } + else + result = 1; /* Success */ +error: + return result; +} + +static void *WatchdogFunc( void *userData ) +{ + PaError result = paNoError, *pres = NULL; + int err; + PaAlsaThreading *th = (PaAlsaThreading *) userData; + unsigned intervalMsec = 500; + const PaTime maxSeconds = 3.; /* Max seconds between callbacks */ + PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed; + double cpuLoad, avgCpuLoad = 0.; + int throttled = 0; + + assert( th ); + + pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */ + + /* Boost priority of callback thread */ + PA_ENSURE( result = BoostPriority( th ) ); + if( !result ) + { + pthread_exit( NULL ); /* Boost failed, might as well exit */ + } + + cpuTimeThen = th->callbackCpuTime; + { + int policy; + struct sched_param spm = { 0 }; + pthread_getschedparam( pthread_self(), &policy, &spm ); + PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority )); + } + + while( 1 ) + { + double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff; + + /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */ + pthread_testcancel(); + Pa_Sleep( intervalMsec ); + pthread_testcancel(); + + if( PaUtil_GetTime() - th->callbackTime > maxSeconds ) + { + PA_DEBUG(( "Watchdog: Terminating callback thread\n" )); + /* Tell thread to terminate */ + err = pthread_kill( th->callbackThread, SIGKILL ); + pthread_exit( NULL ); + } + + PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) )); + + /* Check if we should throttle, or unthrottle :P */ + cpuTimeNow = th->callbackCpuTime; + cpuTimeElapsed = cpuTimeNow - cpuTimeThen; + cpuTimeThen = cpuTimeNow; + + timeNow = PaUtil_GetTime(); + timeElapsed = timeNow - timeThen; + timeThen = timeNow; + cpuLoad = cpuTimeElapsed / timeElapsed; + avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1; + /* + if( throttled ) + PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed )); + */ + if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 ) + { + static int policy; + static struct sched_param spm = { 0 }; + static const struct sched_param defaultSpm = { 0 }; + PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority )); + + pthread_getschedparam( th->callbackThread, &policy, &spm ); + if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) ) + { + throttled = 1; + } + else + PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) )); + + /* Give other processes a go, before raising priority again */ + PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime )); + Pa_Sleep( th->throttledSleepTime ); + + /* Reset callback priority */ + if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 ) + { + PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) )); + } + + if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 ) + intervalMsec = 50; + else + intervalMsec = 100; + + /* + lowpassCoeff = .97; + lowpassCoeff1 = .99999 - lowpassCoeff; + */ + } + else if( throttled && avgCpuLoad < .8 ) + { + intervalMsec = 500; + throttled = 0; + + /* + lowpassCoeff = .9; + lowpassCoeff1 = .99999 - lowpassCoeff; + */ + } + } + + pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */ + +error: + /* Shouldn't get here in the normal case */ + + /* Pass on error code */ + pres = malloc( sizeof (PaError) ); + *pres = result; + + pthread_exit( pres ); +} + +static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s ) +{ + PaError result = paNoError; + pthread_attr_t attr; + int started = 0; + +#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1) + if( th->rtSched ) + { + if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 ) + { + int savedErrno = errno; /* In case errno gets overwritten */ + assert( savedErrno != EINVAL ); /* Most likely a programmer error */ + PA_UNLESS( (savedErrno == EPERM), paInternalError ); + PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ )); + } + else + PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ )); + } +#endif + + PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); + /* Priority relative to other processes */ + PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); + + PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError ); + started = 1; + + if( th->rtSched ) + { + if( th->useWatchdog ) + { + int err; + struct sched_param wdSpm = { 0 }; + /* Launch watchdog, watchdog sets callback thread priority */ + int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) ); + wdSpm.sched_priority = prio; + + PA_UNLESS( !pthread_attr_init( &attr ), paInternalError ); + PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError ); + PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError ); + PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError ); + PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError ); + if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) ) + { + PA_UNLESS( err == EPERM, paInternalError ); + /* Permission error, go on without realtime privileges */ + PA_DEBUG(( "Failed bumping priority\n" )); + } + else + { + int policy; + th->watchdogRunning = 1; + ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 ); + /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */ + if( wdSpm.sched_priority != prio ) + { + PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority )); + PA_ENSURE( paInternalError ); + } + } + } + else + PA_ENSURE( BoostPriority( th ) ); + } + +end: + return result; +error: + if( started ) + KillCallbackThread( th, 0, NULL, NULL ); + + goto end; +} + +static void CallbackUpdate( PaAlsaThreading *th ) +{ + th->callbackTime = PaUtil_GetTime(); + th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer ); +} + +/* prototypes for functions declared in this file */ + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *callback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi ); +static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ); +static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ); + +/* Callback prototypes */ +static void *CallbackThreadFunc( void *userData ); + +/* Blocking prototypes */ +static signed long GetStreamReadAvailable( PaStream* s ); +static signed long GetStreamWriteAvailable( PaStream* s ); +static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); + + +static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device ) +{ + return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device]; +} + +PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + PaAlsaHostApiRepresentation *alsaHostApi = NULL; + + PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory( + sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory ); + PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); + alsaHostApi->hostApiIndex = hostApiIndex; + + *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paALSA; + (*hostApi)->info.name = "ALSA"; + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PA_ENSURE( BuildDeviceList( alsaHostApi ) ); + + PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface, + CloseStream, StartStream, + StopStream, AbortStream, + IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyRead, PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, + PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface, + CloseStream, StartStream, + StopStream, AbortStream, + IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, + GetStreamReadAvailable, + GetStreamWriteAvailable ); + + return result; + +error: + if( alsaHostApi ) + { + if( alsaHostApi->allocations ) + { + PaUtil_FreeAllAllocations( alsaHostApi->allocations ); + PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); + } + + PaUtil_FreeMemory( alsaHostApi ); + } + + return result; +} + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; + + assert( hostApi ); + + if( alsaHostApi->allocations ) + { + PaUtil_FreeAllAllocations( alsaHostApi->allocations ); + PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); + } + + PaUtil_FreeMemory( alsaHostApi ); + snd_config_update_free_global(); +} + +/*! Determine max channels and default latencies. + * + * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for + * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero, + * and a suitable result returned. The device is closed before returning. + */ +static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency, + double *defaultHighLatency, double *defaultSampleRate, int isPlug ) +{ + PaError result = paNoError; + snd_pcm_hw_params_t *hwParams; + snd_pcm_uframes_t lowLatency = 512, highLatency = 2048; + unsigned int minChans, maxChans; + double defaultSr = *defaultSampleRate; + + assert( pcm ); + + ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError ); + + snd_pcm_hw_params_alloca( &hwParams ); + snd_pcm_hw_params_any( pcm, hwParams ); + + if( defaultSr >= 0 ) + { + /* Could be that the device opened in one mode supports samplerates that the other mode wont have, + * so try again .. */ + if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 ) + { + defaultSr = -1.; + PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ )); + } + } + + if( defaultSr < 0. ) /* Default sample rate not set */ + { + unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */ + ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError ); + ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError ); + } + + ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError ); + assert( maxChans <= INT_MAX ); + assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called, + resulting in zeroed values */ + + /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */ + if( isPlug && maxChans > 128 ) + { + maxChans = 128; + PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans )); + } + + /* TWEAKME: + * + * Giving values for default min and max latency is not + * straightforward. Here are our objectives: + * + * * for low latency, we want to give the lowest value + * that will work reliably. This varies based on the + * sound card, kernel, CPU, etc. I think it is better + * to give sub-optimal latency than to give a number + * too low and cause dropouts. My conservative + * estimate at this point is to base it on 4096-sample + * latency at 44.1 kHz, which gives a latency of 23ms. + * * for high latency we want to give a large enough + * value that dropouts are basically impossible. This + * doesn't really require as much tweaking, since + * providing too large a number will just cause us to + * select the nearest setting that will work at stream + * config time. + */ + ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); + + /* Have to reset hwParams, to set new buffer size */ + ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError ); + + *minChannels = (int)minChans; + *maxChannels = (int)maxChans; + *defaultSampleRate = defaultSr; + *defaultLowLatency = (double) lowLatency / *defaultSampleRate; + *defaultHighLatency = (double) highLatency / *defaultSampleRate; + +end: + snd_pcm_close( pcm ); + return result; + +error: + goto end; +} + +/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate + * wether input/output is available) */ +static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo ) +{ + deviceInfo->structVersion = -1; + deviceInfo->name = NULL; + deviceInfo->hostApi = -1; + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + deviceInfo->defaultLowInputLatency = -1.; + deviceInfo->defaultLowOutputLatency = -1.; + deviceInfo->defaultHighInputLatency = -1.; + deviceInfo->defaultHighOutputLatency = -1.; + deviceInfo->defaultSampleRate = -1.; +} + +/* Helper struct */ +typedef struct +{ + char *alsaName; + char *name; + int isPlug; + int hasPlayback; + int hasCapture; +} DeviceNames; + +static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi, + char **dst, + const char *src) +{ + PaError result = paNoError; + int len = strlen( src ) + 1; + + /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */ + + PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), + paInsufficientMemory ); + strncpy( *dst, src, len ); + +error: + return result; +} + +/* Disregard standard plugins + * XXX: Might want to make the "default" plugin available, if we can make it work + */ +static int IgnorePlugin( const char *pluginId ) +{ +#define numIgnored 10 + static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee", + "file", "null", "shm"}; + int i; + + for( i = 0; i < numIgnored; ++i ) + { + if( !strcmp( pluginId, ignoredPlugins[i] ) ) + { + return 1; + } + } + + return 0; +} + +/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */ +static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) +{ + PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep; + PaAlsaDeviceInfo *deviceInfoArray; + int cardIdx = -1, devIdx = 0; + snd_ctl_card_info_t *cardInfo; + PaError result = paNoError; + size_t numDeviceNames = 0, maxDeviceNames = 1, i; + DeviceNames *deviceNames = NULL; + snd_config_t *topNode = NULL; + snd_pcm_info_t *pcmInfo; + int res; + int blocking = SND_PCM_NONBLOCK; + char alsaCardName[50]; + if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) ) + blocking = 0; + + /* These two will be set to the first working input and output device, respectively */ + commonApi->info.defaultInputDevice = paNoDevice; + commonApi->info.defaultOutputDevice = paNoDevice; + + /* count the devices by enumerating all the card numbers */ + + /* snd_card_next() modifies the integer passed to it to be: + * the index of the first card if the parameter is -1 + * the index of the next card if the parameter is the index of a card + * -1 if there are no more cards + * + * The function itself returns 0 if it succeeded. */ + cardIdx = -1; + snd_ctl_card_info_alloca( &cardInfo ); + snd_pcm_info_alloca( &pcmInfo ); + while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) + { + char *cardName; + int devIdx = -1; + snd_ctl_t *ctl; + char buf[50]; + + snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); + + /* Acquire name of card */ + if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) + continue; /* Unable to open card :( */ + snd_ctl_card_info( ctl, cardInfo ); + + PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) ); + + while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) + { + char *alsaDeviceName, *deviceName; + size_t len; + int hasPlayback = 0, hasCapture = 0; + snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx ); + + /* Obtain info about this particular device */ + snd_pcm_info_set_device( pcmInfo, devIdx ); + snd_pcm_info_set_subdevice( pcmInfo, 0 ); + snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); + if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + hasCapture = 1; + + snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); + if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + hasPlayback = 1; + + if( !hasPlayback && !hasCapture ) + { + continue; /* Error */ + } + + /* The length of the string written by snprintf plus terminating 0 */ + len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1; + PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), + paInsufficientMemory ); + snprintf( deviceName, len, "%s: %s (%s)", cardName, + snd_pcm_info_get_name( pcmInfo ), buf ); + + ++numDeviceNames; + if( !deviceNames || numDeviceNames > maxDeviceNames ) + { + maxDeviceNames *= 2; + PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), + paInsufficientMemory ); + } + + PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); + + deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName; + deviceNames[ numDeviceNames - 1 ].name = deviceName; + deviceNames[ numDeviceNames - 1 ].isPlug = 0; + deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback; + deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture; + } + snd_ctl_close( ctl ); + } + + /* Iterate over plugin devices */ + snd_config_update(); + if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 ) + { + snd_config_iterator_t i, next; + + snd_config_for_each( i, next, topNode ) + { + const char *tpStr = NULL, *idStr = NULL; + char *alsaDeviceName, *deviceName; + snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL; + if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND ) + continue; + + ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError ); + ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError ); + + ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError ); + if( IgnorePlugin( idStr ) ) + { + PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr )); + continue; + } + + PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr )); + + PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, + strlen(idStr) + 6 ), paInsufficientMemory ); + strcpy( alsaDeviceName, idStr ); + PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, + strlen(idStr) + 1 ), paInsufficientMemory ); + strcpy( deviceName, idStr ); + + ++numDeviceNames; + if( !deviceNames || numDeviceNames > maxDeviceNames ) + { + maxDeviceNames *= 2; + PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), + paInsufficientMemory ); + } + + deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName; + deviceNames[numDeviceNames - 1].name = deviceName; + deviceNames[numDeviceNames - 1].isPlug = 1; + deviceNames[numDeviceNames - 1].hasPlayback = 1; + deviceNames[numDeviceNames - 1].hasCapture = 1; + } + } + else + PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) )); + + /* allocate deviceInfo memory based on the number of devices */ + PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); + + /* allocate all device info structs in a contiguous block */ + PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( + alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); + + /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name), + * it's ignored. + */ + /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */ + for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) + { + snd_pcm_t *pcm; + PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx]; + PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo; + + /* Zero fields */ + InitializeDeviceInfo( commonDeviceInfo ); + + /* to determine device capabilities, we must open the device and query the + * hardware parameter configuration space */ + + /* Query capture */ + if( deviceNames[i].hasCapture && + snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 ) + { + if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels, + &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency, + &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError ) + continue; /* Error */ + } + + /* Query playback */ + if( deviceNames[i].hasPlayback && + snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 ) + { + if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels, + &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency, + &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError ) + continue; /* Error */ + } + + commonDeviceInfo->structVersion = 2; + commonDeviceInfo->hostApi = alsaApi->hostApiIndex; + commonDeviceInfo->name = deviceNames[i].name; + deviceInfo->alsaName = deviceNames[i].alsaName; + deviceInfo->isPlug = deviceNames[i].isPlug; + + /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object. + * Should now be safe to add device info, unless the device supports neither capture nor playback + */ + if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 ) + { + if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 ) + commonApi->info.defaultInputDevice = devIdx; + if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 ) + commonApi->info.defaultOutputDevice = devIdx; + + commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo; + } + } + free( deviceNames ); + + commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */ + +end: + return result; + +error: + goto end; /* No particular action */ +} + +/* Check against known device capabilities */ +static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode ) +{ + PaError result = paNoError; + int maxChans; + const PaAlsaDeviceInfo *deviceInfo = NULL; + assert( parameters ); + + if( parameters->device != paUseHostApiSpecificDeviceSpecification ) + { + assert( parameters->device < hostApi->info.deviceCount ); + PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination ); + deviceInfo = GetDeviceInfo( hostApi, parameters->device ); + } + else + { + const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo; + + PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice ); + PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1, + paIncompatibleHostApiSpecificStreamInfo ); + + return paNoError; /* Skip further checking */ + } + + assert( deviceInfo ); + assert( parameters->hostApiSpecificStreamInfo == NULL ); + maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels : + deviceInfo->commonDeviceInfo.maxOutputChannels); + PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount ); + +error: + return result; +} + +/* Given an open stream, what sample formats are available? */ + +static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm ) +{ + PaSampleFormat available = 0; + snd_pcm_hw_params_t *hwParams; + snd_pcm_hw_params_alloca( &hwParams ); + + snd_pcm_hw_params_any( pcm, hwParams ); + + if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0) + available |= paFloat32; + + if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0) + available |= paInt32; + + if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0) + available |= paInt24; + + if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) + available |= paInt16; + + if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0) + available |= paUInt8; + + if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) + available |= paInt8; + + return available; +} + +static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) +{ + switch( paFormat ) + { + case paFloat32: + return SND_PCM_FORMAT_FLOAT; + + case paInt16: + return SND_PCM_FORMAT_S16; + + case paInt24: + return SND_PCM_FORMAT_S24; + + case paInt32: + return SND_PCM_FORMAT_S32; + + case paInt8: + return SND_PCM_FORMAT_S8; + + case paUInt8: + return SND_PCM_FORMAT_U8; + + default: + return SND_PCM_FORMAT_UNKNOWN; + } +} + +/** Open an ALSA pcm handle. + * + * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a + * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin + * device. + */ +static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection + streamDir, snd_pcm_t **pcm ) +{ + PaError result = paNoError; + int ret; + const char *deviceName = alloca( 50 ); + const PaAlsaDeviceInfo *deviceInfo = NULL; + PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo; + + if( !streamInfo ) + { + int usePlug = 0; + deviceInfo = GetDeviceInfo( hostApi, params->device ); + + /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */ + if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) ) + usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) ); + if( usePlug ) + snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName ); + else + deviceName = deviceInfo->alsaName; + } + else + deviceName = streamInfo->deviceString; + + if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK )) < 0 ) + { + *pcm = NULL; /* Not to be closed */ + ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination ); + } + ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError ); + +end: + return result; + +error: + goto end; +} + +static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters, + double sampleRate, StreamDirection streamDir ) +{ + PaError result = paNoError; + snd_pcm_t *pcm = NULL; + PaSampleFormat availableFormats; + /* We are able to adapt to a number of channels less than what the device supports */ + unsigned int numHostChannels; + PaSampleFormat hostFormat; + snd_pcm_hw_params_t *hwParams; + snd_pcm_hw_params_alloca( &hwParams ); + + if( !parameters->hostApiSpecificStreamInfo ) + { + const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device ); + numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ? + devInfo->minInputChannels : devInfo->minOutputChannels ); + } + else + numHostChannels = parameters->channelCount; + + PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) ); + + snd_pcm_hw_params_any( pcm, hwParams ); + + if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 ) + { + result = paInvalidSampleRate; + goto error; + } + + if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 ) + { + result = paInvalidChannelCount; + goto error; + } + + /* See if we can find a best possible match */ + availableFormats = GetAvailableFormats( pcm ); + PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) ); + ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); + + ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError ); + +end: + if( pcm ) + snd_pcm_close( pcm ); + return result; + +error: + goto end; +} + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + int inputChannelCount = 0, outputChannelCount = 0; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaError result = paFormatIsSupported; + + if( inputParameters ) + { + PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); + + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + } + + if( outputParameters ) + { + PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); + + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + } + + if( inputChannelCount ) + { + if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In )) + != paNoError ) + goto error; + } + if ( outputChannelCount ) + { + if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out )) + != paNoError ) + goto error; + } + + return paFormatIsSupported; + +error: + return result; +} + +static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi, + const PaStreamParameters *params, StreamDirection streamDir, int callbackMode ) +{ + PaError result = paNoError; + PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat; + assert( params->channelCount > 0 ); + + /* Make sure things have an initial value */ + memset( self, 0, sizeof (PaAlsaStreamComponent) ); + + if( NULL == params->hostApiSpecificStreamInfo ) + { + const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device ); + self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels + : devInfo->minOutputChannels ); + } + else + { + /* We're blissfully unaware of the minimum channelCount */ + self->numHostChannels = params->channelCount; + } + + PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) ); + self->nfds = snd_pcm_poll_descriptors_count( self->pcm ); + hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ); + + self->hostSampleFormat = hostSampleFormat; + self->nativeFormat = Pa2AlsaFormat( hostSampleFormat ); + self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved); + self->numUserChannels = params->channelCount; + self->streamDir = streamDir; + + if( !callbackMode && !self->userInterleaved ) + { + /* Pre-allocate non-interleaved user provided buffers */ + PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ), + paInsufficientMemory ); + } + +error: + return result; +} + +static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self ) +{ + snd_pcm_close( self->pcm ); + if( self->userBuffers ) + PaUtil_FreeMemory( self->userBuffers ); +} + +/** Configure the associated ALSA pcm. + * + */ +static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long + framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency ) +{ + /* + int numPeriods; + + if( getenv("PA_NUMPERIODS") != NULL ) + numPeriods = atoi( getenv("PA_NUMPERIODS") ); + else + numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1; + + PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer )); + if( numPeriods <= 1 ) + numPeriods = 2; + */ + + /* Configuration consists of setting all of ALSA's parameters. + * These parameters come in two flavors: hardware parameters + * and software paramters. Hardware parameters will affect + * the way the device is initialized, software parameters + * affect the way ALSA interacts with me, the user-level client. + */ + + snd_pcm_hw_params_t *hwParams; + snd_pcm_sw_params_t *swParams; + PaError result = paNoError; + snd_pcm_access_t accessMode, alternateAccessMode; + unsigned int numPeriods, minPeriods = 2; + int dir = 0; + snd_pcm_t *pcm = self->pcm; + PaTime latency = params->suggestedLatency; + double sr = *sampleRate; + *returnedLatency = -1.; + + snd_pcm_hw_params_alloca( &hwParams ); + snd_pcm_sw_params_alloca( &swParams ); + + self->framesPerBuffer = framesPerHostBuffer; + + /* ... fill up the configuration space with all possibile + * combinations of parameters this device will accept */ + ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); + + ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError ); + + if( self->userInterleaved ) + { + accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; + alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + } + else + { + accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED; + } + + /* If requested access mode fails, try alternate mode */ + if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 ) + { + ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError ); + /* Flip mode */ + self->hostInterleaved = !self->userInterleaved; + } + + ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError ); + + ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate ); + ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError ); + /* reject if there's no sample rate within 1% of the one requested */ + if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 ) + { + PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr )); + PA_ENSURE( paInvalidSampleRate ); + } + + ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount ); + + /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */ + dir = 0; + ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError ); + dir = 0; + ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError ); + + /* Find an acceptable number of periods */ + numPeriods = (latency * sr) / self->framesPerBuffer + 1; + dir = 0; + ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError ); + /* Minimum of periods should already be 2 */ + PA_UNLESS( numPeriods >= 2, paInternalError ); + + /* Set the parameters! */ + ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); + + /* Latency in seconds, one period is not counted as latency */ + latency = (numPeriods - 1) * self->framesPerBuffer / sr; + + /* Now software parameters... */ + ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError ); + + ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError ); + + /* Silence buffer in the case of underrun */ + if( !primeBuffers ) /* XXX: Make sense? */ + { + snd_pcm_uframes_t boundary; + ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError ); + } + + ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError ); + + /* Set the parameters! */ + ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError ); + + *sampleRate = sr; + *returnedLatency = latency; + +end: + return result; + +error: + goto end; /* No particular action */ +} + +static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams, + const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback, + PaStreamFlags streamFlags, void *userData ) +{ + PaError result = paNoError; + assert( self ); + + memset( self, 0, sizeof (PaAlsaStream) ); + + if( NULL != callback ) + { + PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, + &alsaApi->callbackStreamInterface, + callback, userData ); + self->callbackMode = 1; + } + else + { + PaUtil_InitializeStreamRepresentation( &self->streamRepresentation, + &alsaApi->blockingStreamInterface, + NULL, userData ); + } + + self->framesPerUserBuffer = framesPerUserBuffer; + self->neverDropInput = streamFlags & paNeverDropInput; + /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */ + /* + if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback ) + self->primeBuffers = 1; + */ + memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) ); + memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) ); + if( inParams ) + PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) ); + if( outParams ) + PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) ); + + assert( self->capture.nfds || self->playback.nfds ); + + PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds + + self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory ); + + PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate ); + InitializeThreading( &self->threading, &self->cpuLoadMeasurer ); + ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 ); + ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 ); + ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 ); + +error: + return result; +} + +/** Free resources associated with stream, and eventually stream itself. + * + * Frees allocated memory, and terminates individual StreamComponents. + */ +static void PaAlsaStream_Terminate( PaAlsaStream *self ) +{ + assert( self ); + + if( self->capture.pcm ) + { + PaAlsaStreamComponent_Terminate( &self->capture ); + } + if( self->playback.pcm ) + { + PaAlsaStreamComponent_Terminate( &self->playback ); + } + + PaUtil_FreeMemory( self->pfds ); + ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 ); + ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 ); + ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 ); + + PaUtil_FreeMemory( self ); +} + +/** Calculate polling timeout + * + * @param frames Time to wait + * @return Polling timeout in milliseconds + */ +static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames ) +{ + assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 ); + /* Period in msecs, rounded up */ + return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate ); +} + +/** Set up ALSA stream parameters. + * + */ +static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters + *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency, + unsigned long *maxHostBufferSize ) +{ + PaError result = paNoError; + double realSr = sampleRate; + + if( self->capture.pcm ) + PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers, + self->callbackMode, &realSr, inputLatency ) ); + if( self->playback.pcm ) + PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers, + self->callbackMode, &realSr, outputLatency ) ); + + /* Should be exact now */ + self->streamRepresentation.streamInfo.sampleRate = realSr; + + /* this will cause the two streams to automatically start/stop/prepare in sync. + * We only need to execute these operations on one of the pair. + * A: We don't want to do this on a blocking stream. + */ + if( self->callbackMode && self->capture.pcm && self->playback.pcm ) + { + int err = snd_pcm_link( self->capture.pcm, self->playback.pcm ); + if( err >= 0 ) + self->pcmsSynced = 1; + else + PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) )); + } + + /* Frames per host buffer for the stream is set as a compromise between the two directions */ + framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX, + self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX ); + self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */ + + *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0, + self->playback.pcm ? self->playback.bufferSize : 0 ); + + /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */ + self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000); + + if( self->callbackMode ) + { + /* If the user expects a certain number of frames per callback we will either have to rely on block adaption + * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number + * of host buffer frames with what the user specified */ + if( self->framesPerUserBuffer != paFramesPerBufferUnspecified ) + { + /* self->alignFrames = 1; */ + + /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely + * on block adaption */ + /* + if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm && + self->capture.framesPerBuffer != self->playback.framesPerBuffer) ) + self->useBlockAdaption = 1; + else + self->alignFrames = 1; + */ + } + } + +error: + return result; +} + +/* We need to determine how many frames per host buffer to use. Our + * goals are to provide the best possible performance, but also to + * most closely honor the requested latency settings. Therefore this + * decision is based on: + * + * - the period sizes that playback and/or capture support. The + * host buffer size has to be one of these. + * - the number of periods that playback and/or capture support. + * + * We want to make period_size*(num_periods-1) to be as close as possible + * to latency*rate for both playback and capture. + * + * This is one of those blocks of code that will just take a lot of + * refinement to be any good. + * + * In the full-duplex case it is possible that the routine was unable + * to find a number of frames per buffer acceptable to both devices + * TODO: Implement an algorithm to find the value closest to acceptance + * by both devices, to minimize difference between period sizes? + */ +static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi ) +{ + PaError result = paNoError; + unsigned long framesPerBuffer = 0; + int numHostInputChannels = 0, numHostOutputChannels = 0; + + /* XXX: Clean this up */ + if( stream->capture.pcm ) + { + const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device ); + numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels ); + } + if( stream->playback.pcm ) + { + const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device ); + numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels ); + } + + if( stream->capture.pcm && stream->playback.pcm ) + { + snd_pcm_uframes_t desiredLatency, e; + snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture, + optimalPeriodSize, periodSize; + int dir = 0; + unsigned int minPeriods = 2; + + snd_pcm_t *pcm; + snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture; + + snd_pcm_hw_params_alloca( &hwParamsPlayback ); + snd_pcm_hw_params_alloca( &hwParamsCapture ); + + /* Come up with a common desired latency */ + pcm = stream->playback.pcm; + snd_pcm_hw_params_any( pcm, hwParamsPlayback ); + ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate ); + ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ), + paBadIODeviceCombination ); + + ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError ); + + pcm = stream->capture.pcm; + ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError ); + ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination ); + ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ), + paBadIODeviceCombination ); + + ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError ); + + minPeriodSize = PA_MAX( minPlayback, minCapture ); + maxPeriodSize = PA_MIN( maxPlayback, maxCapture ); + + desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency ) + * sampleRate); + /* Clamp desiredLatency */ + { + snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX; + ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError ); + maxBufferSize = PA_MIN( maxBufferSize, tmp ); + + desiredLatency = PA_MIN( desiredLatency, maxBufferSize ); + } + + /* Find the closest power of 2 */ + e = ilogb( minPeriodSize ); + if( minPeriodSize & (minPeriodSize - 1) ) + e += 1; + periodSize = (snd_pcm_uframes_t) pow( 2, e ); + + while( periodSize <= maxPeriodSize ) + { + if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 && + snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 ) + break; /* Ok! */ + + periodSize *= 2; + } + + /* 4 periods considered optimal */ + optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); + optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); + + /* Find the closest power of 2 */ + e = ilogb( optimalPeriodSize ); + if( optimalPeriodSize & (optimalPeriodSize - 1) ) + e += 1; + optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e ); + + while( optimalPeriodSize >= periodSize ) + { + pcm = stream->playback.pcm; + if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 ) + continue; + + pcm = stream->capture.pcm; + if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 ) + break; + + optimalPeriodSize /= 2; + } + + if( optimalPeriodSize > periodSize ) + periodSize = optimalPeriodSize; + + if( periodSize <= maxPeriodSize ) + { + /* Looks good */ + framesPerBuffer = periodSize; + } + else + { + /* Unable to find a common period size, oh well */ + optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize ); + optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize ); + + /* ConfigureStream should find individual period sizes acceptable for each device */ + framesPerBuffer = optimalPeriodSize; + /* PA_ENSURE( paBadIODeviceCombination ); */ + } + } + else /* half-duplex is a slightly simpler case */ + { + unsigned long bufferSize, channels; + snd_pcm_t *pcm; + snd_pcm_hw_params_t *hwParams; + + snd_pcm_hw_params_alloca( &hwParams ); + + if( stream->capture.pcm ) + { + pcm = stream->capture.pcm; + bufferSize = inputParameters->suggestedLatency * sampleRate; + channels = numHostInputChannels; + } + else + { + pcm = stream->playback.pcm; + bufferSize = outputParameters->suggestedLatency * sampleRate; + channels = numHostOutputChannels; + } + + ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate ); + ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination ); + + ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError ); + + /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period), + finding a combination of period/buffer size which best fits these constraints */ + framesPerBuffer = bufferSize / 4; + bufferSize += framesPerBuffer; /* One period doesn't count as latency */ + ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError ); + ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError ); + } + + PA_UNLESS( framesPerBuffer != 0, paInternalError ); + *determinedFrames = framesPerBuffer; + +error: + return result; +} + +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *callback, + void *userData ) +{ + PaError result = paNoError; + PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; + PaAlsaStream *stream = NULL; + PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0; + PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0; + int numInputChannels = 0, numOutputChannels = 0; + PaTime inputLatency, outputLatency; + unsigned long framesPerHostBuffer; + PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize; + unsigned long maxHostBufferSize; /* Upper bound of host buffer size */ + + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; + + if( inputParameters ) + { + PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) ); + + numInputChannels = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + } + if( outputParameters ) + { + PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) ); + + numOutputChannels = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + } + + /* XXX: Why do we support this anyway? */ + if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL ) + { + PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ )); + framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") ); + } + framesPerHostBuffer = framesPerBuffer; + + PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory ); + PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate, + framesPerBuffer, callback, streamFlags, userData ) ); + + /* If the number of frames per buffer is unspecified, we have to come up with + * one. This is both a blessing and a curse: a blessing because we can optimize + * the number to best meet the requirements, but a curse because that's really + * hard to do well. For this reason we also support an interface where the user + * specifies these by setting environment variables. */ + if( framesPerBuffer == paFramesPerBufferUnspecified ) + { + PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer, + hostApi ) ); + } + + PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer, + &inputLatency, &outputLatency, &maxHostBufferSize ) ); + hostInputSampleFormat = stream->capture.hostSampleFormat; + hostOutputSampleFormat = stream->playback.hostSampleFormat; + + if( framesPerHostBuffer != framesPerBuffer ) + { + PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ )); + } + + PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + numInputChannels, inputSampleFormat, hostInputSampleFormat, + numOutputChannels, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize, + hostBufferSizeMode, callback, userData ) ); + + /* Ok, buffer processor is initialized, now we can deduce it's latency */ + if( numInputChannels > 0 ) + stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency( + &stream->bufferProcessor ); + if( numOutputChannels > 0 ) + stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency( + &stream->bufferProcessor ); + + *s = (PaStream*)stream; + + return result; + +error: + if( stream ) + PaAlsaStream_Terminate( stream ); + + return result; +} + +static PaError CloseStream( PaStream* s ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + + PaAlsaStream_Terminate( stream ); + + return result; +} + +static void SilenceBuffer( PaAlsaStream *stream ) +{ + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset; + + snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames ); + snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat ); + snd_pcm_mmap_commit( stream->playback.pcm, offset, frames ); +} + +/** Start/prepare pcm(s) for streaming. + * + * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply + * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and + * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will + * be started automatically as the user writes to output. + * + * The capture pcm, however, will simply be prepared and started. + * + * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode) + */ +static PaError AlsaStart( PaAlsaStream *stream, int priming ) +{ + PaError result = paNoError; + + if( stream->playback.pcm ) + { + if( stream->callbackMode ) + { + if( !priming ) + { + /* Buffer isn't primed, so prepare and silence */ + ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); + SilenceBuffer( stream ); + } + ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); + } + else + ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); + } + if( stream->capture.pcm && !stream->pcmsSynced ) + { + ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); + /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */ + ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); + } + +end: + return result; +error: + goto end; +} + +/** Utility function for determining if pcms are in running state. + * + */ +static int IsRunning( PaAlsaStream *stream ) +{ + int result = 0; + + ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */ + if( stream->capture.pcm ) + { + snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm ); + + if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN + || capture_state == SND_PCM_STATE_DRAINING ) + { + result = 1; + goto end; + } + } + + if( stream->playback.pcm ) + { + snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm ); + + if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN + || playback_state == SND_PCM_STATE_DRAINING ) + { + result = 1; + goto end; + } + } + +end: + ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 ); + + return result; +} + +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + int streamStarted = 0; /* So we can know wether we need to take the stream down */ + + /* Ready the processor */ + PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); + + /* Set now, so we can test for activity further down */ + stream->isActive = 1; + + if( stream->callbackMode ) + { + int res = 0; + PaTime pt = PaUtil_GetTime(); + struct timespec ts; + + PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) ); + streamStarted = 1; + + /* Wait for stream to be started */ + ts.tv_sec = (time_t) floor( pt + 1 ); + ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000); + + /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking + * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely + * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */ + ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 ); + + /* Due to possible spurious wakeups, we enclose in a loop */ + while( !IsRunning( stream ) && IsStreamActive( s ) && !res ) + { + res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts ); + } + ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 ); + + PA_UNLESS( !res || res == ETIMEDOUT, paInternalError ); + PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt )); + + if( res == ETIMEDOUT ) + { + PA_ENSURE( paTimedOut ); + } + } + else + { + PA_ENSURE( AlsaStart( stream, 0 ) ); + streamStarted = 1; + } + +end: + return result; +error: + if( streamStarted ) + AbortStream( stream ); + stream->isActive = 0; + + goto end; +} + +static PaError AlsaStop( PaAlsaStream *stream, int abort ) +{ + PaError result = paNoError; + + if( abort ) + { + if( stream->playback.pcm ) + ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError ); + if( stream->capture.pcm && !stream->pcmsSynced ) + ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError ); + + PA_DEBUG(( "Dropped frames\n" )); + } + else + { + if( stream->playback.pcm ) + ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError ); + if( stream->capture.pcm && !stream->pcmsSynced ) + ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError ); + } + +end: + return result; +error: + goto end; +} + +/** Stop or abort stream. + * + * If a stream is in callback mode we will have to inspect wether the background thread has + * finished, or we will have to take it out. In either case we join the thread before + * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish + * buffers (drain) + * + * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function + */ +static PaError RealStop( PaAlsaStream *stream, int abort ) +{ + PaError result = paNoError; + + /* First deal with the callback thread, cancelling and/or joining + * it if necessary + */ + if( stream->callbackMode ) + { + PaError threadRes, watchdogRes; + stream->callbackAbort = abort; + + if( !abort ) + { + PA_DEBUG(( "Stopping callback\n" )); + stream->callbackStop = 1; + } + PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) ); + if( threadRes != paNoError ) + PA_DEBUG(( "Callback thread returned: %d\n", threadRes )); + if( watchdogRes != paNoError ) + PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes )); + + stream->callbackStop = 0; /* The deed is done */ + stream->callback_finished = 0; + } + else + { + PA_ENSURE( AlsaStop( stream, abort ) ); + } + + stream->isActive = 0; + +end: + return result; + +error: + goto end; +} + +static PaError StopStream( PaStream *s ) +{ + return RealStop( (PaAlsaStream *) s, 0 ); +} + +static PaError AbortStream( PaStream *s ) +{ + return RealStop( (PaAlsaStream * ) s, 1 ); +} + +/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback + * returning !paContinue is not considered) + * + */ +static PaError IsStreamStopped( PaStream *s ) +{ + PaAlsaStream *stream = (PaAlsaStream *)s; + + /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */ + return !IsStreamActive( s ) && !stream->callback_finished; +} + +static PaError IsStreamActive( PaStream *s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + return stream->isActive; +} + +static PaTime GetStreamTime( PaStream *s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + snd_timestamp_t timestamp; + snd_pcm_status_t *status; + snd_pcm_status_alloca( &status ); + + /* TODO: what if we have both? does it really matter? */ + + /* TODO: if running in callback mode, this will mean + * libasound routines are being called from multiple threads. + * need to verify that libasound is thread-safe. */ + + if( stream->capture.pcm ) + { + snd_pcm_status( stream->capture.pcm, status ); + } + else if( stream->playback.pcm ) + { + snd_pcm_status( stream->playback.pcm, status ); + } + + snd_pcm_status_get_tstamp( status, ×tamp ); + return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0; +} + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + +static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ) +{ + unsigned long approx = (unsigned long) sampleRate; + int dir = 0; + double fraction = sampleRate - approx; + + assert( pcm && hwParams ); + + if( fraction > 0.0 ) + { + if( fraction > 0.5 ) + { + ++approx; + dir = -1; + } + else + dir = 1; + } + + return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir ); +} + +/* Return exact sample rate in param sampleRate */ +static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ) +{ + unsigned int num, den; + int err; + + assert( hwParams ); + + err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den ); + *sampleRate = (double) num / den; + + return err; +} + +/* Utility functions for blocking/callback interfaces */ + +/* Atomic restart of stream (we don't want the intermediate state visible) */ +static PaError AlsaRestart( PaAlsaStream *stream ) +{ + PaError result = paNoError; + + ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); + PA_ENSURE( AlsaStop( stream, 0 ) ); + PA_ENSURE( AlsaStart( stream, 0 ) ); + + PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ )); + +error: + ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 ); + return result; +} + +/** Recover from xrun state. + * + */ +static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self ) +{ + PaError result = paNoError; + snd_pcm_status_t *st; + PaTime now = PaUtil_GetTime(); + snd_timestamp_t t; + + snd_pcm_status_alloca( &st ); + + if( self->playback.pcm ) + { + snd_pcm_status( self->playback.pcm, st ); + if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) + { + snd_pcm_status_get_trigger_tstamp( st, &t ); + self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); + } + } + if( self->capture.pcm ) + { + snd_pcm_status( self->capture.pcm, st ); + if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) + { + snd_pcm_status_get_trigger_tstamp( st, &t ); + self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); + } + } + + PA_ENSURE( AlsaRestart( self ) ); + +end: + return result; +error: + goto end; +} + +/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout. + * + */ +static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll ) +{ + PaError result = paNoError; + snd_pcm_sframes_t delay, margin; + int err; + const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL; + + *continuePoll = 1; + + if( StreamDirection_In == streamDir ) + { + component = &stream->capture; + otherComponent = &stream->playback; + } + else + { + component = &stream->playback; + otherComponent = &stream->capture; + } + + /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */ + if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 ) + { + if( err == -EPIPE ) + { + /* Xrun */ + *continuePoll = 0; + goto error; + } + + ENSURE_( err, paUnanticipatedHostError ); + } + + if( StreamDirection_Out == streamDir ) + { + /* Number of eligible frames before capture overrun */ + delay = otherComponent->bufferSize - delay; + } + margin = delay - otherComponent->framesPerBuffer / 2; + + if( margin < 0 ) + { + PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" )); + *continuePoll = 0; + } + else if( margin < otherComponent->framesPerBuffer ) + { + *pollTimeout = CalculatePollTimeout( stream, margin ); + PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n", + __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout )); + } + +error: + return result; +} + +/* Callback interface */ + +static void OnExit( void *data ) +{ + PaAlsaStream *stream = (PaAlsaStream *) data; + + assert( data ); + + PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); + + stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */ + AlsaStop( stream, stream->callbackAbort ); + stream->callbackAbort = 0; /* Clear state */ + + PA_DEBUG(( "OnExit: Stoppage\n" )); + + /* Eventually notify user all buffers have played */ + if( stream->streamRepresentation.streamFinishedCallback ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + stream->isActive = 0; +} + +static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo ) +{ + snd_pcm_status_t *capture_status, *playback_status; + snd_timestamp_t capture_timestamp, playback_timestamp; + PaTime capture_time = 0., playback_time = 0.; + + snd_pcm_status_alloca( &capture_status ); + snd_pcm_status_alloca( &playback_status ); + + if( stream->capture.pcm ) + { + snd_pcm_sframes_t capture_delay; + + snd_pcm_status( stream->capture.pcm, capture_status ); + snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); + + capture_time = capture_timestamp.tv_sec + + ((PaTime)capture_timestamp.tv_usec / 1000000.0); + timeInfo->currentTime = capture_time; + + capture_delay = snd_pcm_status_get_delay( capture_status ); + timeInfo->inputBufferAdcTime = timeInfo->currentTime - + (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; + } + if( stream->playback.pcm ) + { + snd_pcm_sframes_t playback_delay; + + snd_pcm_status( stream->playback.pcm, playback_status ); + snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); + + playback_time = playback_timestamp.tv_sec + + ((PaTime)playback_timestamp.tv_usec / 1000000.0); + + if( stream->capture.pcm ) /* Full duplex */ + { + /* Hmm, we have both a playback and a capture timestamp. + * Hopefully they are the same... */ + if( fabs( capture_time - playback_time ) > 0.01 ) + PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time))); + } + else + timeInfo->currentTime = playback_time; + + playback_delay = snd_pcm_status_get_delay( playback_status ); + timeInfo->outputBufferDacTime = timeInfo->currentTime + + (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; + } +} + +/** Called after buffer processing is finished. + * + * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime. + * + * @param numFrames The number of frames that has been processed + * @param xrun Return whether an xrun has occurred + */ +static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun ) +{ + PaError result = paNoError; + int res; + + /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed + * afterwards + */ + if( !self->ready ) + goto end; + + res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames ); + if( res == -EPIPE || res == -ESTRPIPE ) + { + *xrun = 1; + } + else + { + ENSURE_( res, paUnanticipatedHostError ); + } + +end: +error: + return result; +} + +/* Extract buffer from channel area */ +static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset ) +{ + return (unsigned char *) area->addr + (area->first + offset * area->step) / 8; +} + +/** Do necessary adaption between user and host channels. + * + @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and + duplicating mono information if host outputs come in pairs. + */ +static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames ) +{ + PaError result = paNoError; + unsigned char *p; + int i; + int unusedChans = self->numHostChannels - self->numUserChannels; + unsigned char *src, *dst; + int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0; + + assert( StreamDirection_Out == self->streamDir ); + + if( self->hostInterleaved ) + { + int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); + unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset ); + + /* Start after the last user channel */ + p = buffer + self->numUserChannels * swidth; + + if( convertMono ) + { + /* Convert the last user channel into stereo pair */ + src = buffer + (self->numUserChannels - 1) * swidth; + for( i = 0; i < numFrames; ++i ) + { + dst = src + swidth; + memcpy( dst, src, swidth ); + src += self->numHostChannels * swidth; + } + + /* Don't touch the channel we just wrote to */ + p += swidth; + --unusedChans; + } + + if( unusedChans > 0 ) + { + /* Silence unused output channels */ + for( i = 0; i < numFrames; ++i ) + { + memset( p, 0, swidth * unusedChans ); + p += self->numHostChannels * swidth; + } + } + } + else + { + /* We extract the last user channel */ + if( convertMono ) + { + ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas + + (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError ); + --unusedChans; + } + if( unusedChans > 0 ) + { + snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames, + self->nativeFormat ); + } + } + +error: + return result; +} + +static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred ) +{ + PaError result = paNoError; + int xrun = 0; + + if( self->capture.pcm ) + { + PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) ); + } + if( self->playback.pcm ) + { + if( self->playback.numHostChannels > self->playback.numUserChannels ) + PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) ); + PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) ); + } + +error: + *xrunOccurred = xrun; + return result; +} + +/** Update the number of available frames. + * + */ +static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred ) +{ + PaError result = paNoError; + snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm ); + *xrunOccurred = 0; + + if( -EPIPE == framesAvail ) + { + *xrunOccurred = 1; + framesAvail = 0; + } + else + ENSURE_( framesAvail, paUnanticipatedHostError ); + + *numFrames = framesAvail; + +error: + return result; +} + +/** Fill in pollfd objects. + */ +static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds ) +{ + PaError result = paNoError; + int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds ); + assert( ret == self->nfds ); + + self->ready = 0; + + return result; +} + +/** Examine results from poll(). + * + * @param pfds pollfds to inspect + * @param shouldPoll Should we continue to poll + * @param xrun Has an xrun occurred + */ +static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun ) +{ + PaError result = paNoError; + unsigned short revents; + + ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError ); + if( revents != 0 ) + { + if( revents & POLLERR ) + { + *xrun = 1; + } + else + self->ready = 1; + + *shouldPoll = 0; + } + +error: + return result; +} + +/** Return the number of available frames for this stream. + * + * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore + * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback. + * + * @param queryCapture Check available for capture + * @param queryPlayback Check available for playback + * @param available The returned number of frames + * @param xrunOccurred Return whether an xrun has occurred + */ +static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long + *available, int *xrunOccurred ) +{ + PaError result = paNoError; + unsigned long captureFrames, playbackFrames; + *xrunOccurred = 0; + + assert( queryCapture || queryPlayback ); + + if( queryCapture ) + { + assert( self->capture.pcm ); + PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) ); + if( *xrunOccurred ) + goto end; + } + if( queryPlayback ) + { + assert( self->playback.pcm ); + PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) ); + if( *xrunOccurred ) + goto end; + } + + if( queryCapture && queryPlayback ) + { + *available = PA_MIN( captureFrames, playbackFrames ); + } + else if( queryCapture ) + { + *available = captureFrames; + } + else + { + *available = playbackFrames; + } + +end: +error: + return result; +} + +/** Wait for and report available buffer space from ALSA. + * + * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more. + * Both of these operations can uncover xrun conditions. + * + * @concern Xruns Both polling and querying available frames can report an xrun condition. + * + * @param framesAvail Return the number of available frames + * @param xrunOccurred Return whether an xrun has occurred + */ +static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred ) +{ + PaError result = paNoError; + int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL; + int pollTimeout = self->pollTimeout; + int xrun = 0; + + assert( self ); + assert( framesAvail ); + + if( !self->callbackMode ) + { + /* In blocking mode we will only wait if necessary */ + PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL, + framesAvail, &xrun ) ); + if( xrun ) + { + goto end; + } + + if( *framesAvail > 0 ) + { + /* Mark pcms ready from poll */ + if( self->capture.pcm ) + self->capture.ready = 1; + if( self->playback.pcm ) + self->playback.ready = 1; + + goto end; + } + } + + while( pollPlayback || pollCapture ) + { + int totalFds = 0; + struct pollfd *capturePfds = NULL, *playbackPfds = NULL; + + pthread_testcancel(); + + if( pollCapture ) + { + capturePfds = self->pfds; + PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) ); + totalFds += self->capture.nfds; + } + if( pollPlayback ) + { + playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0); + PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) ); + totalFds += self->playback.nfds; + } + + if( poll( self->pfds, totalFds, pollTimeout ) < 0 ) + { + /* XXX: Depend on preprocessor condition? */ + if( errno == EINTR ) { /* gdb */ + continue; + } + + /* TODO: Add macro for checking system calls */ + PA_ENSURE( paInternalError ); + } + + /* check the return status of our pfds */ + if( pollCapture ) + { + PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) ); + } + if( pollPlayback ) + { + PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) ); + } + if( xrun ) + { + break; + } + + /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two. + * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will + * stop polling. + */ + if( self->capture.pcm && self->playback.pcm ) + { + if( pollCapture && !pollPlayback ) + { + PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) ); + } + else if( pollPlayback && !pollCapture ) + { + PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) ); + } + } + } + + if( !xrun ) + { + /* Get the number of available frames for the pcms that are marked ready. + * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for + * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput + * is not specified. + */ + int captureReady = self->capture.pcm ? self->capture.ready : 0, + playbackReady = self->playback.pcm ? self->playback.ready : 0; + PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) ); + + if( self->capture.pcm && self->playback.pcm ) + { + if( !self->playback.ready && !self->neverDropInput ) + { + /* TODO: Drop input */ + } + } + } + +end: +error: + if( xrun ) + { + /* Recover from the xrun state */ + PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); + *framesAvail = 0; + } + *xrunOccurred = xrun; + + return result; +} + +/** Register per-channel ALSA buffer information with buffer processor. + * + * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the + * number of host and user channels is taken into account. + * + * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames. + */ +static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, + unsigned long *numFrames, int *xrun ) +{ + PaError result = paNoError; + const snd_pcm_channel_area_t *areas, *area; + void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) = + StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel; + unsigned char *buffer, *p; + int i; + unsigned long framesAvail; + + /* This _must_ be called before mmap_begin */ + PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) ); + if( *xrun ) + { + *numFrames = 0; + goto end; + } + + ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError ); + + if( self->hostInterleaved ) + { + int swidth = snd_pcm_format_size( self->nativeFormat, 1 ); + + p = buffer = ExtractAddress( areas, self->offset ); + for( i = 0; i < self->numUserChannels; ++i ) + { + /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */ + setChannel( bp, i, p, self->numHostChannels ); + p += swidth; + } + } + else + { + for( i = 0; i < self->numUserChannels; ++i ) + { + area = areas + i; + buffer = ExtractAddress( area, self->offset ); + setChannel( bp, i, buffer, 1 ); + } + } + + /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */ + self->channelAreas = (snd_pcm_channel_area_t *)areas; + +end: +error: + return result; +} + +/** Initiate buffer processing. + * + * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set. + * + * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is + * calculated. + * + * @param numFrames On entrance the number of available frames, on exit the number of received frames + * @param xrunOccurred Return whether an xrun has occurred + */ +static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred ) +{ + PaError result = paNoError; + unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0; + int xrun = 0; + + /* Extract per-channel ALSA buffer pointers and register them with the buffer processor. + * It is possible that a direction is not marked ready however, because it is out of sync with the other. + */ + if( self->capture.pcm && self->capture.ready ) + { + captureFrames = *numFrames; + PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames, + &xrun ) ); + } + if( self->playback.pcm && self->playback.ready ) + { + playbackFrames = *numFrames; + PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames, + &xrun ) ); + } + if( xrun ) + { + /* Nothing more to do */ + assert( 0 == commonFrames ); + goto end; + } + + commonFrames = PA_MIN( captureFrames, playbackFrames ); + assert( commonFrames <= *numFrames ); + + /* Inform PortAudio of the number of frames we got. + * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on + * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply + * discard the excess input or call the callback with paOutputOverflow flagged. + */ + if( self->capture.pcm ) + { + if( self->capture.ready ) + { + PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames ); + } + else + { + /* We have input underflow */ + PaUtil_SetNoInput( &self->bufferProcessor ); + } + } + if( self->playback.pcm ) + { + if( self->playback.ready ) + { + PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames ); + } + else + { + /* We have output underflow, but keeping input data (paNeverDropInput) */ + /* assert( self->neverDropInput ); */ + PaUtil_SetNoOutput( &self->bufferProcessor ); + } + } + +end: + *numFrames = commonFrames; +error: + if( xrun ) + { + PA_ENSURE( PaAlsaStream_HandleXrun( self ) ); + *numFrames = 0; + } + *xrunOccurred = xrun; + + return result; +} + +/** Callback thread's function. + * + * Roughly, the workflow can be described in the following way: The number of available frames that can be processed + * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount + * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with + * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can + * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected). + */ +static void *CallbackThreadFunc( void *userData ) +{ + PaError result = paNoError, *pres = NULL; + PaAlsaStream *stream = (PaAlsaStream*) userData; + PaStreamCallbackTimeInfo timeInfo = {0, 0, 0}; + snd_pcm_sframes_t startThreshold = 0; + int callbackResult = paContinue; + PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */ + int streamStarted = 0; + + assert( stream ); + + callbackThread_ = pthread_self(); + /* Execute OnExit when exiting */ + pthread_cleanup_push( &OnExit, stream ); + + /* Not implemented */ + assert( !stream->primeBuffers ); + + /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the + * stream is started immediately. The latter involves signaling the waiting main thread. + */ + if( stream->primeBuffers ) + { + snd_pcm_sframes_t avail; + + if( stream->playback.pcm ) + ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError ); + if( stream->capture.pcm && !stream->pcmsSynced ) + ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError ); + + /* We can't be certain that the whole ring buffer is available for priming, but there should be + * at least one period */ + avail = snd_pcm_avail_update( stream->playback.pcm ); + startThreshold = avail - (avail % stream->playback.framesPerBuffer); + assert( startThreshold >= stream->playback.framesPerBuffer ); + } + else + { + ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 ); + PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */ + ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 ); + ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 ); + + streamStarted = 1; + } + + while( 1 ) + { + unsigned long framesAvail, framesGot; + int xrun = 0; + + pthread_testcancel(); + + /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively + * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output). + */ + if( stream->callbackStop && paContinue == callbackResult ) + { + PA_DEBUG(( "Setting callbackResult to paComplete\n" )); + callbackResult = paComplete; + } + + if( paContinue != callbackResult ) + { + stream->callbackAbort = (paAbort == callbackResult); + if( stream->callbackAbort || + /** @concern BlockAdaption Go on if adaption buffers are empty */ + PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) + goto end; + + PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ )); + /* There is still buffered output that needs to be processed */ + } + + /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have + * a number of available frames. + */ + PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); + if( xrun ) + { + assert( 0 == framesAvail ); + continue; + + /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due + * to constant xruns, it might be desirable to notify the user of this. + */ + } + + /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the + * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller + * portions at a time than is available as a whole. Therefore we should be prepared to process several + * chunks successively. The buffers are passed to the PA buffer processor. + */ + while( framesAvail > 0 ) + { + xrun = 0; + + pthread_testcancel(); + + /** @concern Xruns Under/overflows are to be reported to the callback */ + if( stream->underrun > 0.0 ) + { + cbFlags |= paOutputUnderflow; + stream->underrun = 0.0; + } + if( stream->overrun > 0.0 ) + { + cbFlags |= paInputOverflow; + stream->overrun = 0.0; + } + if( stream->capture.pcm && stream->playback.pcm ) + { + /** @concern FullDuplex It's possible that only one direction is being processed to avoid an + * under- or overflow, this should be reported correspondingly */ + if( !stream->capture.ready ) + { + cbFlags |= paInputUnderflow; + PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ )); + } + else if( !stream->playback.ready ) + { + cbFlags |= paOutputOverflow; + PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ )); + } + } + + CallbackUpdate( &stream->threading ); + CalculateTimeInfo( stream, &timeInfo ); + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); + cbFlags = 0; + + /* CPU load measurement should include processing activivity external to the stream callback */ + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + framesGot = framesAvail; + PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); + framesAvail -= framesGot; + + if( framesGot > 0 ) + { + assert( !xrun ); + + PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); + } + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot ); + + if( framesGot == 0 ) + { + if( !xrun ) + PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ )); + + /* Go back to polling for more frames */ + break; + + } + + if( paContinue != callbackResult ) + break; + } + } + + /* Match pthread_cleanup_push */ + pthread_cleanup_pop( 1 ); + +end: + pthread_exit( pres ); + +error: + /* Pass on error code */ + pres = malloc( sizeof (PaError) ); + *pres = result; + + goto end; +} + +/* Blocking interface */ + +static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + unsigned long framesGot, framesAvail; + void *userBuffer; + snd_pcm_t *save = stream->playback.pcm; + + assert( stream ); + + PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream ); + + /* Disregard playback */ + stream->playback.pcm = NULL; + + if( stream->overrun > 0. ) + { + result = paInputOverflowed; + stream->overrun = 0.0; + } + + if( stream->capture.userInterleaved ) + userBuffer = buffer; + else + { + /* Copy channels into local array */ + userBuffer = stream->capture.userBuffers; + memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels ); + } + + /* Start stream if in prepared state */ + if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) + { + ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); + } + + while( frames > 0 ) + { + int xrun = 0; + PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); + framesGot = PA_MIN( framesAvail, frames ); + + PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); + if( framesGot > 0 ) + { + framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot ); + PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); + frames -= framesGot; + } + } + +end: + stream->playback.pcm = save; + return result; +error: + goto end; +} + +static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames ) +{ + PaError result = paNoError; + signed long err; + PaAlsaStream *stream = (PaAlsaStream*)s; + snd_pcm_uframes_t framesGot, framesAvail; + const void *userBuffer; + snd_pcm_t *save = stream->capture.pcm; + + assert( stream ); + + PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream ); + + /* Disregard capture */ + stream->capture.pcm = NULL; + + if( stream->underrun > 0. ) + { + result = paOutputUnderflowed; + stream->underrun = 0.0; + } + + if( stream->playback.userInterleaved ) + userBuffer = buffer; + else /* Copy channels into local array */ + { + userBuffer = stream->playback.userBuffers; + memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels ); + } + + while( frames > 0 ) + { + int xrun = 0; + snd_pcm_uframes_t hwAvail; + + PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); + framesGot = PA_MIN( framesAvail, frames ); + + PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); + if( framesGot > 0 ) + { + framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot ); + PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) ); + frames -= framesGot; + } + + /* Frames residing in buffer */ + PA_ENSURE( err = GetStreamWriteAvailable( stream ) ); + framesAvail = err; + hwAvail = stream->playback.bufferSize - framesAvail; + + /* Start stream after one period of samples worth */ + if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED && + hwAvail >= stream->playback.framesPerBuffer ) + { + ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError ); + } + } + +end: + stream->capture.pcm = save; + return result; +error: + goto end; +} + +/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */ +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + unsigned long avail; + int xrun; + + PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); + if( xrun ) + { + PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); + PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) ); + if( xrun ) + PA_ENSURE( paInputOverflowed ); + } + + return (signed long)avail; + +error: + return result; +} + +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + unsigned long avail; + int xrun; + + PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) ); + if( xrun ) + { + snd_pcm_sframes_t savail; + + PA_ENSURE( PaAlsaStream_HandleXrun( stream ) ); + savail = snd_pcm_avail_update( stream->playback.pcm ); + + /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */ + ENSURE_( savail, paUnanticipatedHostError ); + + avail = (unsigned long) savail; + } + + return (signed long)avail; + +error: + return result; +} + +/* Extensions */ + +/* Initialize host api specific structure */ +void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ) +{ + info->size = sizeof (PaAlsaStreamInfo); + info->hostApiType = paALSA; + info->version = 1; + info->deviceString = NULL; +} + +void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ) +{ + PaAlsaStream *stream = (PaAlsaStream *) s; + stream->threading.rtSched = enable; +} + +void PaAlsa_EnableWatchdog( PaStream *s, int enable ) +{ + PaAlsaStream *stream = (PaAlsaStream *) s; + stream->threading.useWatchdog = enable; +} diff --git a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h index fd4174528..e6f44b16e 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h +++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h @@ -1,64 +1,64 @@ -#ifndef PA_LINUX_ALSA_H -#define PA_LINUX_ALSA_H - -/* - * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * ALSA-specific extensions - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - * - */ - -/** @file - * ALSA-specific PortAudio API extension header file. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct PaAlsaStreamInfo -{ - unsigned long size; - PaHostApiTypeId hostApiType; - unsigned long version; - - const char *deviceString; -} -PaAlsaStreamInfo; - -void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ); - -void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ); - -void PaAlsa_EnableWatchdog( PaStream *s, int enable ); - -#ifdef __cplusplus -} -#endif - -#endif +#ifndef PA_LINUX_ALSA_H +#define PA_LINUX_ALSA_H + +/* + * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $ + * PortAudio Portable Real-Time Audio Library + * ALSA-specific extensions + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + * + */ + +/** @file + * ALSA-specific PortAudio API extension header file. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct PaAlsaStreamInfo +{ + unsigned long size; + PaHostApiTypeId hostApiType; + unsigned long version; + + const char *deviceString; +} +PaAlsaStreamInfo; + +void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ); + +void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ); + +void PaAlsa_EnableWatchdog( PaStream *s, int enable ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pjmedia/src/pjmedia/portaudio/pa_process.c b/pjmedia/src/pjmedia/portaudio/pa_process.c index 7b25b8bfc..cf711d417 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_process.c +++ b/pjmedia/src/pjmedia/portaudio/pa_process.c @@ -1,1756 +1,1756 @@ -/* - * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $ - * Portable Audio I/O Library - * streamCallback <-> host buffer processing adapter - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Buffer Processor implementation. - - The code in this file is not optimised yet - although it's not clear that - it needs to be. there may appear to be redundancies - that could be factored into common functions, but the redundanceis are left - intentionally as each appearance may have different optimisation possibilities. - - The optimisations which are planned involve only converting data in-place - where possible, rather than copying to the temp buffer(s). - - Note that in the extreme case of being able to convert in-place, and there - being no conversion necessary there should be some code which short-circuits - the operation. - - @todo Consider cache tilings for intereave<->deinterleave. - - @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing() - - @todo specify and implement some kind of logical policy for handling the - underflow and overflow stream flags when the underflow/overflow overlaps - multiple user buffers/callbacks. - - @todo provide support for priming the buffers with data from the callback. - The client interface is now implemented through PaUtil_SetNoInput() - which sets bp->hostInputChannels[0][0].data to zero. However this is - currently only implemented in NonAdaptingProcess(). It shouldn't be - needed for AdaptingInputOnlyProcess() (no priming should ever be - requested for AdaptingInputOnlyProcess()). - Not sure if additional work should be required to make it work with - AdaptingOutputOnlyProcess, but it definitely is required for - AdaptingProcess. - - @todo implement PaUtil_SetNoOutput for AdaptingProcess - - @todo don't allocate temp buffers for blocking streams unless they are - needed. At the moment they are needed, but perhaps for host APIs - where the implementation passes a buffer to the host they could be - used. -*/ - - -#include -#include /* memset() */ - -#include "pa_process.h" -#include "pa_util.h" - - -#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024 - -#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) ) - - -/* greatest common divisor - PGCD in French */ -static unsigned long GCD( unsigned long a, unsigned long b ) -{ - return (b==0) ? a : GCD( b, a%b); -} - -/* least common multiple - PPCM in French */ -static unsigned long LCM( unsigned long a, unsigned long b ) -{ - return (a*b) / GCD(a,b); -} - -#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b)) - -static unsigned long CalculateFrameShift( unsigned long M, unsigned long N ) -{ - unsigned long result = 0; - unsigned long i; - unsigned long lcm; - - assert( M > 0 ); - assert( N > 0 ); - - lcm = LCM( M, N ); - for( i = M; i < lcm; i += M ) - result = PA_MAX_( result, i % N ); - - return result; -} - - -PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp, - int inputChannelCount, PaSampleFormat userInputSampleFormat, - PaSampleFormat hostInputSampleFormat, - int outputChannelCount, PaSampleFormat userOutputSampleFormat, - PaSampleFormat hostOutputSampleFormat, - double sampleRate, - PaStreamFlags streamFlags, - unsigned long framesPerUserBuffer, - unsigned long framesPerHostBuffer, - PaUtilHostBufferSizeMode hostBufferSizeMode, - PaStreamCallback *streamCallback, void *userData ) -{ - PaError result = paNoError; - PaError bytesPerSample; - unsigned long tempInputBufferSize, tempOutputBufferSize; - - /* initialize buffer ptrs to zero so they can be freed if necessary in error */ - bp->tempInputBuffer = 0; - bp->tempInputBufferPtrs = 0; - bp->tempOutputBuffer = 0; - bp->tempOutputBufferPtrs = 0; - - bp->framesPerUserBuffer = framesPerUserBuffer; - bp->framesPerHostBuffer = framesPerHostBuffer; - - bp->inputChannelCount = inputChannelCount; - bp->outputChannelCount = outputChannelCount; - - bp->hostBufferSizeMode = hostBufferSizeMode; - - bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0; - bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0; - - if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */ - { - bp->useNonAdaptingProcess = 1; - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = 0; - - if( hostBufferSizeMode == paUtilFixedHostBufferSize - || hostBufferSizeMode == paUtilBoundedHostBufferSize ) - { - bp->framesPerTempBuffer = framesPerHostBuffer; - } - else /* unknown host buffer size */ - { - bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_; - } - } - else - { - bp->framesPerTempBuffer = framesPerUserBuffer; - - if( hostBufferSizeMode == paUtilFixedHostBufferSize - && framesPerHostBuffer % framesPerUserBuffer == 0 ) - { - bp->useNonAdaptingProcess = 1; - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = 0; - } - else - { - bp->useNonAdaptingProcess = 0; - - if( inputChannelCount > 0 && outputChannelCount > 0 ) - { - /* full duplex */ - if( hostBufferSizeMode == paUtilFixedHostBufferSize ) - { - unsigned long frameShift = - CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer ); - - if( framesPerUserBuffer > framesPerHostBuffer ) - { - bp->initialFramesInTempInputBuffer = frameShift; - bp->initialFramesInTempOutputBuffer = 0; - } - else - { - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = frameShift; - } - } - else /* variable host buffer size, add framesPerUserBuffer latency */ - { - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = framesPerUserBuffer; - } - } - else - { - /* half duplex */ - bp->initialFramesInTempInputBuffer = 0; - bp->initialFramesInTempOutputBuffer = 0; - } - } - } - - - bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; - bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; - - - if( inputChannelCount > 0 ) - { - bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat ); - if( bytesPerSample > 0 ) - { - bp->bytesPerHostInputSample = bytesPerSample; - } - else - { - result = bytesPerSample; - goto error; - } - - bytesPerSample = Pa_GetSampleSize( userInputSampleFormat ); - if( bytesPerSample > 0 ) - { - bp->bytesPerUserInputSample = bytesPerSample; - } - else - { - result = bytesPerSample; - goto error; - } - - bp->inputConverter = - PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags ); - - bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat ); - - bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1; - - - tempInputBufferSize = - bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount; - - bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize ); - if( bp->tempInputBuffer == 0 ) - { - result = paInsufficientMemory; - goto error; - } - - if( bp->framesInTempInputBuffer > 0 ) - memset( bp->tempInputBuffer, 0, tempInputBufferSize ); - - if( userInputSampleFormat & paNonInterleaved ) - { - bp->tempInputBufferPtrs = - (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount ); - if( bp->tempInputBufferPtrs == 0 ) - { - result = paInsufficientMemory; - goto error; - } - } - - bp->hostInputChannels[0] = (PaUtilChannelDescriptor*) - PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2); - if( bp->hostInputChannels[0] == 0 ) - { - result = paInsufficientMemory; - goto error; - } - - bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount]; - } - - if( outputChannelCount > 0 ) - { - bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat ); - if( bytesPerSample > 0 ) - { - bp->bytesPerHostOutputSample = bytesPerSample; - } - else - { - result = bytesPerSample; - goto error; - } - - bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat ); - if( bytesPerSample > 0 ) - { - bp->bytesPerUserOutputSample = bytesPerSample; - } - else - { - result = bytesPerSample; - goto error; - } - - bp->outputConverter = - PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags ); - - bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat ); - - bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1; - - tempOutputBufferSize = - bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount; - - bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize ); - if( bp->tempOutputBuffer == 0 ) - { - result = paInsufficientMemory; - goto error; - } - - if( bp->framesInTempOutputBuffer > 0 ) - memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); - - if( userOutputSampleFormat & paNonInterleaved ) - { - bp->tempOutputBufferPtrs = - (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount ); - if( bp->tempOutputBufferPtrs == 0 ) - { - result = paInsufficientMemory; - goto error; - } - } - - bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*) - PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 ); - if( bp->hostOutputChannels[0] == 0 ) - { - result = paInsufficientMemory; - goto error; - } - - bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount]; - } - - PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator ); - - bp->samplePeriod = 1. / sampleRate; - - bp->streamCallback = streamCallback; - bp->userData = userData; - - return result; - -error: - if( bp->tempInputBuffer ) - PaUtil_FreeMemory( bp->tempInputBuffer ); - - if( bp->tempInputBufferPtrs ) - PaUtil_FreeMemory( bp->tempInputBufferPtrs ); - - if( bp->hostInputChannels[0] ) - PaUtil_FreeMemory( bp->hostInputChannels[0] ); - - if( bp->tempOutputBuffer ) - PaUtil_FreeMemory( bp->tempOutputBuffer ); - - if( bp->tempOutputBufferPtrs ) - PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); - - if( bp->hostOutputChannels[0] ) - PaUtil_FreeMemory( bp->hostOutputChannels[0] ); - - return result; -} - - -void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp ) -{ - if( bp->tempInputBuffer ) - PaUtil_FreeMemory( bp->tempInputBuffer ); - - if( bp->tempInputBufferPtrs ) - PaUtil_FreeMemory( bp->tempInputBufferPtrs ); - - if( bp->hostInputChannels[0] ) - PaUtil_FreeMemory( bp->hostInputChannels[0] ); - - if( bp->tempOutputBuffer ) - PaUtil_FreeMemory( bp->tempOutputBuffer ); - - if( bp->tempOutputBufferPtrs ) - PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); - - if( bp->hostOutputChannels[0] ) - PaUtil_FreeMemory( bp->hostOutputChannels[0] ); -} - - -void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp ) -{ - unsigned long tempInputBufferSize, tempOutputBufferSize; - - bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; - bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; - - if( bp->framesInTempInputBuffer > 0 ) - { - tempInputBufferSize = - bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount; - memset( bp->tempInputBuffer, 0, tempInputBufferSize ); - } - - if( bp->framesInTempOutputBuffer > 0 ) - { - tempOutputBufferSize = - bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount; - memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); - } -} - - -unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp ) -{ - return bp->initialFramesInTempInputBuffer; -} - - -unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp ) -{ - return bp->initialFramesInTempOutputBuffer; -} - - -void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp, - unsigned long frameCount ) -{ - if( frameCount == 0 ) - bp->hostInputFrameCount[0] = bp->framesPerHostBuffer; - else - bp->hostInputFrameCount[0] = frameCount; -} - - -void PaUtil_SetNoInput( PaUtilBufferProcessor* bp ) -{ - assert( bp->inputChannelCount > 0 ); - - bp->hostInputChannels[0][0].data = 0; -} - - -void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data, unsigned int stride ) -{ - assert( channel < bp->inputChannelCount ); - - bp->hostInputChannels[0][channel].data = data; - bp->hostInputChannels[0][channel].stride = stride; -} - - -void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp, - unsigned int firstChannel, void *data, unsigned int channelCount ) -{ - unsigned int i; - unsigned int channel = firstChannel; - unsigned char *p = (unsigned char*)data; - - if( channelCount == 0 ) - channelCount = bp->inputChannelCount; - - assert( firstChannel < bp->inputChannelCount ); - assert( firstChannel + channelCount <= bp->inputChannelCount ); - - for( i=0; i< channelCount; ++i ) - { - bp->hostInputChannels[0][channel+i].data = p; - p += bp->bytesPerHostInputSample; - bp->hostInputChannels[0][channel+i].stride = channelCount; - } -} - - -void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data ) -{ - assert( channel < bp->inputChannelCount ); - - bp->hostInputChannels[0][channel].data = data; - bp->hostInputChannels[0][channel].stride = 1; -} - - -void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp, - unsigned long frameCount ) -{ - bp->hostInputFrameCount[1] = frameCount; -} - - -void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data, unsigned int stride ) -{ - assert( channel < bp->inputChannelCount ); - - bp->hostInputChannels[1][channel].data = data; - bp->hostInputChannels[1][channel].stride = stride; -} - - -void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp, - unsigned int firstChannel, void *data, unsigned int channelCount ) -{ - unsigned int i; - unsigned int channel = firstChannel; - unsigned char *p = (unsigned char*)data; - - if( channelCount == 0 ) - channelCount = bp->inputChannelCount; - - assert( firstChannel < bp->inputChannelCount ); - assert( firstChannel + channelCount <= bp->inputChannelCount ); - - for( i=0; i< channelCount; ++i ) - { - bp->hostInputChannels[1][channel+i].data = p; - p += bp->bytesPerHostInputSample; - bp->hostInputChannels[1][channel+i].stride = channelCount; - } -} - - -void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data ) -{ - assert( channel < bp->inputChannelCount ); - - bp->hostInputChannels[1][channel].data = data; - bp->hostInputChannels[1][channel].stride = 1; -} - - -void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp, - unsigned long frameCount ) -{ - if( frameCount == 0 ) - bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer; - else - bp->hostOutputFrameCount[0] = frameCount; -} - - -void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp ) -{ - assert( bp->outputChannelCount > 0 ); - - bp->hostOutputChannels[0][0].data = 0; -} - - -void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data, unsigned int stride ) -{ - assert( channel < bp->outputChannelCount ); - - bp->hostOutputChannels[0][channel].data = data; - bp->hostOutputChannels[0][channel].stride = stride; -} - - -void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp, - unsigned int firstChannel, void *data, unsigned int channelCount ) -{ - unsigned int i; - unsigned int channel = firstChannel; - unsigned char *p = (unsigned char*)data; - - if( channelCount == 0 ) - channelCount = bp->outputChannelCount; - - assert( firstChannel < bp->outputChannelCount ); - assert( firstChannel + channelCount <= bp->outputChannelCount ); - - for( i=0; i< channelCount; ++i ) - { - bp->hostOutputChannels[0][channel+i].data = p; - p += bp->bytesPerHostOutputSample; - bp->hostOutputChannels[0][channel+i].stride = channelCount; - } -} - - -void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data ) -{ - assert( channel < bp->outputChannelCount ); - - bp->hostOutputChannels[0][channel].data = data; - bp->hostOutputChannels[0][channel].stride = 1; -} - - -void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp, - unsigned long frameCount ) -{ - bp->hostOutputFrameCount[1] = frameCount; -} - - -void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data, unsigned int stride ) -{ - assert( channel < bp->outputChannelCount ); - - bp->hostOutputChannels[1][channel].data = data; - bp->hostOutputChannels[1][channel].stride = stride; -} - - -void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp, - unsigned int firstChannel, void *data, unsigned int channelCount ) -{ - unsigned int i; - unsigned int channel = firstChannel; - unsigned char *p = (unsigned char*)data; - - if( channelCount == 0 ) - channelCount = bp->outputChannelCount; - - assert( firstChannel < bp->outputChannelCount ); - assert( firstChannel + channelCount <= bp->outputChannelCount ); - - for( i=0; i< channelCount; ++i ) - { - bp->hostOutputChannels[1][channel+i].data = p; - p += bp->bytesPerHostOutputSample; - bp->hostOutputChannels[1][channel+i].stride = channelCount; - } -} - - -void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, - unsigned int channel, void *data ) -{ - assert( channel < bp->outputChannelCount ); - - bp->hostOutputChannels[1][channel].data = data; - bp->hostOutputChannels[1][channel].stride = 1; -} - - -void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp, - PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags ) -{ - bp->timeInfo = timeInfo; - - /* the first streamCallback will be called to process samples which are - currently in the input buffer before the ones starting at the timeInfo time */ - - bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod; - - bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */ - - /* the first streamCallback will be called to generate samples which will be - outputted after the frames currently in the output buffer have been - outputted. */ - bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod; - - bp->callbackStatusFlags = callbackStatusFlags; - - bp->hostInputFrameCount[1] = 0; - bp->hostOutputFrameCount[1] = 0; -} - - -/* - NonAdaptingProcess() is a simple buffer copying adaptor that can handle - both full and half duplex copies. It processes framesToProcess frames, - broken into blocks bp->framesPerTempBuffer long. - This routine can be used when the streamCallback doesn't care what length - the buffers are, or when framesToProcess is an integer multiple of - bp->framesPerTempBuffer, in which case streamCallback will always be called - with bp->framesPerTempBuffer samples. -*/ -static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp, - int *streamCallbackResult, - PaUtilChannelDescriptor *hostInputChannels, - PaUtilChannelDescriptor *hostOutputChannels, - unsigned long framesToProcess ) -{ - void *userInput, *userOutput; - unsigned char *srcBytePtr, *destBytePtr; - unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - unsigned long frameCount; - unsigned long framesToGo = framesToProcess; - unsigned long framesProcessed = 0; - - - if( *streamCallbackResult == paContinue ) - { - do - { - frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo ); - - /* configure user input buffer and convert input data (host -> user) */ - if( bp->inputChannelCount == 0 ) - { - /* no input */ - userInput = 0; - } - else /* there are input channels */ - { - /* - could use more elaborate logic here and sometimes process - buffers in-place. - */ - - destBytePtr = (unsigned char *)bp->tempInputBuffer; - - if( bp->userInputIsInterleaved ) - { - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - userInput = bp->tempInputBuffer; - } - else /* user input is not interleaved */ - { - destSampleStrideSamples = 1; - destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample; - - /* setup non-interleaved ptrs */ - for( i=0; iinputChannelCount; ++i ) - { - bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + - i * bp->bytesPerUserInputSample * frameCount; - } - - userInput = bp->tempInputBufferPtrs; - } - - if( !bp->hostInputChannels[0][0].data ) - { - /* no input was supplied (see PaUtil_SetNoInput), so - zero the input buffer */ - - for( i=0; iinputChannelCount; ++i ) - { - bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount ); - destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ - } - } - else - { - for( i=0; iinputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - frameCount, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ - - /* advance src ptr for next iteration */ - hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + - frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; - } - } - } - - /* configure user output buffer */ - if( bp->outputChannelCount == 0 ) - { - /* no output */ - userOutput = 0; - } - else /* there are output channels */ - { - if( bp->userOutputIsInterleaved ) - { - userOutput = bp->tempOutputBuffer; - } - else /* user output is not interleaved */ - { - for( i = 0; i < bp->outputChannelCount; ++i ) - { - bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + - i * bp->bytesPerUserOutputSample * frameCount; - } - - userOutput = bp->tempOutputBufferPtrs; - } - } - - *streamCallbackResult = bp->streamCallback( userInput, userOutput, - frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData ); - - if( *streamCallbackResult == paAbort ) - { - /* callback returned paAbort, don't advance framesProcessed - and framesToGo, they will be handled below */ - } - else - { - bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; - bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod; - - /* convert output data (user -> host) */ - - if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) - { - /* - could use more elaborate logic here and sometimes process - buffers in-place. - */ - - srcBytePtr = (unsigned char *)bp->tempOutputBuffer; - - if( bp->userOutputIsInterleaved ) - { - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - } - else /* user output is not interleaved */ - { - srcSampleStrideSamples = 1; - srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample; - } - - for( i=0; ioutputChannelCount; ++i ) - { - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - frameCount, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - } - - framesProcessed += frameCount; - - framesToGo -= frameCount; - } - } - while( framesToGo > 0 && *streamCallbackResult == paContinue ); - } - - if( framesToGo > 0 ) - { - /* zero any remaining frames output. There will only be remaining frames - if the callback has returned paComplete or paAbort */ - - frameCount = framesToGo; - - if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) - { - for( i=0; ioutputChannelCount; ++i ) - { - bp->outputZeroer( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - frameCount ); - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - } - - framesProcessed += frameCount; - } - - return framesProcessed; -} - - -/* - AdaptingInputOnlyProcess() is a half duplex input buffer processor. It - converts data from the input buffers into the temporary input buffer, - when the temporary input buffer is full, it calls the streamCallback. -*/ -static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp, - int *streamCallbackResult, - PaUtilChannelDescriptor *hostInputChannels, - unsigned long framesToProcess ) -{ - void *userInput, *userOutput; - unsigned char *destBytePtr; - unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - unsigned long frameCount; - unsigned long framesToGo = framesToProcess; - unsigned long framesProcessed = 0; - - userOutput = 0; - - do - { - frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer ) - ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer ) - : framesToGo; - - /* convert frameCount samples into temp buffer */ - - if( bp->userInputIsInterleaved ) - { - destBytePtr = ((unsigned char*)bp->tempInputBuffer) + - bp->bytesPerUserInputSample * bp->inputChannelCount * - bp->framesInTempInputBuffer; - - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - - userInput = bp->tempInputBuffer; - } - else /* user input is not interleaved */ - { - destBytePtr = ((unsigned char*)bp->tempInputBuffer) + - bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; - - destSampleStrideSamples = 1; - destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; - - /* setup non-interleaved ptrs */ - for( i=0; iinputChannelCount; ++i ) - { - bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + - i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer; - } - - userInput = bp->tempInputBufferPtrs; - } - - for( i=0; iinputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - frameCount, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ - - /* advance src ptr for next iteration */ - hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + - frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; - } - - bp->framesInTempInputBuffer += frameCount; - - if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer ) - { - /** - @todo (non-critical optimisation) - The conditional below implements the continue/complete/abort mechanism - simply by continuing on iterating through the input buffer, but not - passing the data to the callback. With care, the outer loop could be - terminated earlier, thus some unneeded conversion cycles would be - saved. - */ - if( *streamCallbackResult == paContinue ) - { - bp->timeInfo->outputBufferDacTime = 0; - - *streamCallbackResult = bp->streamCallback( userInput, userOutput, - bp->framesPerUserBuffer, bp->timeInfo, - bp->callbackStatusFlags, bp->userData ); - - bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; - } - - bp->framesInTempInputBuffer = 0; - } - - framesProcessed += frameCount; - - framesToGo -= frameCount; - }while( framesToGo > 0 ); - - return framesProcessed; -} - - -/* - AdaptingOutputOnlyProcess() is a half duplex output buffer processor. - It converts data from the temporary output buffer, to the output buffers, - when the temporary output buffer is empty, it calls the streamCallback. -*/ -static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp, - int *streamCallbackResult, - PaUtilChannelDescriptor *hostOutputChannels, - unsigned long framesToProcess ) -{ - void *userInput, *userOutput; - unsigned char *srcBytePtr; - unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - unsigned long frameCount; - unsigned long framesToGo = framesToProcess; - unsigned long framesProcessed = 0; - - do - { - if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue ) - { - userInput = 0; - - /* setup userOutput */ - if( bp->userOutputIsInterleaved ) - { - userOutput = bp->tempOutputBuffer; - } - else /* user output is not interleaved */ - { - for( i = 0; i < bp->outputChannelCount; ++i ) - { - bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + - i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; - } - - userOutput = bp->tempOutputBufferPtrs; - } - - bp->timeInfo->inputBufferAdcTime = 0; - - *streamCallbackResult = bp->streamCallback( userInput, userOutput, - bp->framesPerUserBuffer, bp->timeInfo, - bp->callbackStatusFlags, bp->userData ); - - if( *streamCallbackResult == paAbort ) - { - /* if the callback returned paAbort, we disregard its output */ - } - else - { - bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; - - bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; - } - } - - if( bp->framesInTempOutputBuffer > 0 ) - { - /* convert frameCount frames from user buffer to host buffer */ - - frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo ); - - if( bp->userOutputIsInterleaved ) - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * bp->outputChannelCount * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - } - else /* user output is not interleaved */ - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = 1; - srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; - } - - for( i=0; ioutputChannelCount; ++i ) - { - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - frameCount, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - - bp->framesInTempOutputBuffer -= frameCount; - } - else - { - /* no more user data is available because the callback has returned - paComplete or paAbort. Fill the remainder of the host buffer - with zeros. - */ - - frameCount = framesToGo; - - for( i=0; ioutputChannelCount; ++i ) - { - bp->outputZeroer( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - frameCount ); - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - } - - framesProcessed += frameCount; - - framesToGo -= frameCount; - - }while( framesToGo > 0 ); - - return framesProcessed; -} - -/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from - tempOutputBuffer to hostOutputChannels. This includes data conversion - and interleaving. -*/ -static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp) -{ - unsigned long maxFramesToCopy; - PaUtilChannelDescriptor *hostOutputChannels; - unsigned int frameCount; - unsigned char *srcBytePtr; - unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - - /* copy frames from user to host output buffers */ - while( bp->framesInTempOutputBuffer > 0 && - ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) ) - { - maxFramesToCopy = bp->framesInTempOutputBuffer; - - /* select the output buffer set (1st or 2nd) */ - if( bp->hostOutputFrameCount[0] > 0 ) - { - hostOutputChannels = bp->hostOutputChannels[0]; - frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy ); - } - else - { - hostOutputChannels = bp->hostOutputChannels[1]; - frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy ); - } - - if( bp->userOutputIsInterleaved ) - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * bp->outputChannelCount * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - } - else /* user output is not interleaved */ - { - srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + - bp->bytesPerUserOutputSample * - (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); - - srcSampleStrideSamples = 1; - srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; - } - - for( i=0; ioutputChannelCount; ++i ) - { - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - frameCount, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - - if( bp->hostOutputFrameCount[0] > 0 ) - bp->hostOutputFrameCount[0] -= frameCount; - else - bp->hostOutputFrameCount[1] -= frameCount; - - bp->framesInTempOutputBuffer -= frameCount; - } -} - -/* - AdaptingProcess is a full duplex adapting buffer processor. It converts - data from the temporary output buffer into the host output buffers, then - from the host input buffers into the temporary input buffers. Calling the - streamCallback when necessary. - When processPartialUserBuffers is 0, all available input data will be - consumed and all available output space will be filled. When - processPartialUserBuffers is non-zero, as many full user buffers - as possible will be processed, but partial buffers will not be consumed. -*/ -static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp, - int *streamCallbackResult, int processPartialUserBuffers ) -{ - void *userInput, *userOutput; - unsigned long framesProcessed = 0; - unsigned long framesAvailable; - unsigned long endProcessingMinFrameCount; - unsigned long maxFramesToCopy; - PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels; - unsigned int frameCount; - unsigned char *destBytePtr; - unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i, j; - - - framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */ - - if( processPartialUserBuffers ) - endProcessingMinFrameCount = 0; - else - endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1); - - /* Fill host output with remaining frames in user output (tempOutputBuffer) */ - CopyTempOutputBuffersToHostOutputBuffers( bp ); - - while( framesAvailable > endProcessingMinFrameCount ) - { - - if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue ) - { - /* the callback will not be called any more, so zero what remains - of the host output buffers */ - - for( i=0; i<2; ++i ) - { - frameCount = bp->hostOutputFrameCount[i]; - if( frameCount > 0 ) - { - hostOutputChannels = bp->hostOutputChannels[i]; - - for( j=0; joutputChannelCount; ++j ) - { - bp->outputZeroer( hostOutputChannels[j].data, - hostOutputChannels[j].stride, - frameCount ); - - /* advance dest ptr for next iteration */ - hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) + - frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample; - } - bp->hostOutputFrameCount[i] = 0; - } - } - } - - - /* copy frames from host to user input buffers */ - while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer && - ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) ) - { - maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer; - - /* select the input buffer set (1st or 2nd) */ - if( bp->hostInputFrameCount[0] > 0 ) - { - hostInputChannels = bp->hostInputChannels[0]; - frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy ); - } - else - { - hostInputChannels = bp->hostInputChannels[1]; - frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy ); - } - - /* configure conversion destination pointers */ - if( bp->userInputIsInterleaved ) - { - destBytePtr = ((unsigned char*)bp->tempInputBuffer) + - bp->bytesPerUserInputSample * bp->inputChannelCount * - bp->framesInTempInputBuffer; - - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - } - else /* user input is not interleaved */ - { - destBytePtr = ((unsigned char*)bp->tempInputBuffer) + - bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; - - destSampleStrideSamples = 1; - destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; - } - - for( i=0; iinputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - frameCount, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ - - /* advance src ptr for next iteration */ - hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + - frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; - } - - if( bp->hostInputFrameCount[0] > 0 ) - bp->hostInputFrameCount[0] -= frameCount; - else - bp->hostInputFrameCount[1] -= frameCount; - - bp->framesInTempInputBuffer += frameCount; - - /* update framesAvailable and framesProcessed based on input consumed - unless something is very wrong this will also correspond to the - amount of output generated */ - framesAvailable -= frameCount; - framesProcessed += frameCount; - } - - /* call streamCallback */ - if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer && - bp->framesInTempOutputBuffer == 0 ) - { - if( *streamCallbackResult == paContinue ) - { - /* setup userInput */ - if( bp->userInputIsInterleaved ) - { - userInput = bp->tempInputBuffer; - } - else /* user input is not interleaved */ - { - for( i = 0; i < bp->inputChannelCount; ++i ) - { - bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + - i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample; - } - - userInput = bp->tempInputBufferPtrs; - } - - /* setup userOutput */ - if( bp->userOutputIsInterleaved ) - { - userOutput = bp->tempOutputBuffer; - } - else /* user output is not interleaved */ - { - for( i = 0; i < bp->outputChannelCount; ++i ) - { - bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + - i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; - } - - userOutput = bp->tempOutputBufferPtrs; - } - - /* call streamCallback */ - - *streamCallbackResult = bp->streamCallback( userInput, userOutput, - bp->framesPerUserBuffer, bp->timeInfo, - bp->callbackStatusFlags, bp->userData ); - - bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod; - bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; - - bp->framesInTempInputBuffer = 0; - - if( *streamCallbackResult == paAbort ) - bp->framesInTempOutputBuffer = 0; - else - bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; - } - else - { - /* paComplete or paAbort has already been called. */ - - bp->framesInTempInputBuffer = 0; - } - } - - /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels) - Means to process the user output provided by the callback. Has to be called after - each callback. */ - CopyTempOutputBuffersToHostOutputBuffers( bp ); - - } - - return framesProcessed; -} - - -unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult ) -{ - unsigned long framesToProcess, framesToGo; - unsigned long framesProcessed = 0; - - if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 - && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */ - && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ ) - { - assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) == - (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) ); - } - - assert( *streamCallbackResult == paContinue - || *streamCallbackResult == paComplete - || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */ - - if( bp->useNonAdaptingProcess ) - { - if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) - { - /* full duplex non-adapting process, splice buffers if they are - different lengths */ - - framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */ - - do{ - unsigned long noInputInputFrameCount; - unsigned long *hostInputFrameCount; - PaUtilChannelDescriptor *hostInputChannels; - unsigned long noOutputOutputFrameCount; - unsigned long *hostOutputFrameCount; - PaUtilChannelDescriptor *hostOutputChannels; - unsigned long framesProcessedThisIteration; - - if( !bp->hostInputChannels[0][0].data ) - { - /* no input was supplied (see PaUtil_SetNoInput) - NonAdaptingProcess knows how to deal with this - */ - noInputInputFrameCount = framesToGo; - hostInputFrameCount = &noInputInputFrameCount; - hostInputChannels = 0; - } - else if( bp->hostInputFrameCount[0] != 0 ) - { - hostInputFrameCount = &bp->hostInputFrameCount[0]; - hostInputChannels = bp->hostInputChannels[0]; - } - else - { - hostInputFrameCount = &bp->hostInputFrameCount[1]; - hostInputChannels = bp->hostInputChannels[1]; - } - - if( !bp->hostOutputChannels[0][0].data ) - { - /* no output was supplied (see PaUtil_SetNoOutput) - NonAdaptingProcess knows how to deal with this - */ - noOutputOutputFrameCount = framesToGo; - hostOutputFrameCount = &noOutputOutputFrameCount; - hostOutputChannels = 0; - } - if( bp->hostOutputFrameCount[0] != 0 ) - { - hostOutputFrameCount = &bp->hostOutputFrameCount[0]; - hostOutputChannels = bp->hostOutputChannels[0]; - } - else - { - hostOutputFrameCount = &bp->hostOutputFrameCount[1]; - hostOutputChannels = bp->hostOutputChannels[1]; - } - - framesToProcess = PA_MIN_( *hostInputFrameCount, - *hostOutputFrameCount ); - - assert( framesToProcess != 0 ); - - framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult, - hostInputChannels, hostOutputChannels, - framesToProcess ); - - *hostInputFrameCount -= framesProcessedThisIteration; - *hostOutputFrameCount -= framesProcessedThisIteration; - - framesProcessed += framesProcessedThisIteration; - framesToGo -= framesProcessedThisIteration; - - }while( framesToGo > 0 ); - } - else - { - /* half duplex non-adapting process, just process 1st and 2nd buffer */ - /* process first buffer */ - - framesToProcess = (bp->inputChannelCount != 0) - ? bp->hostInputFrameCount[0] - : bp->hostOutputFrameCount[0]; - - framesProcessed = NonAdaptingProcess( bp, streamCallbackResult, - bp->hostInputChannels[0], bp->hostOutputChannels[0], - framesToProcess ); - - /* process second buffer if provided */ - - framesToProcess = (bp->inputChannelCount != 0) - ? bp->hostInputFrameCount[1] - : bp->hostOutputFrameCount[1]; - if( framesToProcess > 0 ) - { - framesProcessed += NonAdaptingProcess( bp, streamCallbackResult, - bp->hostInputChannels[1], bp->hostOutputChannels[1], - framesToProcess ); - } - } - } - else /* block adaption necessary*/ - { - - if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) - { - /* full duplex */ - - if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed ) - { - framesProcessed = AdaptingProcess( bp, streamCallbackResult, - 0 /* dont process partial user buffers */ ); - } - else - { - framesProcessed = AdaptingProcess( bp, streamCallbackResult, - 1 /* process partial user buffers */ ); - } - } - else if( bp->inputChannelCount != 0 ) - { - /* input only */ - framesToProcess = bp->hostInputFrameCount[0]; - - framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult, - bp->hostInputChannels[0], framesToProcess ); - - framesToProcess = bp->hostInputFrameCount[1]; - if( framesToProcess > 0 ) - { - framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult, - bp->hostInputChannels[1], framesToProcess ); - } - } - else - { - /* output only */ - framesToProcess = bp->hostOutputFrameCount[0]; - - framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult, - bp->hostOutputChannels[0], framesToProcess ); - - framesToProcess = bp->hostOutputFrameCount[1]; - if( framesToProcess > 0 ) - { - framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult, - bp->hostOutputChannels[1], framesToProcess ); - } - } - } - - return framesProcessed; -} - - -int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp ) -{ - return (bp->framesInTempOutputBuffer) ? 0 : 1; -} - - -unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp, - void **buffer, unsigned long frameCount ) -{ - PaUtilChannelDescriptor *hostInputChannels; - unsigned int framesToCopy; - unsigned char *destBytePtr; - void **nonInterleavedDestPtrs; - unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - - hostInputChannels = bp->hostInputChannels[0]; - framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount ); - - if( bp->userInputIsInterleaved ) - { - destBytePtr = (unsigned char*)*buffer; - - destSampleStrideSamples = bp->inputChannelCount; - destChannelStrideBytes = bp->bytesPerUserInputSample; - - for( i=0; iinputChannelCount; ++i ) - { - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - framesToCopy, &bp->ditherGenerator ); - - destBytePtr += destChannelStrideBytes; /* skip to next source channel */ - - /* advance dest ptr for next iteration */ - hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + - framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; - } - - /* advance callers dest pointer (buffer) */ - *buffer = ((unsigned char *)*buffer) + - framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample; - } - else - { - /* user input is not interleaved */ - - nonInterleavedDestPtrs = (void**)*buffer; - - destSampleStrideSamples = 1; - - for( i=0; iinputChannelCount; ++i ) - { - destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i]; - - bp->inputConverter( destBytePtr, destSampleStrideSamples, - hostInputChannels[i].data, - hostInputChannels[i].stride, - framesToCopy, &bp->ditherGenerator ); - - /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */ - destBytePtr += bp->bytesPerUserInputSample * framesToCopy; - nonInterleavedDestPtrs[i] = destBytePtr; - - /* advance dest ptr for next iteration */ - hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + - framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; - } - } - - bp->hostInputFrameCount[0] -= framesToCopy; - - return framesToCopy; -} - -unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp, - const void ** buffer, unsigned long frameCount ) -{ - PaUtilChannelDescriptor *hostOutputChannels; - unsigned int framesToCopy; - unsigned char *srcBytePtr; - void **nonInterleavedSrcPtrs; - unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ - unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ - unsigned int i; - - hostOutputChannels = bp->hostOutputChannels[0]; - framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); - - if( bp->userOutputIsInterleaved ) - { - srcBytePtr = (unsigned char*)*buffer; - - srcSampleStrideSamples = bp->outputChannelCount; - srcChannelStrideBytes = bp->bytesPerUserOutputSample; - - for( i=0; ioutputChannelCount; ++i ) - { - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - framesToCopy, &bp->ditherGenerator ); - - srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - - /* advance callers source pointer (buffer) */ - *buffer = ((unsigned char *)*buffer) + - framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample; - - } - else - { - /* user output is not interleaved */ - - nonInterleavedSrcPtrs = (void**)*buffer; - - srcSampleStrideSamples = 1; - - for( i=0; ioutputChannelCount; ++i ) - { - srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i]; - - bp->outputConverter( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - srcBytePtr, srcSampleStrideSamples, - framesToCopy, &bp->ditherGenerator ); - - - /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */ - srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy; - nonInterleavedSrcPtrs[i] = srcBytePtr; - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - } - - bp->hostOutputFrameCount[0] += framesToCopy; - - return framesToCopy; -} - - -unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount ) -{ - PaUtilChannelDescriptor *hostOutputChannels; - unsigned int framesToZero; - unsigned int i; - - hostOutputChannels = bp->hostOutputChannels[0]; - framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); - - for( i=0; ioutputChannelCount; ++i ) - { - bp->outputZeroer( hostOutputChannels[i].data, - hostOutputChannels[i].stride, - framesToZero ); - - - /* advance dest ptr for next iteration */ - hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + - framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; - } - - bp->hostOutputFrameCount[0] += framesToZero; - - return framesToZero; -} +/* + * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $ + * Portable Audio I/O Library + * streamCallback <-> host buffer processing adapter + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Buffer Processor implementation. + + The code in this file is not optimised yet - although it's not clear that + it needs to be. there may appear to be redundancies + that could be factored into common functions, but the redundanceis are left + intentionally as each appearance may have different optimisation possibilities. + + The optimisations which are planned involve only converting data in-place + where possible, rather than copying to the temp buffer(s). + + Note that in the extreme case of being able to convert in-place, and there + being no conversion necessary there should be some code which short-circuits + the operation. + + @todo Consider cache tilings for intereave<->deinterleave. + + @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing() + + @todo specify and implement some kind of logical policy for handling the + underflow and overflow stream flags when the underflow/overflow overlaps + multiple user buffers/callbacks. + + @todo provide support for priming the buffers with data from the callback. + The client interface is now implemented through PaUtil_SetNoInput() + which sets bp->hostInputChannels[0][0].data to zero. However this is + currently only implemented in NonAdaptingProcess(). It shouldn't be + needed for AdaptingInputOnlyProcess() (no priming should ever be + requested for AdaptingInputOnlyProcess()). + Not sure if additional work should be required to make it work with + AdaptingOutputOnlyProcess, but it definitely is required for + AdaptingProcess. + + @todo implement PaUtil_SetNoOutput for AdaptingProcess + + @todo don't allocate temp buffers for blocking streams unless they are + needed. At the moment they are needed, but perhaps for host APIs + where the implementation passes a buffer to the host they could be + used. +*/ + + +#include +#include /* memset() */ + +#include "pa_process.h" +#include "pa_util.h" + + +#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024 + +#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) ) + + +/* greatest common divisor - PGCD in French */ +static unsigned long GCD( unsigned long a, unsigned long b ) +{ + return (b==0) ? a : GCD( b, a%b); +} + +/* least common multiple - PPCM in French */ +static unsigned long LCM( unsigned long a, unsigned long b ) +{ + return (a*b) / GCD(a,b); +} + +#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b)) + +static unsigned long CalculateFrameShift( unsigned long M, unsigned long N ) +{ + unsigned long result = 0; + unsigned long i; + unsigned long lcm; + + assert( M > 0 ); + assert( N > 0 ); + + lcm = LCM( M, N ); + for( i = M; i < lcm; i += M ) + result = PA_MAX_( result, i % N ); + + return result; +} + + +PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp, + int inputChannelCount, PaSampleFormat userInputSampleFormat, + PaSampleFormat hostInputSampleFormat, + int outputChannelCount, PaSampleFormat userOutputSampleFormat, + PaSampleFormat hostOutputSampleFormat, + double sampleRate, + PaStreamFlags streamFlags, + unsigned long framesPerUserBuffer, + unsigned long framesPerHostBuffer, + PaUtilHostBufferSizeMode hostBufferSizeMode, + PaStreamCallback *streamCallback, void *userData ) +{ + PaError result = paNoError; + PaError bytesPerSample; + unsigned long tempInputBufferSize, tempOutputBufferSize; + + /* initialize buffer ptrs to zero so they can be freed if necessary in error */ + bp->tempInputBuffer = 0; + bp->tempInputBufferPtrs = 0; + bp->tempOutputBuffer = 0; + bp->tempOutputBufferPtrs = 0; + + bp->framesPerUserBuffer = framesPerUserBuffer; + bp->framesPerHostBuffer = framesPerHostBuffer; + + bp->inputChannelCount = inputChannelCount; + bp->outputChannelCount = outputChannelCount; + + bp->hostBufferSizeMode = hostBufferSizeMode; + + bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0; + bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0; + + if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */ + { + bp->useNonAdaptingProcess = 1; + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = 0; + + if( hostBufferSizeMode == paUtilFixedHostBufferSize + || hostBufferSizeMode == paUtilBoundedHostBufferSize ) + { + bp->framesPerTempBuffer = framesPerHostBuffer; + } + else /* unknown host buffer size */ + { + bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_; + } + } + else + { + bp->framesPerTempBuffer = framesPerUserBuffer; + + if( hostBufferSizeMode == paUtilFixedHostBufferSize + && framesPerHostBuffer % framesPerUserBuffer == 0 ) + { + bp->useNonAdaptingProcess = 1; + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = 0; + } + else + { + bp->useNonAdaptingProcess = 0; + + if( inputChannelCount > 0 && outputChannelCount > 0 ) + { + /* full duplex */ + if( hostBufferSizeMode == paUtilFixedHostBufferSize ) + { + unsigned long frameShift = + CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer ); + + if( framesPerUserBuffer > framesPerHostBuffer ) + { + bp->initialFramesInTempInputBuffer = frameShift; + bp->initialFramesInTempOutputBuffer = 0; + } + else + { + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = frameShift; + } + } + else /* variable host buffer size, add framesPerUserBuffer latency */ + { + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = framesPerUserBuffer; + } + } + else + { + /* half duplex */ + bp->initialFramesInTempInputBuffer = 0; + bp->initialFramesInTempOutputBuffer = 0; + } + } + } + + + bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; + bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; + + + if( inputChannelCount > 0 ) + { + bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerHostInputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bytesPerSample = Pa_GetSampleSize( userInputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerUserInputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bp->inputConverter = + PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags ); + + bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat ); + + bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1; + + + tempInputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount; + + bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize ); + if( bp->tempInputBuffer == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + if( bp->framesInTempInputBuffer > 0 ) + memset( bp->tempInputBuffer, 0, tempInputBufferSize ); + + if( userInputSampleFormat & paNonInterleaved ) + { + bp->tempInputBufferPtrs = + (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount ); + if( bp->tempInputBufferPtrs == 0 ) + { + result = paInsufficientMemory; + goto error; + } + } + + bp->hostInputChannels[0] = (PaUtilChannelDescriptor*) + PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2); + if( bp->hostInputChannels[0] == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount]; + } + + if( outputChannelCount > 0 ) + { + bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerHostOutputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerUserOutputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bp->outputConverter = + PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags ); + + bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat ); + + bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1; + + tempOutputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount; + + bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize ); + if( bp->tempOutputBuffer == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + if( bp->framesInTempOutputBuffer > 0 ) + memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); + + if( userOutputSampleFormat & paNonInterleaved ) + { + bp->tempOutputBufferPtrs = + (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount ); + if( bp->tempOutputBufferPtrs == 0 ) + { + result = paInsufficientMemory; + goto error; + } + } + + bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*) + PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 ); + if( bp->hostOutputChannels[0] == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount]; + } + + PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator ); + + bp->samplePeriod = 1. / sampleRate; + + bp->streamCallback = streamCallback; + bp->userData = userData; + + return result; + +error: + if( bp->tempInputBuffer ) + PaUtil_FreeMemory( bp->tempInputBuffer ); + + if( bp->tempInputBufferPtrs ) + PaUtil_FreeMemory( bp->tempInputBufferPtrs ); + + if( bp->hostInputChannels[0] ) + PaUtil_FreeMemory( bp->hostInputChannels[0] ); + + if( bp->tempOutputBuffer ) + PaUtil_FreeMemory( bp->tempOutputBuffer ); + + if( bp->tempOutputBufferPtrs ) + PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); + + if( bp->hostOutputChannels[0] ) + PaUtil_FreeMemory( bp->hostOutputChannels[0] ); + + return result; +} + + +void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp ) +{ + if( bp->tempInputBuffer ) + PaUtil_FreeMemory( bp->tempInputBuffer ); + + if( bp->tempInputBufferPtrs ) + PaUtil_FreeMemory( bp->tempInputBufferPtrs ); + + if( bp->hostInputChannels[0] ) + PaUtil_FreeMemory( bp->hostInputChannels[0] ); + + if( bp->tempOutputBuffer ) + PaUtil_FreeMemory( bp->tempOutputBuffer ); + + if( bp->tempOutputBufferPtrs ) + PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); + + if( bp->hostOutputChannels[0] ) + PaUtil_FreeMemory( bp->hostOutputChannels[0] ); +} + + +void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp ) +{ + unsigned long tempInputBufferSize, tempOutputBufferSize; + + bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer; + bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer; + + if( bp->framesInTempInputBuffer > 0 ) + { + tempInputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount; + memset( bp->tempInputBuffer, 0, tempInputBufferSize ); + } + + if( bp->framesInTempOutputBuffer > 0 ) + { + tempOutputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount; + memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); + } +} + + +unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp ) +{ + return bp->initialFramesInTempInputBuffer; +} + + +unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp ) +{ + return bp->initialFramesInTempOutputBuffer; +} + + +void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + if( frameCount == 0 ) + bp->hostInputFrameCount[0] = bp->framesPerHostBuffer; + else + bp->hostInputFrameCount[0] = frameCount; +} + + +void PaUtil_SetNoInput( PaUtilBufferProcessor* bp ) +{ + assert( bp->inputChannelCount > 0 ); + + bp->hostInputChannels[0][0].data = 0; +} + + +void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[0][channel].data = data; + bp->hostInputChannels[0][channel].stride = stride; +} + + +void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->inputChannelCount; + + assert( firstChannel < bp->inputChannelCount ); + assert( firstChannel + channelCount <= bp->inputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostInputChannels[0][channel+i].data = p; + p += bp->bytesPerHostInputSample; + bp->hostInputChannels[0][channel+i].stride = channelCount; + } +} + + +void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[0][channel].data = data; + bp->hostInputChannels[0][channel].stride = 1; +} + + +void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + bp->hostInputFrameCount[1] = frameCount; +} + + +void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[1][channel].data = data; + bp->hostInputChannels[1][channel].stride = stride; +} + + +void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->inputChannelCount; + + assert( firstChannel < bp->inputChannelCount ); + assert( firstChannel + channelCount <= bp->inputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostInputChannels[1][channel+i].data = p; + p += bp->bytesPerHostInputSample; + bp->hostInputChannels[1][channel+i].stride = channelCount; + } +} + + +void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[1][channel].data = data; + bp->hostInputChannels[1][channel].stride = 1; +} + + +void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + if( frameCount == 0 ) + bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer; + else + bp->hostOutputFrameCount[0] = frameCount; +} + + +void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp ) +{ + assert( bp->outputChannelCount > 0 ); + + bp->hostOutputChannels[0][0].data = 0; +} + + +void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[0][channel].data = data; + bp->hostOutputChannels[0][channel].stride = stride; +} + + +void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->outputChannelCount; + + assert( firstChannel < bp->outputChannelCount ); + assert( firstChannel + channelCount <= bp->outputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostOutputChannels[0][channel+i].data = p; + p += bp->bytesPerHostOutputSample; + bp->hostOutputChannels[0][channel+i].stride = channelCount; + } +} + + +void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[0][channel].data = data; + bp->hostOutputChannels[0][channel].stride = 1; +} + + +void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + bp->hostOutputFrameCount[1] = frameCount; +} + + +void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[1][channel].data = data; + bp->hostOutputChannels[1][channel].stride = stride; +} + + +void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->outputChannelCount; + + assert( firstChannel < bp->outputChannelCount ); + assert( firstChannel + channelCount <= bp->outputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostOutputChannels[1][channel+i].data = p; + p += bp->bytesPerHostOutputSample; + bp->hostOutputChannels[1][channel+i].stride = channelCount; + } +} + + +void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[1][channel].data = data; + bp->hostOutputChannels[1][channel].stride = 1; +} + + +void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp, + PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags ) +{ + bp->timeInfo = timeInfo; + + /* the first streamCallback will be called to process samples which are + currently in the input buffer before the ones starting at the timeInfo time */ + + bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod; + + bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */ + + /* the first streamCallback will be called to generate samples which will be + outputted after the frames currently in the output buffer have been + outputted. */ + bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod; + + bp->callbackStatusFlags = callbackStatusFlags; + + bp->hostInputFrameCount[1] = 0; + bp->hostOutputFrameCount[1] = 0; +} + + +/* + NonAdaptingProcess() is a simple buffer copying adaptor that can handle + both full and half duplex copies. It processes framesToProcess frames, + broken into blocks bp->framesPerTempBuffer long. + This routine can be used when the streamCallback doesn't care what length + the buffers are, or when framesToProcess is an integer multiple of + bp->framesPerTempBuffer, in which case streamCallback will always be called + with bp->framesPerTempBuffer samples. +*/ +static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *srcBytePtr, *destBytePtr; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + + + if( *streamCallbackResult == paContinue ) + { + do + { + frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo ); + + /* configure user input buffer and convert input data (host -> user) */ + if( bp->inputChannelCount == 0 ) + { + /* no input */ + userInput = 0; + } + else /* there are input channels */ + { + /* + could use more elaborate logic here and sometimes process + buffers in-place. + */ + + destBytePtr = (unsigned char *)bp->tempInputBuffer; + + if( bp->userInputIsInterleaved ) + { + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + destSampleStrideSamples = 1; + destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample; + + /* setup non-interleaved ptrs */ + for( i=0; iinputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->bytesPerUserInputSample * frameCount; + } + + userInput = bp->tempInputBufferPtrs; + } + + if( !bp->hostInputChannels[0][0].data ) + { + /* no input was supplied (see PaUtil_SetNoInput), so + zero the input buffer */ + + for( i=0; iinputChannelCount; ++i ) + { + bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount ); + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + } + } + else + { + for( i=0; iinputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + } + } + + /* configure user output buffer */ + if( bp->outputChannelCount == 0 ) + { + /* no output */ + userOutput = 0; + } + else /* there are output channels */ + { + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->bytesPerUserOutputSample * frameCount; + } + + userOutput = bp->tempOutputBufferPtrs; + } + } + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData ); + + if( *streamCallbackResult == paAbort ) + { + /* callback returned paAbort, don't advance framesProcessed + and framesToGo, they will be handled below */ + } + else + { + bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; + bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod; + + /* convert output data (user -> host) */ + + if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) + { + /* + could use more elaborate logic here and sometimes process + buffers in-place. + */ + + srcBytePtr = (unsigned char *)bp->tempOutputBuffer; + + if( bp->userOutputIsInterleaved ) + { + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcSampleStrideSamples = 1; + srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample; + } + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + } + } + while( framesToGo > 0 && *streamCallbackResult == paContinue ); + } + + if( framesToGo > 0 ) + { + /* zero any remaining frames output. There will only be remaining frames + if the callback has returned paComplete or paAbort */ + + frameCount = framesToGo; + + if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data ) + { + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputZeroer( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + frameCount ); + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + framesProcessed += frameCount; + } + + return framesProcessed; +} + + +/* + AdaptingInputOnlyProcess() is a half duplex input buffer processor. It + converts data from the input buffers into the temporary input buffer, + when the temporary input buffer is full, it calls the streamCallback. +*/ +static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *destBytePtr; + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + + userOutput = 0; + + do + { + frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer ) + ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer ) + : framesToGo; + + /* convert frameCount samples into temp buffer */ + + if( bp->userInputIsInterleaved ) + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->inputChannelCount * + bp->framesInTempInputBuffer; + + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; + + destSampleStrideSamples = 1; + destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + + /* setup non-interleaved ptrs */ + for( i=0; iinputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer; + } + + userInput = bp->tempInputBufferPtrs; + } + + for( i=0; iinputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + bp->framesInTempInputBuffer += frameCount; + + if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer ) + { + /** + @todo (non-critical optimisation) + The conditional below implements the continue/complete/abort mechanism + simply by continuing on iterating through the input buffer, but not + passing the data to the callback. With care, the outer loop could be + terminated earlier, thus some unneeded conversion cycles would be + saved. + */ + if( *streamCallbackResult == paContinue ) + { + bp->timeInfo->outputBufferDacTime = 0; + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + bp->callbackStatusFlags, bp->userData ); + + bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; + } + + bp->framesInTempInputBuffer = 0; + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + }while( framesToGo > 0 ); + + return framesProcessed; +} + + +/* + AdaptingOutputOnlyProcess() is a half duplex output buffer processor. + It converts data from the temporary output buffer, to the output buffers, + when the temporary output buffer is empty, it calls the streamCallback. +*/ +static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *srcBytePtr; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + + do + { + if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue ) + { + userInput = 0; + + /* setup userOutput */ + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + userOutput = bp->tempOutputBufferPtrs; + } + + bp->timeInfo->inputBufferAdcTime = 0; + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + bp->callbackStatusFlags, bp->userData ); + + if( *streamCallbackResult == paAbort ) + { + /* if the callback returned paAbort, we disregard its output */ + } + else + { + bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; + + bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; + } + } + + if( bp->framesInTempOutputBuffer > 0 ) + { + /* convert frameCount frames from user buffer to host buffer */ + + frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo ); + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * bp->outputChannelCount * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = 1; + srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + bp->framesInTempOutputBuffer -= frameCount; + } + else + { + /* no more user data is available because the callback has returned + paComplete or paAbort. Fill the remainder of the host buffer + with zeros. + */ + + frameCount = framesToGo; + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputZeroer( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + frameCount ); + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + + }while( framesToGo > 0 ); + + return framesProcessed; +} + +/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from + tempOutputBuffer to hostOutputChannels. This includes data conversion + and interleaving. +*/ +static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp) +{ + unsigned long maxFramesToCopy; + PaUtilChannelDescriptor *hostOutputChannels; + unsigned int frameCount; + unsigned char *srcBytePtr; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + + /* copy frames from user to host output buffers */ + while( bp->framesInTempOutputBuffer > 0 && + ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) ) + { + maxFramesToCopy = bp->framesInTempOutputBuffer; + + /* select the output buffer set (1st or 2nd) */ + if( bp->hostOutputFrameCount[0] > 0 ) + { + hostOutputChannels = bp->hostOutputChannels[0]; + frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy ); + } + else + { + hostOutputChannels = bp->hostOutputChannels[1]; + frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy ); + } + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * bp->outputChannelCount * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcSampleStrideSamples = 1; + srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + if( bp->hostOutputFrameCount[0] > 0 ) + bp->hostOutputFrameCount[0] -= frameCount; + else + bp->hostOutputFrameCount[1] -= frameCount; + + bp->framesInTempOutputBuffer -= frameCount; + } +} + +/* + AdaptingProcess is a full duplex adapting buffer processor. It converts + data from the temporary output buffer into the host output buffers, then + from the host input buffers into the temporary input buffers. Calling the + streamCallback when necessary. + When processPartialUserBuffers is 0, all available input data will be + consumed and all available output space will be filled. When + processPartialUserBuffers is non-zero, as many full user buffers + as possible will be processed, but partial buffers will not be consumed. +*/ +static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, int processPartialUserBuffers ) +{ + void *userInput, *userOutput; + unsigned long framesProcessed = 0; + unsigned long framesAvailable; + unsigned long endProcessingMinFrameCount; + unsigned long maxFramesToCopy; + PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels; + unsigned int frameCount; + unsigned char *destBytePtr; + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i, j; + + + framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */ + + if( processPartialUserBuffers ) + endProcessingMinFrameCount = 0; + else + endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1); + + /* Fill host output with remaining frames in user output (tempOutputBuffer) */ + CopyTempOutputBuffersToHostOutputBuffers( bp ); + + while( framesAvailable > endProcessingMinFrameCount ) + { + + if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue ) + { + /* the callback will not be called any more, so zero what remains + of the host output buffers */ + + for( i=0; i<2; ++i ) + { + frameCount = bp->hostOutputFrameCount[i]; + if( frameCount > 0 ) + { + hostOutputChannels = bp->hostOutputChannels[i]; + + for( j=0; joutputChannelCount; ++j ) + { + bp->outputZeroer( hostOutputChannels[j].data, + hostOutputChannels[j].stride, + frameCount ); + + /* advance dest ptr for next iteration */ + hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) + + frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample; + } + bp->hostOutputFrameCount[i] = 0; + } + } + } + + + /* copy frames from host to user input buffers */ + while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer && + ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) ) + { + maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer; + + /* select the input buffer set (1st or 2nd) */ + if( bp->hostInputFrameCount[0] > 0 ) + { + hostInputChannels = bp->hostInputChannels[0]; + frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy ); + } + else + { + hostInputChannels = bp->hostInputChannels[1]; + frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy ); + } + + /* configure conversion destination pointers */ + if( bp->userInputIsInterleaved ) + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->inputChannelCount * + bp->framesInTempInputBuffer; + + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + } + else /* user input is not interleaved */ + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; + + destSampleStrideSamples = 1; + destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + } + + for( i=0; iinputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + if( bp->hostInputFrameCount[0] > 0 ) + bp->hostInputFrameCount[0] -= frameCount; + else + bp->hostInputFrameCount[1] -= frameCount; + + bp->framesInTempInputBuffer += frameCount; + + /* update framesAvailable and framesProcessed based on input consumed + unless something is very wrong this will also correspond to the + amount of output generated */ + framesAvailable -= frameCount; + framesProcessed += frameCount; + } + + /* call streamCallback */ + if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer && + bp->framesInTempOutputBuffer == 0 ) + { + if( *streamCallbackResult == paContinue ) + { + /* setup userInput */ + if( bp->userInputIsInterleaved ) + { + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + for( i = 0; i < bp->inputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + } + + userInput = bp->tempInputBufferPtrs; + } + + /* setup userOutput */ + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + userOutput = bp->tempOutputBufferPtrs; + } + + /* call streamCallback */ + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + bp->callbackStatusFlags, bp->userData ); + + bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod; + bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; + + bp->framesInTempInputBuffer = 0; + + if( *streamCallbackResult == paAbort ) + bp->framesInTempOutputBuffer = 0; + else + bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; + } + else + { + /* paComplete or paAbort has already been called. */ + + bp->framesInTempInputBuffer = 0; + } + } + + /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels) + Means to process the user output provided by the callback. Has to be called after + each callback. */ + CopyTempOutputBuffersToHostOutputBuffers( bp ); + + } + + return framesProcessed; +} + + +unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult ) +{ + unsigned long framesToProcess, framesToGo; + unsigned long framesProcessed = 0; + + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 + && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */ + && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ ) + { + assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) == + (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) ); + } + + assert( *streamCallbackResult == paContinue + || *streamCallbackResult == paComplete + || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */ + + if( bp->useNonAdaptingProcess ) + { + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) + { + /* full duplex non-adapting process, splice buffers if they are + different lengths */ + + framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */ + + do{ + unsigned long noInputInputFrameCount; + unsigned long *hostInputFrameCount; + PaUtilChannelDescriptor *hostInputChannels; + unsigned long noOutputOutputFrameCount; + unsigned long *hostOutputFrameCount; + PaUtilChannelDescriptor *hostOutputChannels; + unsigned long framesProcessedThisIteration; + + if( !bp->hostInputChannels[0][0].data ) + { + /* no input was supplied (see PaUtil_SetNoInput) + NonAdaptingProcess knows how to deal with this + */ + noInputInputFrameCount = framesToGo; + hostInputFrameCount = &noInputInputFrameCount; + hostInputChannels = 0; + } + else if( bp->hostInputFrameCount[0] != 0 ) + { + hostInputFrameCount = &bp->hostInputFrameCount[0]; + hostInputChannels = bp->hostInputChannels[0]; + } + else + { + hostInputFrameCount = &bp->hostInputFrameCount[1]; + hostInputChannels = bp->hostInputChannels[1]; + } + + if( !bp->hostOutputChannels[0][0].data ) + { + /* no output was supplied (see PaUtil_SetNoOutput) + NonAdaptingProcess knows how to deal with this + */ + noOutputOutputFrameCount = framesToGo; + hostOutputFrameCount = &noOutputOutputFrameCount; + hostOutputChannels = 0; + } + if( bp->hostOutputFrameCount[0] != 0 ) + { + hostOutputFrameCount = &bp->hostOutputFrameCount[0]; + hostOutputChannels = bp->hostOutputChannels[0]; + } + else + { + hostOutputFrameCount = &bp->hostOutputFrameCount[1]; + hostOutputChannels = bp->hostOutputChannels[1]; + } + + framesToProcess = PA_MIN_( *hostInputFrameCount, + *hostOutputFrameCount ); + + assert( framesToProcess != 0 ); + + framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult, + hostInputChannels, hostOutputChannels, + framesToProcess ); + + *hostInputFrameCount -= framesProcessedThisIteration; + *hostOutputFrameCount -= framesProcessedThisIteration; + + framesProcessed += framesProcessedThisIteration; + framesToGo -= framesProcessedThisIteration; + + }while( framesToGo > 0 ); + } + else + { + /* half duplex non-adapting process, just process 1st and 2nd buffer */ + /* process first buffer */ + + framesToProcess = (bp->inputChannelCount != 0) + ? bp->hostInputFrameCount[0] + : bp->hostOutputFrameCount[0]; + + framesProcessed = NonAdaptingProcess( bp, streamCallbackResult, + bp->hostInputChannels[0], bp->hostOutputChannels[0], + framesToProcess ); + + /* process second buffer if provided */ + + framesToProcess = (bp->inputChannelCount != 0) + ? bp->hostInputFrameCount[1] + : bp->hostOutputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += NonAdaptingProcess( bp, streamCallbackResult, + bp->hostInputChannels[1], bp->hostOutputChannels[1], + framesToProcess ); + } + } + } + else /* block adaption necessary*/ + { + + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) + { + /* full duplex */ + + if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed ) + { + framesProcessed = AdaptingProcess( bp, streamCallbackResult, + 0 /* dont process partial user buffers */ ); + } + else + { + framesProcessed = AdaptingProcess( bp, streamCallbackResult, + 1 /* process partial user buffers */ ); + } + } + else if( bp->inputChannelCount != 0 ) + { + /* input only */ + framesToProcess = bp->hostInputFrameCount[0]; + + framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult, + bp->hostInputChannels[0], framesToProcess ); + + framesToProcess = bp->hostInputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult, + bp->hostInputChannels[1], framesToProcess ); + } + } + else + { + /* output only */ + framesToProcess = bp->hostOutputFrameCount[0]; + + framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult, + bp->hostOutputChannels[0], framesToProcess ); + + framesToProcess = bp->hostOutputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult, + bp->hostOutputChannels[1], framesToProcess ); + } + } + } + + return framesProcessed; +} + + +int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp ) +{ + return (bp->framesInTempOutputBuffer) ? 0 : 1; +} + + +unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp, + void **buffer, unsigned long frameCount ) +{ + PaUtilChannelDescriptor *hostInputChannels; + unsigned int framesToCopy; + unsigned char *destBytePtr; + void **nonInterleavedDestPtrs; + unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + + hostInputChannels = bp->hostInputChannels[0]; + framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount ); + + if( bp->userInputIsInterleaved ) + { + destBytePtr = (unsigned char*)*buffer; + + destSampleStrideSamples = bp->inputChannelCount; + destChannelStrideBytes = bp->bytesPerUserInputSample; + + for( i=0; iinputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + framesToCopy, &bp->ditherGenerator ); + + destBytePtr += destChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + /* advance callers dest pointer (buffer) */ + *buffer = ((unsigned char *)*buffer) + + framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample; + } + else + { + /* user input is not interleaved */ + + nonInterleavedDestPtrs = (void**)*buffer; + + destSampleStrideSamples = 1; + + for( i=0; iinputChannelCount; ++i ) + { + destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i]; + + bp->inputConverter( destBytePtr, destSampleStrideSamples, + hostInputChannels[i].data, + hostInputChannels[i].stride, + framesToCopy, &bp->ditherGenerator ); + + /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */ + destBytePtr += bp->bytesPerUserInputSample * framesToCopy; + nonInterleavedDestPtrs[i] = destBytePtr; + + /* advance dest ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + } + + bp->hostInputFrameCount[0] -= framesToCopy; + + return framesToCopy; +} + +unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp, + const void ** buffer, unsigned long frameCount ) +{ + PaUtilChannelDescriptor *hostOutputChannels; + unsigned int framesToCopy; + unsigned char *srcBytePtr; + void **nonInterleavedSrcPtrs; + unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */ + unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */ + unsigned int i; + + hostOutputChannels = bp->hostOutputChannels[0]; + framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = (unsigned char*)*buffer; + + srcSampleStrideSamples = bp->outputChannelCount; + srcChannelStrideBytes = bp->bytesPerUserOutputSample; + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + framesToCopy, &bp->ditherGenerator ); + + srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + /* advance callers source pointer (buffer) */ + *buffer = ((unsigned char *)*buffer) + + framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample; + + } + else + { + /* user output is not interleaved */ + + nonInterleavedSrcPtrs = (void**)*buffer; + + srcSampleStrideSamples = 1; + + for( i=0; ioutputChannelCount; ++i ) + { + srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i]; + + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcSampleStrideSamples, + framesToCopy, &bp->ditherGenerator ); + + + /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */ + srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy; + nonInterleavedSrcPtrs[i] = srcBytePtr; + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + bp->hostOutputFrameCount[0] += framesToCopy; + + return framesToCopy; +} + + +unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount ) +{ + PaUtilChannelDescriptor *hostOutputChannels; + unsigned int framesToZero; + unsigned int i; + + hostOutputChannels = bp->hostOutputChannels[0]; + framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount ); + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputZeroer( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + framesToZero ); + + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + bp->hostOutputFrameCount[0] += framesToZero; + + return framesToZero; +} diff --git a/pjmedia/src/pjmedia/portaudio/pa_process.h b/pjmedia/src/pjmedia/portaudio/pa_process.h index 8ebfbadab..c52e9ea0a 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_process.h +++ b/pjmedia/src/pjmedia/portaudio/pa_process.h @@ -1,741 +1,741 @@ -#ifndef PA_PROCESS_H -#define PA_PROCESS_H -/* - * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 rossbencina Exp $ - * Portable Audio I/O Library callback buffer processing adapters - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Phil Burk, Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Buffer Processor prototypes. A Buffer Processor performs buffer length - adaption, coordinates sample format conversion, and interleaves/deinterleaves - channels. - -

Overview

- - The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio - data from host buffers to user buffers and back again. Where required, the - buffer processor takes care of converting between host and user sample formats, - interleaving and deinterleaving multichannel buffers, and adapting between host - and user buffers with different lengths. The buffer processor may be used with - full and half duplex streams, for both callback streams and blocking read/write - streams. - - One of the important capabilities provided by the buffer processor is - the ability to adapt between user and host buffer sizes of different lengths - with minimum latency. Although this task is relatively easy to perform when - the host buffer size is an integer multiple of the user buffer size, the - problem is more complicated when this is not the case - especially for - full-duplex callback streams. Where necessary the adaption is implemented by - internally buffering some input and/or output data. The buffer adation - algorithm used by the buffer processor was originally implemented by - Stephan Letz for the ASIO version of PortAudio, and is described in his - Callback_adaption_.pdf which is included in the distribution. - - The buffer processor performs sample conversion using the functions provided - by pa_converters.c. - - The following sections provide an overview of how to use the buffer processor. - Interested readers are advised to consult the host API implementations for - examples of buffer processor usage. - - -

Initialization, resetting and termination

- - When a stream is opened, the buffer processor should be initialized using - PaUtil_InitializeBufferProcessor. This function initializes internal state - and allocates temporary buffers as neccesary according to the supplied - configuration parameters. Some of the parameters correspond to those requested - by the user in their call to Pa_OpenStream(), others reflect the requirements - of the host API implementation - they indicate host buffer sizes, formats, - and the type of buffering which the Host API uses. The buffer processor should - be initialized for callback streams and blocking read/write streams. - - Call PaUtil_ResetBufferProcessor to clear any sample data which is present - in the buffer processor before starting to use it (for example when - Pa_StartStream is called). - - When the buffer processor is no longer used call - PaUtil_TerminateBufferProcessor. - - -

Using the buffer processor for a callback stream

- - The buffer processor's role in a callback stream is to take host input buffers - process them with the stream callback, and fill host output buffers. For a - full duplex stream, the buffer processor handles input and output simultaneously - due to the requirements of the minimum-latency buffer adation algorithm. - - When a host buffer becomes available, the implementation should call - the buffer processor to process the buffer. The buffer processor calls the - stream callback to consume and/or produce audio data as necessary. The buffer - processor will convert sample formats, interleave/deinterleave channels, - and slice or chunk the data to the appropriate buffer lengths according to - the requirements of the stream callback and the host API. - - To process a host buffer (or a pair of host buffers for a full-duplex stream) - use the following calling sequence: - - -# Call PaUtil_BeginBufferProcessing - -# For a stream which takes input: - - Call PaUtil_SetInputFrameCount with the number of frames in the host input - buffer. - - Call one of the following functions one or more times to tell the - buffer processor about the host input buffer(s): PaUtil_SetInputChannel, - PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - - If the available host data is split accross two buffers (for example a - data range at the end of a circular buffer and another range at the - beginning of the circular buffer), also call - PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel, - PaUtil_Set2ndInterleavedInputChannels, - PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer - processor about the second buffer. - -# For a stream which generates output: - - Call PaUtil_SetOutputFrameCount with the number of frames in the host - output buffer. - - Call one of the following functions one or more times to tell the - buffer processor about the host output buffer(s): PaUtil_SetOutputChannel, - PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - - If the available host output buffer space is split accross two buffers - (for example a data range at the end of a circular buffer and another - range at the beginning of the circular buffer), call - PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel, - PaUtil_Set2ndInterleavedOutputChannels, - PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer - processor about the second buffer. - -# Call PaUtil_EndBufferProcessing, this function performs the actual data - conversion and processing. - - -

Using the buffer processor for a blocking read/write stream

- - Blocking read/write streams use the buffer processor to convert and copy user - output data to a host buffer, and to convert and copy host input data to - the user's buffer. The buffer processor does not perform any buffer adaption. - When using the buffer processor in a blocking read/write stream the input and - output conversion are performed separately by the PaUtil_CopyInput and - PaUtil_CopyOutput functions. - - To copy data from a host input buffer to the buffer(s) which the user supplies - to Pa_ReadStream, use the following calling sequence. - - - Repeat the following three steps until the user buffer(s) have been filled - with samples from the host input buffers: - -# Call PaUtil_SetInputFrameCount with the number of frames in the host - input buffer. - -# Call one of the following functions one or more times to tell the - buffer processor about the host input buffer(s): PaUtil_SetInputChannel, - PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the - array of buffer pointers for a non-interleaved stream) passed to - Pa_ReadStream, along with the number of frames in the user buffer(s). - Be careful to pass a copy of the user buffer pointers to - PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to - the start of the next region to copy. - - PaUtil_CopyInput will not copy more data than is available in the - host buffer(s), so the above steps need to be repeated until the user - buffer(s) are full. - - - To copy data to the host output buffer from the user buffers(s) supplied - to Pa_WriteStream use the following calling sequence. - - - Repeat the following three steps until all frames from the user buffer(s) - have been copied to the host API: - -# Call PaUtil_SetOutputFrameCount with the number of frames in the host - output buffer. - -# Call one of the following functions one or more times to tell the - buffer processor about the host output buffer(s): PaUtil_SetOutputChannel, - PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel. - Which function you call will depend on whether the host buffer(s) are - interleaved or not. - -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the - array of buffer pointers for a non-interleaved stream) passed to - Pa_WriteStream, along with the number of frames in the user buffer(s). - Be careful to pass a copy of the user buffer pointers to - PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to - the start of the next region to copy. - - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s), - so the above steps need to be repeated until all user data is copied. -*/ - - -#include "portaudio.h" -#include "pa_converters.h" -#include "pa_dither.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type - of buffering that the host API uses. - - The mode used depends on whether the host API or the implementation manages - the buffers, and how these buffers are used (scatter gather, circular buffer). -*/ -typedef enum { -/** The host buffer size is a fixed known size. */ - paUtilFixedHostBufferSize, - -/** The host buffer size may vary, but has a known maximum size. */ - paUtilBoundedHostBufferSize, - -/** Nothing is known about the host buffer size. */ - paUtilUnknownHostBufferSize, - -/** The host buffer size varies, and the client does not require the buffer - processor to consume all of the input and fill all of the output buffer. This - is useful when the implementation has access to the host API's circular buffer - and only needs to consume/fill some of it, not necessarily all of it, with each - call to the buffer processor. This is the only mode where - PaUtil_EndBufferProcessing() may not consume the whole buffer. -*/ - paUtilVariableHostBufferSizePartialUsageAllowed -}PaUtilHostBufferSizeMode; - - -/** @brief An auxilliary data structure used internally by the buffer processor - to represent host input and output buffers. */ -typedef struct PaUtilChannelDescriptor{ - void *data; - unsigned int stride; /**< stride in samples, not bytes */ -}PaUtilChannelDescriptor; - - -/** @brief The main buffer processor data structure. - - Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor - and terminate it with PaUtil_TerminateBufferProcessor. -*/ -typedef struct { - unsigned long framesPerUserBuffer; - unsigned long framesPerHostBuffer; - - PaUtilHostBufferSizeMode hostBufferSizeMode; - int useNonAdaptingProcess; - unsigned long framesPerTempBuffer; - - unsigned int inputChannelCount; - unsigned int bytesPerHostInputSample; - unsigned int bytesPerUserInputSample; - int userInputIsInterleaved; - PaUtilConverter *inputConverter; - PaUtilZeroer *inputZeroer; - - unsigned int outputChannelCount; - unsigned int bytesPerHostOutputSample; - unsigned int bytesPerUserOutputSample; - int userOutputIsInterleaved; - PaUtilConverter *outputConverter; - PaUtilZeroer *outputZeroer; - - unsigned long initialFramesInTempInputBuffer; - unsigned long initialFramesInTempOutputBuffer; - - void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */ - void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */ - unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ - - void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */ - void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */ - unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ - - PaStreamCallbackTimeInfo *timeInfo; - - PaStreamCallbackFlags callbackStatusFlags; - - unsigned long hostInputFrameCount[2]; - PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors. - pointers are NULL for half-duplex output processing. - hostInputChannels[i].data is NULL when the caller - calls PaUtil_SetNoInput() - */ - unsigned long hostOutputFrameCount[2]; - PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors. - pointers are NULL for half-duplex input processing. - hostOutputChannels[i].data is NULL when the caller - calls PaUtil_SetNoOutput() - */ - - PaUtilTriangularDitherGenerator ditherGenerator; - - double samplePeriod; - - PaStreamCallback *streamCallback; - void *userData; -} PaUtilBufferProcessor; - - -/** @name Initialization, termination, resetting and info */ -/*@{*/ - -/** Initialize a buffer processor's representation stored in a - PaUtilBufferProcessor structure. Be sure to call - PaUtil_TerminateBufferProcessor after finishing with a buffer processor. - - @param bufferProcessor The buffer processor structure to initialize. - - @param inputChannelCount The number of input channels as passed to - Pa_OpenStream or 0 for an output-only stream. - - @param userInputSampleFormat Format of user input samples, as passed to - Pa_OpenStream. This parameter is ignored for ouput-only streams. - - @param hostInputSampleFormat Format of host input samples. This parameter is - ignored for output-only streams. See note about host buffer interleave below. - - @param outputChannelCount The number of output channels as passed to - Pa_OpenStream or 0 for an input-only stream. - - @param userOutputSampleFormat Format of user output samples, as passed to - Pa_OpenStream. This parameter is ignored for input-only streams. - - @param hostOutputSampleFormat Format of host output samples. This parameter is - ignored for input-only streams. See note about host buffer interleave below. - - @param sampleRate Sample rate of the stream. The more accurate this is the - better - it is used for updating time stamps when adapting buffers. - - @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is - used for selecting special sample conversion options such as clipping and - dithering. - - @param framesPerUserBuffer Number of frames per user buffer, as requested - by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be - zero to indicate that the user will accept any (and varying) buffer sizes. - - @param framesPerHostBuffer Specifies the number of frames per host buffer - for the fixed buffer size mode, and the maximum number of frames - per host buffer for the bounded host buffer size mode. It is ignored for - the other modes. - - @param hostBufferSizeMode A mode flag indicating the size variability of - host buffers that will be passed to the buffer processor. See - PaUtilHostBufferSizeMode for further details. - - @param streamCallback The user stream callback passed to Pa_OpenStream. - - @param userData The user data field passed to Pa_OpenStream. - - @note The interleave flag is ignored for host buffer formats. Host - interleave is determined by the use of different SetInput and SetOutput - functions. - - @return An error code indicating whether the initialization was successful. - If the error code is not PaNoError, the buffer processor was not initialized - and should not be used. - - @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor -*/ -PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor, - int inputChannelCount, PaSampleFormat userInputSampleFormat, - PaSampleFormat hostInputSampleFormat, - int outputChannelCount, PaSampleFormat userOutputSampleFormat, - PaSampleFormat hostOutputSampleFormat, - double sampleRate, - PaStreamFlags streamFlags, - unsigned long framesPerUserBuffer, /* 0 indicates don't care */ - unsigned long framesPerHostBuffer, - PaUtilHostBufferSizeMode hostBufferSizeMode, - PaStreamCallback *streamCallback, void *userData ); - - -/** Terminate a buffer processor's representation. Deallocates any temporary - buffers allocated by PaUtil_InitializeBufferProcessor. - - @param bufferProcessor The buffer processor structure to terminate. - - @see PaUtil_InitializeBufferProcessor. -*/ -void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); - - -/** Clear any internally buffered data. If you call - PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you - call PaUtil_ResetBufferProcessor in your StartStream call. - - @param bufferProcessor The buffer processor to reset. -*/ -void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); - - -/** Retrieve the input latency of a buffer processor. - - @param bufferProcessor The buffer processor examine. - - @return The input latency introduced by the buffer processor, in frames. - - @see PaUtil_GetBufferProcessorOutputLatency -*/ -unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor ); - -/** Retrieve the output latency of a buffer processor. - - @param bufferProcessor The buffer processor examine. - - @return The output latency introduced by the buffer processor, in frames. - - @see PaUtil_GetBufferProcessorInputLatency -*/ -unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor ); - -/*@}*/ - - -/** @name Host buffer pointer configuration - - Functions to set host input and output buffers, used by both callback streams - and blocking read/write streams. -*/ -/*@{*/ - - -/** Set the number of frames in the input host buffer(s) specified by the - PaUtil_Set*InputChannel functions. - - @param bufferProcessor The buffer processor. - - @param frameCount The number of host input frames. A 0 frameCount indicates to - use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor. - - @see PaUtil_SetNoInput, PaUtil_SetInputChannel, - PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel -*/ -void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - - -/** Indicate that no input is avalable. This function should be used when - priming the output of a full-duplex stream opened with the - paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary - to call this or any othe PaUtil_Set*Input* functions for ouput-only streams. - - @param bufferProcessor The buffer processor. -*/ -void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor ); - - -/** Provide the buffer processor with a pointer to a host input channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. - @param stride The stride from one sample to the next, in samples. For - interleaved host buffers, the stride will usually be the same as the number of - channels in the buffer. -*/ -void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - - -/** Provide the buffer processor with a pointer to an number of interleaved - host input channels. - - @param bufferProcessor The buffer processor. - @param firstChannel The first channel number. - @param data The buffer. - @param channelCount The number of interleaved channels in the buffer. If - channelCount is zero, the number of channels specified to - PaUtil_InitializeBufferProcessor will be used. -*/ -void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - - -/** Provide the buffer processor with a pointer to one non-interleaved host - output channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. -*/ -void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetInputFrameCount -*/ -void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetInputChannel -*/ -void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetInterleavedInputChannels -*/ -void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - -/** Use for the second buffer half when the input buffer is split in two halves. - @see PaUtil_SetNonInterleavedInputChannel -*/ -void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - - -/** Set the number of frames in the output host buffer(s) specified by the - PaUtil_Set*OutputChannel functions. - - @param bufferProcessor The buffer processor. - - @param frameCount The number of host output frames. A 0 frameCount indicates to - use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor. - - @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels, - PaUtil_SetNonInterleavedOutputChannel -*/ -void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - - -/** Indicate that the output will be discarded. This function should be used - when implementing the paNeverDropInput mode for full duplex streams. - - @param bufferProcessor The buffer processor. -*/ -void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor ); - - -/** Provide the buffer processor with a pointer to a host output channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. - @param stride The stride from one sample to the next, in samples. For - interleaved host buffers, the stride will usually be the same as the number of - channels in the buffer. -*/ -void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - - -/** Provide the buffer processor with a pointer to a number of interleaved - host output channels. - - @param bufferProcessor The buffer processor. - @param firstChannel The first channel number. - @param data The buffer. - @param channelCount The number of interleaved channels in the buffer. If - channelCount is zero, the number of channels specified to - PaUtil_InitializeBufferProcessor will be used. -*/ -void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - - -/** Provide the buffer processor with a pointer to one non-interleaved host - output channel. - - @param bufferProcessor The buffer processor. - @param channel The channel number. - @param data The buffer. -*/ -void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetOutputFrameCount -*/ -void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetOutputChannel -*/ -void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data, unsigned int stride ); - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetInterleavedOutputChannels -*/ -void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, - unsigned int firstChannel, void *data, unsigned int channelCount ); - -/** Use for the second buffer half when the output buffer is split in two halves. - @see PaUtil_SetNonInterleavedOutputChannel -*/ -void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, - unsigned int channel, void *data ); - -/*@}*/ - - -/** @name Buffer processing functions for callback streams -*/ -/*@{*/ - -/** Commence processing a host buffer (or a pair of host buffers in the - full-duplex case) for a callback stream. - - @param bufferProcessor The buffer processor. - - @param timeInfo Timing information for the first sample of the host - buffer(s). This information may be adjusted when buffer adaption is being - performed. - - @param callbackStatusFlags Flags indicating whether underruns and overruns - have occurred since the last time the buffer processor was called. -*/ -void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor, - PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags ); - - -/** Finish processing a host buffer (or a pair of host buffers in the - full-duplex case) for a callback stream. - - @param bufferProcessor The buffer processor. - - @param callbackResult On input, indicates a previous callback result, and on - exit, the result of the user stream callback, if it is called. - On entry callbackResult should contain one of { paContinue, paComplete, or - paAbort}. If paComplete is passed, the stream callback will not be called - but any audio that was generated by previous stream callbacks will be copied - to the output buffer(s). You can check whether the buffer processor's internal - buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty. - - If the stream callback is called its result is stored in *callbackResult. If - the stream callback returns paComplete or paAbort, all output buffers will be - full of valid data - some of which may be zeros to acount for data that - wasn't generated by the terminating callback. - - @return The number of frames processed. This usually corresponds to the - number of frames specified by the PaUtil_Set*FrameCount functions, exept in - the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a - smaller value may be returned. -*/ -unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor, - int *callbackResult ); - - -/** Determine whether any callback generated output remains in the bufffer - processor's internal buffers. This method may be used to determine when to - continue calling PaUtil_EndBufferProcessing() after the callback has returned - a callbackResult of paComplete. - - @param bufferProcessor The buffer processor. - - @return Returns non-zero when callback generated output remains in the internal - buffer and zero (0) when there internal buffer contains no callback generated - data. -*/ -int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor ); - -/*@}*/ - - -/** @name Buffer processing functions for blocking read/write streams -*/ -/*@{*/ - -/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels - functions to a user supplied buffer. This function is intended for use with - blocking read/write streams. Copies the minimum of the number of - user frames (specified by the frameCount parameter) and the number of available - host frames (specified in a previous call to SetInputFrameCount()). - - @param bufferProcessor The buffer processor. - - @param buffer A pointer to the user buffer pointer, or a pointer to a pointer - to an array of user buffer pointers for a non-interleaved stream. It is - important that this parameter points to a copy of the user buffer pointers, - not to the actual user buffer pointers, because this function updates the - pointers before returning. - - @param frameCount The number of frames of data in the buffer(s) pointed to by - the buffer parameter. - - @return The number of frames copied. The buffer pointer(s) pointed to by the - buffer parameter are advanced to point to the frame(s) following the last one - filled. -*/ -unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor, - void **buffer, unsigned long frameCount ); - - -/* Copy samples from a user supplied buffer to host output channels set up by - the PaUtil_Set*OutputChannels functions. This function is intended for use with - blocking read/write streams. Copies the minimum of the number of - user frames (specified by the frameCount parameter) and the number of - host frames (specified in a previous call to SetOutputFrameCount()). - - @param bufferProcessor The buffer processor. - - @param buffer A pointer to the user buffer pointer, or a pointer to a pointer - to an array of user buffer pointers for a non-interleaved stream. It is - important that this parameter points to a copy of the user buffer pointers, - not to the actual user buffer pointers, because this function updates the - pointers before returning. - - @param frameCount The number of frames of data in the buffer(s) pointed to by - the buffer parameter. - - @return The number of frames copied. The buffer pointer(s) pointed to by the - buffer parameter are advanced to point to the frame(s) following the last one - copied. -*/ -unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor, - const void ** buffer, unsigned long frameCount ); - - -/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels - functions. This function is useful for flushing streams. - Zeros the minimum of frameCount and the number of host frames specified in a - previous call to SetOutputFrameCount(). - - @param bufferProcessor The buffer processor. - - @param frameCount The maximum number of frames to zero. - - @return The number of frames zeroed. -*/ -unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor, - unsigned long frameCount ); - - -/*@}*/ - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_PROCESS_H */ +#ifndef PA_PROCESS_H +#define PA_PROCESS_H +/* + * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 rossbencina Exp $ + * Portable Audio I/O Library callback buffer processing adapters + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Buffer Processor prototypes. A Buffer Processor performs buffer length + adaption, coordinates sample format conversion, and interleaves/deinterleaves + channels. + +

Overview

+ + The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio + data from host buffers to user buffers and back again. Where required, the + buffer processor takes care of converting between host and user sample formats, + interleaving and deinterleaving multichannel buffers, and adapting between host + and user buffers with different lengths. The buffer processor may be used with + full and half duplex streams, for both callback streams and blocking read/write + streams. + + One of the important capabilities provided by the buffer processor is + the ability to adapt between user and host buffer sizes of different lengths + with minimum latency. Although this task is relatively easy to perform when + the host buffer size is an integer multiple of the user buffer size, the + problem is more complicated when this is not the case - especially for + full-duplex callback streams. Where necessary the adaption is implemented by + internally buffering some input and/or output data. The buffer adation + algorithm used by the buffer processor was originally implemented by + Stephan Letz for the ASIO version of PortAudio, and is described in his + Callback_adaption_.pdf which is included in the distribution. + + The buffer processor performs sample conversion using the functions provided + by pa_converters.c. + + The following sections provide an overview of how to use the buffer processor. + Interested readers are advised to consult the host API implementations for + examples of buffer processor usage. + + +

Initialization, resetting and termination

+ + When a stream is opened, the buffer processor should be initialized using + PaUtil_InitializeBufferProcessor. This function initializes internal state + and allocates temporary buffers as neccesary according to the supplied + configuration parameters. Some of the parameters correspond to those requested + by the user in their call to Pa_OpenStream(), others reflect the requirements + of the host API implementation - they indicate host buffer sizes, formats, + and the type of buffering which the Host API uses. The buffer processor should + be initialized for callback streams and blocking read/write streams. + + Call PaUtil_ResetBufferProcessor to clear any sample data which is present + in the buffer processor before starting to use it (for example when + Pa_StartStream is called). + + When the buffer processor is no longer used call + PaUtil_TerminateBufferProcessor. + + +

Using the buffer processor for a callback stream

+ + The buffer processor's role in a callback stream is to take host input buffers + process them with the stream callback, and fill host output buffers. For a + full duplex stream, the buffer processor handles input and output simultaneously + due to the requirements of the minimum-latency buffer adation algorithm. + + When a host buffer becomes available, the implementation should call + the buffer processor to process the buffer. The buffer processor calls the + stream callback to consume and/or produce audio data as necessary. The buffer + processor will convert sample formats, interleave/deinterleave channels, + and slice or chunk the data to the appropriate buffer lengths according to + the requirements of the stream callback and the host API. + + To process a host buffer (or a pair of host buffers for a full-duplex stream) + use the following calling sequence: + + -# Call PaUtil_BeginBufferProcessing + -# For a stream which takes input: + - Call PaUtil_SetInputFrameCount with the number of frames in the host input + buffer. + - Call one of the following functions one or more times to tell the + buffer processor about the host input buffer(s): PaUtil_SetInputChannel, + PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel. + Which function you call will depend on whether the host buffer(s) are + interleaved or not. + - If the available host data is split accross two buffers (for example a + data range at the end of a circular buffer and another range at the + beginning of the circular buffer), also call + PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel, + PaUtil_Set2ndInterleavedInputChannels, + PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer + processor about the second buffer. + -# For a stream which generates output: + - Call PaUtil_SetOutputFrameCount with the number of frames in the host + output buffer. + - Call one of the following functions one or more times to tell the + buffer processor about the host output buffer(s): PaUtil_SetOutputChannel, + PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel. + Which function you call will depend on whether the host buffer(s) are + interleaved or not. + - If the available host output buffer space is split accross two buffers + (for example a data range at the end of a circular buffer and another + range at the beginning of the circular buffer), call + PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel, + PaUtil_Set2ndInterleavedOutputChannels, + PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer + processor about the second buffer. + -# Call PaUtil_EndBufferProcessing, this function performs the actual data + conversion and processing. + + +

Using the buffer processor for a blocking read/write stream

+ + Blocking read/write streams use the buffer processor to convert and copy user + output data to a host buffer, and to convert and copy host input data to + the user's buffer. The buffer processor does not perform any buffer adaption. + When using the buffer processor in a blocking read/write stream the input and + output conversion are performed separately by the PaUtil_CopyInput and + PaUtil_CopyOutput functions. + + To copy data from a host input buffer to the buffer(s) which the user supplies + to Pa_ReadStream, use the following calling sequence. + + - Repeat the following three steps until the user buffer(s) have been filled + with samples from the host input buffers: + -# Call PaUtil_SetInputFrameCount with the number of frames in the host + input buffer. + -# Call one of the following functions one or more times to tell the + buffer processor about the host input buffer(s): PaUtil_SetInputChannel, + PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel. + Which function you call will depend on whether the host buffer(s) are + interleaved or not. + -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the + array of buffer pointers for a non-interleaved stream) passed to + Pa_ReadStream, along with the number of frames in the user buffer(s). + Be careful to pass a copy of the user buffer pointers to + PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to + the start of the next region to copy. + - PaUtil_CopyInput will not copy more data than is available in the + host buffer(s), so the above steps need to be repeated until the user + buffer(s) are full. + + + To copy data to the host output buffer from the user buffers(s) supplied + to Pa_WriteStream use the following calling sequence. + + - Repeat the following three steps until all frames from the user buffer(s) + have been copied to the host API: + -# Call PaUtil_SetOutputFrameCount with the number of frames in the host + output buffer. + -# Call one of the following functions one or more times to tell the + buffer processor about the host output buffer(s): PaUtil_SetOutputChannel, + PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel. + Which function you call will depend on whether the host buffer(s) are + interleaved or not. + -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the + array of buffer pointers for a non-interleaved stream) passed to + Pa_WriteStream, along with the number of frames in the user buffer(s). + Be careful to pass a copy of the user buffer pointers to + PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to + the start of the next region to copy. + - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s), + so the above steps need to be repeated until all user data is copied. +*/ + + +#include "portaudio.h" +#include "pa_converters.h" +#include "pa_dither.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type + of buffering that the host API uses. + + The mode used depends on whether the host API or the implementation manages + the buffers, and how these buffers are used (scatter gather, circular buffer). +*/ +typedef enum { +/** The host buffer size is a fixed known size. */ + paUtilFixedHostBufferSize, + +/** The host buffer size may vary, but has a known maximum size. */ + paUtilBoundedHostBufferSize, + +/** Nothing is known about the host buffer size. */ + paUtilUnknownHostBufferSize, + +/** The host buffer size varies, and the client does not require the buffer + processor to consume all of the input and fill all of the output buffer. This + is useful when the implementation has access to the host API's circular buffer + and only needs to consume/fill some of it, not necessarily all of it, with each + call to the buffer processor. This is the only mode where + PaUtil_EndBufferProcessing() may not consume the whole buffer. +*/ + paUtilVariableHostBufferSizePartialUsageAllowed +}PaUtilHostBufferSizeMode; + + +/** @brief An auxilliary data structure used internally by the buffer processor + to represent host input and output buffers. */ +typedef struct PaUtilChannelDescriptor{ + void *data; + unsigned int stride; /**< stride in samples, not bytes */ +}PaUtilChannelDescriptor; + + +/** @brief The main buffer processor data structure. + + Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor + and terminate it with PaUtil_TerminateBufferProcessor. +*/ +typedef struct { + unsigned long framesPerUserBuffer; + unsigned long framesPerHostBuffer; + + PaUtilHostBufferSizeMode hostBufferSizeMode; + int useNonAdaptingProcess; + unsigned long framesPerTempBuffer; + + unsigned int inputChannelCount; + unsigned int bytesPerHostInputSample; + unsigned int bytesPerUserInputSample; + int userInputIsInterleaved; + PaUtilConverter *inputConverter; + PaUtilZeroer *inputZeroer; + + unsigned int outputChannelCount; + unsigned int bytesPerHostOutputSample; + unsigned int bytesPerUserOutputSample; + int userOutputIsInterleaved; + PaUtilConverter *outputConverter; + PaUtilZeroer *outputZeroer; + + unsigned long initialFramesInTempInputBuffer; + unsigned long initialFramesInTempOutputBuffer; + + void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */ + void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */ + unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ + + void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */ + void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */ + unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ + + PaStreamCallbackTimeInfo *timeInfo; + + PaStreamCallbackFlags callbackStatusFlags; + + unsigned long hostInputFrameCount[2]; + PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors. + pointers are NULL for half-duplex output processing. + hostInputChannels[i].data is NULL when the caller + calls PaUtil_SetNoInput() + */ + unsigned long hostOutputFrameCount[2]; + PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors. + pointers are NULL for half-duplex input processing. + hostOutputChannels[i].data is NULL when the caller + calls PaUtil_SetNoOutput() + */ + + PaUtilTriangularDitherGenerator ditherGenerator; + + double samplePeriod; + + PaStreamCallback *streamCallback; + void *userData; +} PaUtilBufferProcessor; + + +/** @name Initialization, termination, resetting and info */ +/*@{*/ + +/** Initialize a buffer processor's representation stored in a + PaUtilBufferProcessor structure. Be sure to call + PaUtil_TerminateBufferProcessor after finishing with a buffer processor. + + @param bufferProcessor The buffer processor structure to initialize. + + @param inputChannelCount The number of input channels as passed to + Pa_OpenStream or 0 for an output-only stream. + + @param userInputSampleFormat Format of user input samples, as passed to + Pa_OpenStream. This parameter is ignored for ouput-only streams. + + @param hostInputSampleFormat Format of host input samples. This parameter is + ignored for output-only streams. See note about host buffer interleave below. + + @param outputChannelCount The number of output channels as passed to + Pa_OpenStream or 0 for an input-only stream. + + @param userOutputSampleFormat Format of user output samples, as passed to + Pa_OpenStream. This parameter is ignored for input-only streams. + + @param hostOutputSampleFormat Format of host output samples. This parameter is + ignored for input-only streams. See note about host buffer interleave below. + + @param sampleRate Sample rate of the stream. The more accurate this is the + better - it is used for updating time stamps when adapting buffers. + + @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is + used for selecting special sample conversion options such as clipping and + dithering. + + @param framesPerUserBuffer Number of frames per user buffer, as requested + by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be + zero to indicate that the user will accept any (and varying) buffer sizes. + + @param framesPerHostBuffer Specifies the number of frames per host buffer + for the fixed buffer size mode, and the maximum number of frames + per host buffer for the bounded host buffer size mode. It is ignored for + the other modes. + + @param hostBufferSizeMode A mode flag indicating the size variability of + host buffers that will be passed to the buffer processor. See + PaUtilHostBufferSizeMode for further details. + + @param streamCallback The user stream callback passed to Pa_OpenStream. + + @param userData The user data field passed to Pa_OpenStream. + + @note The interleave flag is ignored for host buffer formats. Host + interleave is determined by the use of different SetInput and SetOutput + functions. + + @return An error code indicating whether the initialization was successful. + If the error code is not PaNoError, the buffer processor was not initialized + and should not be used. + + @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor +*/ +PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor, + int inputChannelCount, PaSampleFormat userInputSampleFormat, + PaSampleFormat hostInputSampleFormat, + int outputChannelCount, PaSampleFormat userOutputSampleFormat, + PaSampleFormat hostOutputSampleFormat, + double sampleRate, + PaStreamFlags streamFlags, + unsigned long framesPerUserBuffer, /* 0 indicates don't care */ + unsigned long framesPerHostBuffer, + PaUtilHostBufferSizeMode hostBufferSizeMode, + PaStreamCallback *streamCallback, void *userData ); + + +/** Terminate a buffer processor's representation. Deallocates any temporary + buffers allocated by PaUtil_InitializeBufferProcessor. + + @param bufferProcessor The buffer processor structure to terminate. + + @see PaUtil_InitializeBufferProcessor. +*/ +void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); + + +/** Clear any internally buffered data. If you call + PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you + call PaUtil_ResetBufferProcessor in your StartStream call. + + @param bufferProcessor The buffer processor to reset. +*/ +void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); + + +/** Retrieve the input latency of a buffer processor. + + @param bufferProcessor The buffer processor examine. + + @return The input latency introduced by the buffer processor, in frames. + + @see PaUtil_GetBufferProcessorOutputLatency +*/ +unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor ); + +/** Retrieve the output latency of a buffer processor. + + @param bufferProcessor The buffer processor examine. + + @return The output latency introduced by the buffer processor, in frames. + + @see PaUtil_GetBufferProcessorInputLatency +*/ +unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor ); + +/*@}*/ + + +/** @name Host buffer pointer configuration + + Functions to set host input and output buffers, used by both callback streams + and blocking read/write streams. +*/ +/*@{*/ + + +/** Set the number of frames in the input host buffer(s) specified by the + PaUtil_Set*InputChannel functions. + + @param bufferProcessor The buffer processor. + + @param frameCount The number of host input frames. A 0 frameCount indicates to + use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor. + + @see PaUtil_SetNoInput, PaUtil_SetInputChannel, + PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel +*/ +void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + + +/** Indicate that no input is avalable. This function should be used when + priming the output of a full-duplex stream opened with the + paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary + to call this or any othe PaUtil_Set*Input* functions for ouput-only streams. + + @param bufferProcessor The buffer processor. +*/ +void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor ); + + +/** Provide the buffer processor with a pointer to a host input channel. + + @param bufferProcessor The buffer processor. + @param channel The channel number. + @param data The buffer. + @param stride The stride from one sample to the next, in samples. For + interleaved host buffers, the stride will usually be the same as the number of + channels in the buffer. +*/ +void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + + +/** Provide the buffer processor with a pointer to an number of interleaved + host input channels. + + @param bufferProcessor The buffer processor. + @param firstChannel The first channel number. + @param data The buffer. + @param channelCount The number of interleaved channels in the buffer. If + channelCount is zero, the number of channels specified to + PaUtil_InitializeBufferProcessor will be used. +*/ +void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + + +/** Provide the buffer processor with a pointer to one non-interleaved host + output channel. + + @param bufferProcessor The buffer processor. + @param channel The channel number. + @param data The buffer. +*/ +void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + + +/** Use for the second buffer half when the input buffer is split in two halves. + @see PaUtil_SetInputFrameCount +*/ +void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + +/** Use for the second buffer half when the input buffer is split in two halves. + @see PaUtil_SetInputChannel +*/ +void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + +/** Use for the second buffer half when the input buffer is split in two halves. + @see PaUtil_SetInterleavedInputChannels +*/ +void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + +/** Use for the second buffer half when the input buffer is split in two halves. + @see PaUtil_SetNonInterleavedInputChannel +*/ +void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + + +/** Set the number of frames in the output host buffer(s) specified by the + PaUtil_Set*OutputChannel functions. + + @param bufferProcessor The buffer processor. + + @param frameCount The number of host output frames. A 0 frameCount indicates to + use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor. + + @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels, + PaUtil_SetNonInterleavedOutputChannel +*/ +void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + + +/** Indicate that the output will be discarded. This function should be used + when implementing the paNeverDropInput mode for full duplex streams. + + @param bufferProcessor The buffer processor. +*/ +void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor ); + + +/** Provide the buffer processor with a pointer to a host output channel. + + @param bufferProcessor The buffer processor. + @param channel The channel number. + @param data The buffer. + @param stride The stride from one sample to the next, in samples. For + interleaved host buffers, the stride will usually be the same as the number of + channels in the buffer. +*/ +void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + + +/** Provide the buffer processor with a pointer to a number of interleaved + host output channels. + + @param bufferProcessor The buffer processor. + @param firstChannel The first channel number. + @param data The buffer. + @param channelCount The number of interleaved channels in the buffer. If + channelCount is zero, the number of channels specified to + PaUtil_InitializeBufferProcessor will be used. +*/ +void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + + +/** Provide the buffer processor with a pointer to one non-interleaved host + output channel. + + @param bufferProcessor The buffer processor. + @param channel The channel number. + @param data The buffer. +*/ +void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + + +/** Use for the second buffer half when the output buffer is split in two halves. + @see PaUtil_SetOutputFrameCount +*/ +void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + +/** Use for the second buffer half when the output buffer is split in two halves. + @see PaUtil_SetOutputChannel +*/ +void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + +/** Use for the second buffer half when the output buffer is split in two halves. + @see PaUtil_SetInterleavedOutputChannels +*/ +void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + +/** Use for the second buffer half when the output buffer is split in two halves. + @see PaUtil_SetNonInterleavedOutputChannel +*/ +void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + +/*@}*/ + + +/** @name Buffer processing functions for callback streams +*/ +/*@{*/ + +/** Commence processing a host buffer (or a pair of host buffers in the + full-duplex case) for a callback stream. + + @param bufferProcessor The buffer processor. + + @param timeInfo Timing information for the first sample of the host + buffer(s). This information may be adjusted when buffer adaption is being + performed. + + @param callbackStatusFlags Flags indicating whether underruns and overruns + have occurred since the last time the buffer processor was called. +*/ +void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor, + PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags ); + + +/** Finish processing a host buffer (or a pair of host buffers in the + full-duplex case) for a callback stream. + + @param bufferProcessor The buffer processor. + + @param callbackResult On input, indicates a previous callback result, and on + exit, the result of the user stream callback, if it is called. + On entry callbackResult should contain one of { paContinue, paComplete, or + paAbort}. If paComplete is passed, the stream callback will not be called + but any audio that was generated by previous stream callbacks will be copied + to the output buffer(s). You can check whether the buffer processor's internal + buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty. + + If the stream callback is called its result is stored in *callbackResult. If + the stream callback returns paComplete or paAbort, all output buffers will be + full of valid data - some of which may be zeros to acount for data that + wasn't generated by the terminating callback. + + @return The number of frames processed. This usually corresponds to the + number of frames specified by the PaUtil_Set*FrameCount functions, exept in + the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a + smaller value may be returned. +*/ +unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor, + int *callbackResult ); + + +/** Determine whether any callback generated output remains in the bufffer + processor's internal buffers. This method may be used to determine when to + continue calling PaUtil_EndBufferProcessing() after the callback has returned + a callbackResult of paComplete. + + @param bufferProcessor The buffer processor. + + @return Returns non-zero when callback generated output remains in the internal + buffer and zero (0) when there internal buffer contains no callback generated + data. +*/ +int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor ); + +/*@}*/ + + +/** @name Buffer processing functions for blocking read/write streams +*/ +/*@{*/ + +/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels + functions to a user supplied buffer. This function is intended for use with + blocking read/write streams. Copies the minimum of the number of + user frames (specified by the frameCount parameter) and the number of available + host frames (specified in a previous call to SetInputFrameCount()). + + @param bufferProcessor The buffer processor. + + @param buffer A pointer to the user buffer pointer, or a pointer to a pointer + to an array of user buffer pointers for a non-interleaved stream. It is + important that this parameter points to a copy of the user buffer pointers, + not to the actual user buffer pointers, because this function updates the + pointers before returning. + + @param frameCount The number of frames of data in the buffer(s) pointed to by + the buffer parameter. + + @return The number of frames copied. The buffer pointer(s) pointed to by the + buffer parameter are advanced to point to the frame(s) following the last one + filled. +*/ +unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor, + void **buffer, unsigned long frameCount ); + + +/* Copy samples from a user supplied buffer to host output channels set up by + the PaUtil_Set*OutputChannels functions. This function is intended for use with + blocking read/write streams. Copies the minimum of the number of + user frames (specified by the frameCount parameter) and the number of + host frames (specified in a previous call to SetOutputFrameCount()). + + @param bufferProcessor The buffer processor. + + @param buffer A pointer to the user buffer pointer, or a pointer to a pointer + to an array of user buffer pointers for a non-interleaved stream. It is + important that this parameter points to a copy of the user buffer pointers, + not to the actual user buffer pointers, because this function updates the + pointers before returning. + + @param frameCount The number of frames of data in the buffer(s) pointed to by + the buffer parameter. + + @return The number of frames copied. The buffer pointer(s) pointed to by the + buffer parameter are advanced to point to the frame(s) following the last one + copied. +*/ +unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor, + const void ** buffer, unsigned long frameCount ); + + +/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels + functions. This function is useful for flushing streams. + Zeros the minimum of frameCount and the number of host frames specified in a + previous call to SetOutputFrameCount(). + + @param bufferProcessor The buffer processor. + + @param frameCount The maximum number of frames to zero. + + @return The number of frames zeroed. +*/ +unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + + +/*@}*/ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_PROCESS_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_skeleton.c b/pjmedia/src/pjmedia/portaudio/pa_skeleton.c index bea14d9f7..ebc1f5018 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_skeleton.c +++ b/pjmedia/src/pjmedia/portaudio/pa_skeleton.c @@ -1,807 +1,807 @@ -/* - * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 rossbencina Exp $ - * Portable Audio I/O Library skeleton implementation - * demonstrates how to use the common functions to implement support - * for a host API - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Skeleton implementation of support for a host API. - - @note This file is provided as a starting point for implementing support for - a new host API. IMPLEMENT ME comments are used to indicate functionality - which much be customised for each implementation. -*/ - - -#include /* strlen() */ - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - - -/* IMPLEMENT ME: a macro like the following one should be used for reporting - host errors */ -#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) - -/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ -} -PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */ - - -PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i, deviceCount; - PaSkeletonHostApiRepresentation *skeletonHostApi; - PaDeviceInfo *deviceInfoArray; - - skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) ); - if( !skeletonHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - skeletonHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !skeletonHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &skeletonHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */ - (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */ - - (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */ - (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */ - - (*hostApi)->info.deviceCount = 0; - - deviceCount = 0; /* IMPLEMENT ME */ - - if( deviceCount > 0 ) - { - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i < deviceCount; ++i ) - { - PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg: - deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, srcName ); - deviceInfo->name = deviceName; - */ - - deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */ - deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */ - - deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */ - - deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */ - - (*hostApi)->deviceInfos[i] = deviceInfo; - ++(*hostApi)->info.deviceCount; - } - } - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( skeletonHostApi ) - { - if( skeletonHostApi->allocations ) - { - PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); - PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); - } - - PaUtil_FreeMemory( skeletonHostApi ); - } - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - */ - - if( skeletonHostApi->allocations ) - { - PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); - PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); - } - - PaUtil_FreeMemory( skeletonHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support outputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - /* - IMPLEMENT ME: - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported if necessary - - - check that the device supports sampleRate - - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from inputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - */ - - - /* suppress unused variable warnings */ - (void) sampleRate; - - return paFormatIsSupported; -} - -/* PaSkeletonStream - a stream data structure specifically for this implementation */ - -typedef struct PaSkeletonStream -{ /* IMPLEMENT ME: rename this */ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - /* IMPLEMENT ME: - - implementation specific data goes here - */ - unsigned long framesPerHostCallback; /* just an example */ -} -PaSkeletonStream; - -/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; - PaSkeletonStream *stream = 0; - unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - /* IMPLEMENT ME - establish which host formats are available */ - hostInputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); - } - else - { - inputChannelCount = 0; - inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - - /* IMPLEMENT ME - establish which host formats are available */ - hostOutputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); - } - else - { - outputChannelCount = 0; - outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ - } - - /* - IMPLEMENT ME: - - ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? ) - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - - - alter sampleRate to a close allowable rate if possible / necessary - - - validate suggestedInputLatency and suggestedOutputLatency parameters, - use default values where necessary - */ - - - - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - - stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &skeletonHostApi->callbackStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &skeletonHostApi->blockingStreamInterface, streamCallback, userData ); - } - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - /* we assume a fixed host buffer size in this example, but the buffer processor - can also support bounded and unknown host buffer sizes by passing - paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of - paUtilFixedHostBufferSize below. */ - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerHostBuffer, paUtilFixedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) - goto error; - - - /* - IMPLEMENT ME: initialise the following fields with estimated or actual - values. - */ - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - - /* - IMPLEMENT ME: - - additional stream setup + opening - */ - - stream->framesPerHostCallback = framesPerHostBuffer; - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - PaUtil_FreeMemory( stream ); - - return result; -} - -/* - ExampleHostProcessingLoop() illustrates the kind of processing which may - occur in a host implementation. - -*/ -static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)userData; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ - int callbackResult; - unsigned long framesProcessed; - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* - IMPLEMENT ME: - - generate timing information - - handle buffer slips - */ - - /* - If you need to byte swap or shift inputBuffer to convert it into a - portaudio format, do it here. - */ - - - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ ); - - /* - depending on whether the host buffers are interleaved, non-interleaved - or a mixture, you will want to call PaUtil_SetInterleaved*Channels(), - PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here. - */ - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, - 0, /* first channel of inputBuffer is channel 0 */ - inputBuffer, - 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, - 0, /* first channel of outputBuffer is channel 0 */ - outputBuffer, - 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ - - /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing() - in general you would pass paContinue for normal operation, and - paComplete to drain the buffer processor's internal output buffer. - You can check whether the buffer processor's output buffer is empty - using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor ) - */ - callbackResult = paContinue; - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - - - /* - If you need to byte swap or shift outputBuffer to convert it to - host format, do it here. - */ - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - - if( callbackResult == paContinue ) - { - /* nothing special to do */ - } - else if( callbackResult == paAbort ) - { - /* IMPLEMENT ME - finish playback immediately */ - - /* once finished, call the finished callback */ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - else - { - /* User callback has asked us to stop with paComplete or other non-zero value */ - - /* IMPLEMENT ME - finish playback once currently queued audio has completed */ - - /* once finished, call the finished callback */ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } -} - - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* - IMPLEMENT ME: - - additional stream closing + cleanup - */ - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - - return result; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - /* suppress unused function warning. the code in ExampleHostProcessingLoop or - something similar should be implemented to feed samples to and from the - host after StartStream() is called. - */ - (void) ExampleHostProcessingLoop; - - return result; -} - - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return result; -} - - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return 0; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior */ - - return 0; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaSkeletonStream *stream = (PaSkeletonStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - - - +/* + * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 rossbencina Exp $ + * Portable Audio I/O Library skeleton implementation + * demonstrates how to use the common functions to implement support + * for a host API + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Skeleton implementation of support for a host API. + + @note This file is provided as a starting point for implementing support for + a new host API. IMPLEMENT ME comments are used to indicate functionality + which much be customised for each implementation. +*/ + + +#include /* strlen() */ + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + + +/* prototypes for functions declared in this file */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + + +/* IMPLEMENT ME: a macro like the following one should be used for reporting + host errors */ +#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ + PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) + +/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + /* implementation specific data goes here */ +} +PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */ + + +PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i, deviceCount; + PaSkeletonHostApiRepresentation *skeletonHostApi; + PaDeviceInfo *deviceInfoArray; + + skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) ); + if( !skeletonHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + skeletonHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !skeletonHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &skeletonHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */ + (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */ + + (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */ + (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */ + + (*hostApi)->info.deviceCount = 0; + + deviceCount = 0; /* IMPLEMENT ME */ + + if( deviceCount > 0 ) + { + (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); + if( !(*hostApi)->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + goto error; + } + + for( i=0; i < deviceCount; ++i ) + { + PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg: + deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, srcName ); + deviceInfo->name = deviceName; + */ + + deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */ + deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */ + + deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */ + + deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */ + + (*hostApi)->deviceInfos[i] = deviceInfo; + ++(*hostApi)->info.deviceCount; + } + } + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyRead, PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + return result; + +error: + if( skeletonHostApi ) + { + if( skeletonHostApi->allocations ) + { + PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); + PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); + } + + PaUtil_FreeMemory( skeletonHostApi ); + } + return result; +} + + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; + + /* + IMPLEMENT ME: + - clean up any resources not handled by the allocation group + */ + + if( skeletonHostApi->allocations ) + { + PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); + PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); + } + + PaUtil_FreeMemory( skeletonHostApi ); +} + + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if( inputSampleFormat & paCustomFormat ) + return paSampleFormatNotSupported; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if( outputSampleFormat & paCustomFormat ) + return paSampleFormatNotSupported; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support outputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + } + + /* + IMPLEMENT ME: + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported if necessary + + - check that the device supports sampleRate + + Because the buffer adapter handles conversion between all standard + sample formats, the following checks are only required if paCustomFormat + is implemented, or under some other unusual conditions. + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from inputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + */ + + + /* suppress unused variable warnings */ + (void) sampleRate; + + return paFormatIsSupported; +} + +/* PaSkeletonStream - a stream data structure specifically for this implementation */ + +typedef struct PaSkeletonStream +{ /* IMPLEMENT ME: rename this */ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + /* IMPLEMENT ME: + - implementation specific data goes here + */ + unsigned long framesPerHostCallback; /* just an example */ +} +PaSkeletonStream; + +/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ + +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result = paNoError; + PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; + PaSkeletonStream *stream = 0; + unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + + /* IMPLEMENT ME - establish which host formats are available */ + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); + } + else + { + inputChannelCount = 0; + inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */ + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + + /* IMPLEMENT ME - establish which host formats are available */ + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); + } + else + { + outputChannelCount = 0; + outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ + } + + /* + IMPLEMENT ME: + + ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? ) + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + + - alter sampleRate to a close allowable rate if possible / necessary + + - validate suggestedInputLatency and suggestedOutputLatency parameters, + use default values where necessary + */ + + + + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + + stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + if( streamCallback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &skeletonHostApi->callbackStreamInterface, streamCallback, userData ); + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &skeletonHostApi->blockingStreamInterface, streamCallback, userData ); + } + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + + /* we assume a fixed host buffer size in this example, but the buffer processor + can also support bounded and unknown host buffer sizes by passing + paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of + paUtilFixedHostBufferSize below. */ + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerHostBuffer, paUtilFixedHostBufferSize, + streamCallback, userData ); + if( result != paNoError ) + goto error; + + + /* + IMPLEMENT ME: initialise the following fields with estimated or actual + values. + */ + stream->streamRepresentation.streamInfo.inputLatency = + PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); + stream->streamRepresentation.streamInfo.outputLatency = + PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + + /* + IMPLEMENT ME: + - additional stream setup + opening + */ + + stream->framesPerHostCallback = framesPerHostBuffer; + + *s = (PaStream*)stream; + + return result; + +error: + if( stream ) + PaUtil_FreeMemory( stream ); + + return result; +} + +/* + ExampleHostProcessingLoop() illustrates the kind of processing which may + occur in a host implementation. + +*/ +static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)userData; + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ + int callbackResult; + unsigned long framesProcessed; + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + /* + IMPLEMENT ME: + - generate timing information + - handle buffer slips + */ + + /* + If you need to byte swap or shift inputBuffer to convert it into a + portaudio format, do it here. + */ + + + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ ); + + /* + depending on whether the host buffers are interleaved, non-interleaved + or a mixture, you will want to call PaUtil_SetInterleaved*Channels(), + PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here. + */ + + PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, + 0, /* first channel of inputBuffer is channel 0 */ + inputBuffer, + 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ + + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, + 0, /* first channel of outputBuffer is channel 0 */ + outputBuffer, + 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ + + /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing() + in general you would pass paContinue for normal operation, and + paComplete to drain the buffer processor's internal output buffer. + You can check whether the buffer processor's output buffer is empty + using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor ) + */ + callbackResult = paContinue; + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + + + /* + If you need to byte swap or shift outputBuffer to convert it to + host format, do it here. + */ + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + + if( callbackResult == paContinue ) + { + /* nothing special to do */ + } + else if( callbackResult == paAbort ) + { + /* IMPLEMENT ME - finish playback immediately */ + + /* once finished, call the finished callback */ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + } + else + { + /* User callback has asked us to stop with paComplete or other non-zero value */ + + /* IMPLEMENT ME - finish playback once currently queued audio has completed */ + + /* once finished, call the finished callback */ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + } +} + + +/* + When CloseStream() is called, the multi-api layer ensures that + the stream has already been stopped or aborted. +*/ +static PaError CloseStream( PaStream* s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* + IMPLEMENT ME: + - additional stream closing + cleanup + */ + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + + return result; +} + + +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + /* suppress unused function warning. the code in ExampleHostProcessingLoop or + something similar should be implemented to feed samples to and from the + host after StartStream() is called. + */ + (void) ExampleHostProcessingLoop; + + return result; +} + + +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return result; +} + + +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return result; +} + + +static PaError IsStreamStopped( PaStream *s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return 0; +} + + +static PaError IsStreamActive( PaStream *s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return 0; +} + + +static PaTime GetStreamTime( PaStream *s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + + +/* + As separate stream interfaces are used for blocking and callback + streams, the following functions can be guaranteed to only be called + for blocking streams. +*/ + +static PaError ReadStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) buffer; + (void) frames; + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +static PaError WriteStream( PaStream* s, + const void *buffer, + unsigned long frames ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) buffer; + (void) frames; + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + + + diff --git a/pjmedia/src/pjmedia/portaudio/pa_stream.c b/pjmedia/src/pjmedia/portaudio/pa_stream.c index 590b79437..044dde29e 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_stream.c +++ b/pjmedia/src/pjmedia/portaudio/pa_stream.c @@ -1,141 +1,141 @@ -/* - * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 rossbencina Exp $ - * Portable Audio I/O Library - * - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 2002 Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Interface used by pa_front to virtualize functions which operate on - streams. -*/ - - -#include "pa_stream.h" - - -void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, - PaError (*Close)( PaStream* ), - PaError (*Start)( PaStream* ), - PaError (*Stop)( PaStream* ), - PaError (*Abort)( PaStream* ), - PaError (*IsStopped)( PaStream* ), - PaError (*IsActive)( PaStream* ), - PaTime (*GetTime)( PaStream* ), - double (*GetCpuLoad)( PaStream* ), - PaError (*Read)( PaStream*, void *, unsigned long ), - PaError (*Write)( PaStream*, const void *, unsigned long ), - signed long (*GetReadAvailable)( PaStream* ), - signed long (*GetWriteAvailable)( PaStream* ) ) -{ - streamInterface->Close = Close; - streamInterface->Start = Start; - streamInterface->Stop = Stop; - streamInterface->Abort = Abort; - streamInterface->IsStopped = IsStopped; - streamInterface->IsActive = IsActive; - streamInterface->GetTime = GetTime; - streamInterface->GetCpuLoad = GetCpuLoad; - streamInterface->Read = Read; - streamInterface->Write = Write; - streamInterface->GetReadAvailable = GetReadAvailable; - streamInterface->GetWriteAvailable = GetWriteAvailable; -} - - -void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation, - PaUtilStreamInterface *streamInterface, - PaStreamCallback *streamCallback, - void *userData ) -{ - streamRepresentation->magic = PA_STREAM_MAGIC; - streamRepresentation->nextOpenStream = 0; - streamRepresentation->streamInterface = streamInterface; - streamRepresentation->streamCallback = streamCallback; - streamRepresentation->streamFinishedCallback = 0; - - streamRepresentation->userData = userData; - - streamRepresentation->streamInfo.inputLatency = 0.; - streamRepresentation->streamInfo.outputLatency = 0.; - streamRepresentation->streamInfo.sampleRate = 0.; -} - - -void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ) -{ - streamRepresentation->magic = 0; -} - - -PaError PaUtil_DummyRead( PaStream* stream, - void *buffer, - unsigned long frames ) -{ - (void)stream; /* unused parameter */ - (void)buffer; /* unused parameter */ - (void)frames; /* unused parameter */ - - return paCanNotReadFromACallbackStream; -} - - -PaError PaUtil_DummyWrite( PaStream* stream, - const void *buffer, - unsigned long frames ) -{ - (void)stream; /* unused parameter */ - (void)buffer; /* unused parameter */ - (void)frames; /* unused parameter */ - - return paCanNotWriteToACallbackStream; -} - - -signed long PaUtil_DummyGetReadAvailable( PaStream* stream ) -{ - (void)stream; /* unused parameter */ - - return paCanNotReadFromACallbackStream; -} - - -signed long PaUtil_DummyGetWriteAvailable( PaStream* stream ) -{ - (void)stream; /* unused parameter */ - - return paCanNotWriteToACallbackStream; -} - - -double PaUtil_DummyGetCpuLoad( PaStream* stream ) -{ - (void)stream; /* unused parameter */ - - return 0.0; -} +/* + * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 rossbencina Exp $ + * Portable Audio I/O Library + * + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 2002 Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Interface used by pa_front to virtualize functions which operate on + streams. +*/ + + +#include "pa_stream.h" + + +void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, + PaError (*Close)( PaStream* ), + PaError (*Start)( PaStream* ), + PaError (*Stop)( PaStream* ), + PaError (*Abort)( PaStream* ), + PaError (*IsStopped)( PaStream* ), + PaError (*IsActive)( PaStream* ), + PaTime (*GetTime)( PaStream* ), + double (*GetCpuLoad)( PaStream* ), + PaError (*Read)( PaStream*, void *, unsigned long ), + PaError (*Write)( PaStream*, const void *, unsigned long ), + signed long (*GetReadAvailable)( PaStream* ), + signed long (*GetWriteAvailable)( PaStream* ) ) +{ + streamInterface->Close = Close; + streamInterface->Start = Start; + streamInterface->Stop = Stop; + streamInterface->Abort = Abort; + streamInterface->IsStopped = IsStopped; + streamInterface->IsActive = IsActive; + streamInterface->GetTime = GetTime; + streamInterface->GetCpuLoad = GetCpuLoad; + streamInterface->Read = Read; + streamInterface->Write = Write; + streamInterface->GetReadAvailable = GetReadAvailable; + streamInterface->GetWriteAvailable = GetWriteAvailable; +} + + +void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation, + PaUtilStreamInterface *streamInterface, + PaStreamCallback *streamCallback, + void *userData ) +{ + streamRepresentation->magic = PA_STREAM_MAGIC; + streamRepresentation->nextOpenStream = 0; + streamRepresentation->streamInterface = streamInterface; + streamRepresentation->streamCallback = streamCallback; + streamRepresentation->streamFinishedCallback = 0; + + streamRepresentation->userData = userData; + + streamRepresentation->streamInfo.inputLatency = 0.; + streamRepresentation->streamInfo.outputLatency = 0.; + streamRepresentation->streamInfo.sampleRate = 0.; +} + + +void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ) +{ + streamRepresentation->magic = 0; +} + + +PaError PaUtil_DummyRead( PaStream* stream, + void *buffer, + unsigned long frames ) +{ + (void)stream; /* unused parameter */ + (void)buffer; /* unused parameter */ + (void)frames; /* unused parameter */ + + return paCanNotReadFromACallbackStream; +} + + +PaError PaUtil_DummyWrite( PaStream* stream, + const void *buffer, + unsigned long frames ) +{ + (void)stream; /* unused parameter */ + (void)buffer; /* unused parameter */ + (void)frames; /* unused parameter */ + + return paCanNotWriteToACallbackStream; +} + + +signed long PaUtil_DummyGetReadAvailable( PaStream* stream ) +{ + (void)stream; /* unused parameter */ + + return paCanNotReadFromACallbackStream; +} + + +signed long PaUtil_DummyGetWriteAvailable( PaStream* stream ) +{ + (void)stream; /* unused parameter */ + + return paCanNotWriteToACallbackStream; +} + + +double PaUtil_DummyGetCpuLoad( PaStream* stream ) +{ + (void)stream; /* unused parameter */ + + return 0.0; +} diff --git a/pjmedia/src/pjmedia/portaudio/pa_stream.h b/pjmedia/src/pjmedia/portaudio/pa_stream.h index c5d8f9f4d..8b86943bf 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_stream.h +++ b/pjmedia/src/pjmedia/portaudio/pa_stream.h @@ -1,196 +1,196 @@ -#ifndef PA_STREAM_H -#define PA_STREAM_H -/* - * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 rossbencina Exp $ - * Portable Audio I/O Library - * stream interface - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Interface used by pa_front to virtualize functions which operate on - streams. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#define PA_STREAM_MAGIC (0x18273645) - - -/** A structure representing an (abstract) interface to a host API. Contains - pointers to functions which implement the interface. - - All PaStreamInterface functions are guaranteed to be called with a non-null, - valid stream parameter. -*/ -typedef struct { - PaError (*Close)( PaStream* stream ); - PaError (*Start)( PaStream *stream ); - PaError (*Stop)( PaStream *stream ); - PaError (*Abort)( PaStream *stream ); - PaError (*IsStopped)( PaStream *stream ); - PaError (*IsActive)( PaStream *stream ); - PaTime (*GetTime)( PaStream *stream ); - double (*GetCpuLoad)( PaStream* stream ); - PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ); - PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ); - signed long (*GetReadAvailable)( PaStream* stream ); - signed long (*GetWriteAvailable)( PaStream* stream ); -} PaUtilStreamInterface; - - -/** Initialize the fields of a PaUtilStreamInterface structure. -*/ -void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, - PaError (*Close)( PaStream* ), - PaError (*Start)( PaStream* ), - PaError (*Stop)( PaStream* ), - PaError (*Abort)( PaStream* ), - PaError (*IsStopped)( PaStream* ), - PaError (*IsActive)( PaStream* ), - PaTime (*GetTime)( PaStream* ), - double (*GetCpuLoad)( PaStream* ), - PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ), - PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ), - signed long (*GetReadAvailable)( PaStream* stream ), - signed long (*GetWriteAvailable)( PaStream* stream ) ); - - -/** Dummy Read function for use in interfaces to a callback based streams. - Pass to the Read parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -PaError PaUtil_DummyRead( PaStream* stream, - void *buffer, - unsigned long frames ); - - -/** Dummy Write function for use in an interfaces to callback based streams. - Pass to the Write parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -PaError PaUtil_DummyWrite( PaStream* stream, - const void *buffer, - unsigned long frames ); - - -/** Dummy GetReadAvailable function for use in interfaces to callback based - streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -signed long PaUtil_DummyGetReadAvailable( PaStream* stream ); - - -/** Dummy GetWriteAvailable function for use in interfaces to callback based - streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface. - @return An error code indicating that the function has no effect - because the stream is a callback stream. -*/ -signed long PaUtil_DummyGetWriteAvailable( PaStream* stream ); - - - -/** Dummy GetCpuLoad function for use in an interface to a read/write stream. - Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface. - @return Returns 0. -*/ -double PaUtil_DummyGetCpuLoad( PaStream* stream ); - - -/** Non host specific data for a stream. This data is used by pa_front to - forward to the appropriate functions in the streamInterface structure. -*/ -typedef struct PaUtilStreamRepresentation { - unsigned long magic; /**< set to PA_STREAM_MAGIC */ - struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */ - PaUtilStreamInterface *streamInterface; - PaStreamCallback *streamCallback; - PaStreamFinishedCallback *streamFinishedCallback; - void *userData; - PaStreamInfo streamInfo; -} PaUtilStreamRepresentation; - - -/** Initialize a PaUtilStreamRepresentation structure. - - @see PaUtil_InitializeStreamRepresentation -*/ -void PaUtil_InitializeStreamRepresentation( - PaUtilStreamRepresentation *streamRepresentation, - PaUtilStreamInterface *streamInterface, - PaStreamCallback *streamCallback, - void *userData ); - - -/** Clean up a PaUtilStreamRepresentation structure previously initialized - by a call to PaUtil_InitializeStreamRepresentation. - - @see PaUtil_InitializeStreamRepresentation -*/ -void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ); - - -/** Check that the stream pointer is valid. - - @return Returns paNoError if the stream pointer appears to be OK, otherwise - returns an error indicating the cause of failure. -*/ -PaError PaUtil_ValidateStreamPointer( PaStream *stream ); - - -/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation. - - @see PaUtilStreamRepresentation -*/ -#define PA_STREAM_REP( stream )\ - ((PaUtilStreamRepresentation*) (stream) ) - - -/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface. - - @see PaUtilStreamRepresentation, PaUtilStreamInterface -*/ -#define PA_STREAM_INTERFACE( stream )\ - PA_STREAM_REP( (stream) )->streamInterface - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_STREAM_H */ +#ifndef PA_STREAM_H +#define PA_STREAM_H +/* + * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 rossbencina Exp $ + * Portable Audio I/O Library + * stream interface + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Interface used by pa_front to virtualize functions which operate on + streams. +*/ + + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +#define PA_STREAM_MAGIC (0x18273645) + + +/** A structure representing an (abstract) interface to a host API. Contains + pointers to functions which implement the interface. + + All PaStreamInterface functions are guaranteed to be called with a non-null, + valid stream parameter. +*/ +typedef struct { + PaError (*Close)( PaStream* stream ); + PaError (*Start)( PaStream *stream ); + PaError (*Stop)( PaStream *stream ); + PaError (*Abort)( PaStream *stream ); + PaError (*IsStopped)( PaStream *stream ); + PaError (*IsActive)( PaStream *stream ); + PaTime (*GetTime)( PaStream *stream ); + double (*GetCpuLoad)( PaStream* stream ); + PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ); + PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ); + signed long (*GetReadAvailable)( PaStream* stream ); + signed long (*GetWriteAvailable)( PaStream* stream ); +} PaUtilStreamInterface; + + +/** Initialize the fields of a PaUtilStreamInterface structure. +*/ +void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, + PaError (*Close)( PaStream* ), + PaError (*Start)( PaStream* ), + PaError (*Stop)( PaStream* ), + PaError (*Abort)( PaStream* ), + PaError (*IsStopped)( PaStream* ), + PaError (*IsActive)( PaStream* ), + PaTime (*GetTime)( PaStream* ), + double (*GetCpuLoad)( PaStream* ), + PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ), + PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ), + signed long (*GetReadAvailable)( PaStream* stream ), + signed long (*GetWriteAvailable)( PaStream* stream ) ); + + +/** Dummy Read function for use in interfaces to a callback based streams. + Pass to the Read parameter of PaUtil_InitializeStreamInterface. + @return An error code indicating that the function has no effect + because the stream is a callback stream. +*/ +PaError PaUtil_DummyRead( PaStream* stream, + void *buffer, + unsigned long frames ); + + +/** Dummy Write function for use in an interfaces to callback based streams. + Pass to the Write parameter of PaUtil_InitializeStreamInterface. + @return An error code indicating that the function has no effect + because the stream is a callback stream. +*/ +PaError PaUtil_DummyWrite( PaStream* stream, + const void *buffer, + unsigned long frames ); + + +/** Dummy GetReadAvailable function for use in interfaces to callback based + streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface. + @return An error code indicating that the function has no effect + because the stream is a callback stream. +*/ +signed long PaUtil_DummyGetReadAvailable( PaStream* stream ); + + +/** Dummy GetWriteAvailable function for use in interfaces to callback based + streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface. + @return An error code indicating that the function has no effect + because the stream is a callback stream. +*/ +signed long PaUtil_DummyGetWriteAvailable( PaStream* stream ); + + + +/** Dummy GetCpuLoad function for use in an interface to a read/write stream. + Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface. + @return Returns 0. +*/ +double PaUtil_DummyGetCpuLoad( PaStream* stream ); + + +/** Non host specific data for a stream. This data is used by pa_front to + forward to the appropriate functions in the streamInterface structure. +*/ +typedef struct PaUtilStreamRepresentation { + unsigned long magic; /**< set to PA_STREAM_MAGIC */ + struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */ + PaUtilStreamInterface *streamInterface; + PaStreamCallback *streamCallback; + PaStreamFinishedCallback *streamFinishedCallback; + void *userData; + PaStreamInfo streamInfo; +} PaUtilStreamRepresentation; + + +/** Initialize a PaUtilStreamRepresentation structure. + + @see PaUtil_InitializeStreamRepresentation +*/ +void PaUtil_InitializeStreamRepresentation( + PaUtilStreamRepresentation *streamRepresentation, + PaUtilStreamInterface *streamInterface, + PaStreamCallback *streamCallback, + void *userData ); + + +/** Clean up a PaUtilStreamRepresentation structure previously initialized + by a call to PaUtil_InitializeStreamRepresentation. + + @see PaUtil_InitializeStreamRepresentation +*/ +void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ); + + +/** Check that the stream pointer is valid. + + @return Returns paNoError if the stream pointer appears to be OK, otherwise + returns an error indicating the cause of failure. +*/ +PaError PaUtil_ValidateStreamPointer( PaStream *stream ); + + +/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation. + + @see PaUtilStreamRepresentation +*/ +#define PA_STREAM_REP( stream )\ + ((PaUtilStreamRepresentation*) (stream) ) + + +/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface. + + @see PaUtilStreamRepresentation, PaUtilStreamInterface +*/ +#define PA_STREAM_INTERFACE( stream )\ + PA_STREAM_REP( (stream) )->streamInterface + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_STREAM_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_trace.c b/pjmedia/src/pjmedia/portaudio/pa_trace.c index a80541b28..83514a053 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_trace.c +++ b/pjmedia/src/pjmedia/portaudio/pa_trace.c @@ -1,88 +1,88 @@ -/* - * $Id: pa_trace.c,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $ - * Portable Audio I/O Library Trace Facility - * Store trace information in real-time for later printing. - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Event trace mechanism for debugging. -*/ - - -#include -#include -#include -#include "pa_trace.h" - -#if PA_TRACE_REALTIME_EVENTS - -static char *traceTextArray[MAX_TRACE_RECORDS]; -static int traceIntArray[MAX_TRACE_RECORDS]; -static int traceIndex = 0; -static int traceBlock = 0; - -/*********************************************************************/ -void PaUtil_ResetTraceMessages() -{ - traceIndex = 0; -} - -/*********************************************************************/ -void PaUtil_DumpTraceMessages() -{ - int i; - int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS; - - printf("DumpTraceMessages: traceIndex = %d\n", traceIndex ); - for( i=0; i +#include +#include +#include "pa_trace.h" + +#if PA_TRACE_REALTIME_EVENTS + +static char *traceTextArray[MAX_TRACE_RECORDS]; +static int traceIntArray[MAX_TRACE_RECORDS]; +static int traceIndex = 0; +static int traceBlock = 0; + +/*********************************************************************/ +void PaUtil_ResetTraceMessages() +{ + traceIndex = 0; +} + +/*********************************************************************/ +void PaUtil_DumpTraceMessages() +{ + int i; + int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS; + + printf("DumpTraceMessages: traceIndex = %d\n", traceIndex ); + for( i=0; i must be included in the - context in which this macro is used. -*/ -#define PA_VALIDATE_TYPE_SIZES \ - { \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \ - assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \ - } - - -#endif /* PA_TYPES_H */ +#ifndef PA_TYPES_H +#define PA_TYPES_H + +/* + SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script + when it is used. Otherwise we default to the common 32 bit values, if your + platform doesn't use configure, and doesn't use the default values below + you will need to explicitly define these symbols in your make file. + + A PA_VALIDATE_SIZES macro is provided to assert that the values set in this + file are correct. +*/ + +#ifndef SIZEOF_SHORT +#define SIZEOF_SHORT 2 +#endif + +#ifndef SIZEOF_INT +#define SIZEOF_INT 4 +#endif + +#ifndef SIZEOF_LONG +#define SIZEOF_LONG 4 +#endif + + +#if SIZEOF_SHORT == 2 +typedef signed short PaInt16; +typedef unsigned short PaUint16; +#elif SIZEOF_INT == 2 +typedef signed int PaInt16; +typedef unsigned int PaUint16; +#else +#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform +#endif + +#if SIZEOF_SHORT == 4 +typedef signed short PaInt32; +typedef unsigned short PaUint32; +#elif SIZEOF_INT == 4 +typedef signed int PaInt32; +typedef unsigned int PaUint32; +#elif SIZEOF_LONG == 4 +typedef signed long PaInt32; +typedef unsigned long PaUint32; +#else +#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform +#endif + + +/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to + ensure that PortAudio was configured correctly, and raises an assertion if + they don't match the expected values. must be included in the + context in which this macro is used. +*/ +#define PA_VALIDATE_TYPE_SIZES \ + { \ + assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \ + assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \ + assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \ + assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \ + } + + +#endif /* PA_TYPES_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c b/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c index ac94b095c..9bddc2e0d 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c +++ b/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c @@ -1,64 +1,64 @@ -/* - * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter Exp $ - * Portable Audio I/O Library UNIX initialization table - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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 "pa_hostapi.h" - -PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -/* Added for IRIX, Pieter, oct 2, 2003: */ -PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - - -PaUtilHostApiInitializer *paHostApiInitializers[] = - { -#ifdef PA_USE_OSS - PaOSS_Initialize, -#endif - -#ifdef PA_USE_ALSA - PaAlsa_Initialize, -#endif - -#ifdef PA_USE_JACK - PaJack_Initialize, -#endif - /* Added for IRIX, Pieter, oct 2, 2003: */ -#ifdef PA_USE_SGI - PaSGI_Initialize, -#endif - 0 /* NULL terminated array */ - }; - -int paDefaultHostApiIndex = 0; - - +/* + * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter Exp $ + * Portable Audio I/O Library UNIX initialization table + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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 "pa_hostapi.h" + +PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +/* Added for IRIX, Pieter, oct 2, 2003: */ +PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + + +PaUtilHostApiInitializer *paHostApiInitializers[] = + { +#ifdef PA_USE_OSS + PaOSS_Initialize, +#endif + +#ifdef PA_USE_ALSA + PaAlsa_Initialize, +#endif + +#ifdef PA_USE_JACK + PaJack_Initialize, +#endif + /* Added for IRIX, Pieter, oct 2, 2003: */ +#ifdef PA_USE_SGI + PaSGI_Initialize, +#endif + 0 /* NULL terminated array */ + }; + +int paDefaultHostApiIndex = 0; + + diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c b/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c index 13a6a6935..2490e68f9 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c +++ b/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c @@ -1,1918 +1,1918 @@ -/* - * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $ - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * OSS implementation by: - * Douglas Repetto - * Phil Burk - * Dominic Mazzoni - * Arve Knudsen - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -# include -# define DEVICE_NAME_BASE "/dev/dsp" -#else -# include /* JH20010905 */ -# define DEVICE_NAME_BASE "/dev/audio" -#endif - -#include "portaudio.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" -#include "pa_unix_util.h" - -static int sysErr_; -static pthread_t mainThread_; - -/* Check return value of system call, and map it to PaError */ -#define ENSURE_(expr, code) \ - do { \ - if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \ - { \ - /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ - if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ - { \ - PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \ - } \ - \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (code); \ - goto error; \ - } \ - } while( 0 ); - -#ifndef AFMT_S16_NE -#define AFMT_S16_NE Get_AFMT_S16_NE() -/********************************************************************* - * Some versions of OSS do not define AFMT_S16_NE. So check CPU. - * PowerPC is Big Endian. X86 is Little Endian. - */ -static int Get_AFMT_S16_NE( void ) -{ - long testData = 1; - char *ptr = (char *) &testData; - int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */ - return isLittle ? AFMT_S16_LE : AFMT_S16_BE; -} -#endif - -/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - PaHostApiIndex hostApiIndex; -} -PaOSSHostApiRepresentation; - -/** Per-direction structure for PaOssStream. - * - * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback, - * but with different number of channels we will have to adapt between the number of user and host - * channels for at least one direction, since the configuration space is the same for both directions - * of an OSS device. - */ -typedef struct -{ - int fd; - const char *devName; - int userChannelCount, hostChannelCount; - int userInterleaved; - void *buffer; - PaSampleFormat userFormat, hostFormat; - double latency; - unsigned long hostFrames, numBufs; - void **userBuffers; /* For non-interleaved blocking */ -} PaOssStreamComponent; - -/** Implementation specific representation of a PaStream. - * - */ -typedef struct PaOssStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - PaUtilThreading threading; - - int sharedDevice; - unsigned long framesPerHostBuffer; - int triggered; /* Have the devices been triggered yet (first start) */ - - int isActive; - int isStopped; - - int lastPosPtr; - double lastStreamBytes; - - int framesProcessed; - - double sampleRate; - - int callbackMode; - int callbackStop, callbackAbort; - - PaOssStreamComponent *capture, *playback; - unsigned long pollTimeout; - sem_t semaphore; -} -PaOssStream; - -typedef enum { - StreamMode_In, - StreamMode_Out -} StreamMode; - -/* prototypes for functions declared in this file */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); -static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi ); - - -/** Initialize the OSS API implementation. - * - * This function will initialize host API datastructures and query host devices for information. - * - * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here - * - * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function, - * this happens with the usual "error" label. - */ -PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - PaOSSHostApiRepresentation *ossHostApi = NULL; - - PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ), - paInsufficientMemory ); - PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); - ossHostApi->hostApiIndex = hostApiIndex; - - /* Initialize host API structure */ - *hostApi = &ossHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paOSS; - (*hostApi)->info.name = "OSS"; - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PA_ENSURE( BuildDeviceList( ossHostApi ) ); - - PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, - PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - mainThread_ = pthread_self(); - - return result; - -error: - if( ossHostApi ) - { - if( ossHostApi->allocations ) - { - PaUtil_FreeAllAllocations( ossHostApi->allocations ); - PaUtil_DestroyAllocationGroup( ossHostApi->allocations ); - } - - PaUtil_FreeMemory( ossHostApi ); - } - return result; -} - -PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels, - int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency, - PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations ) -{ - PaError result = paNoError; - - deviceInfo->structVersion = 2; - if( allocations ) - { - size_t len = strlen( name ) + 1; - PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory ); - strncpy( (char *)deviceInfo->name, name, len ); - } - else - deviceInfo->name = name; - - deviceInfo->hostApi = hostApiIndex; - deviceInfo->maxInputChannels = maxInputChannels; - deviceInfo->maxOutputChannels = maxOutputChannels; - deviceInfo->defaultLowInputLatency = defaultLowInputLatency; - deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency; - deviceInfo->defaultHighInputLatency = defaultHighInputLatency; - deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency; - deviceInfo->defaultSampleRate = defaultSampleRate; - -error: - return result; -} - -static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount, - double *defaultLowLatency, double *defaultHighLatency ) -{ - PaError result = paNoError; - int numChannels, maxNumChannels; - int busy = 0; - int devHandle = -1; - int sr; - *maxChannelCount = 0; /* Default value in case this fails */ - - if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 ) - { - if( errno == EBUSY || errno == EAGAIN ) - { - PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName )); - } - else - { - PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) )); - } - - return paDeviceUnavailable; - } - - /* Negotiate for the maximum number of channels for this device. PLB20010927 - * Consider up to 16 as the upper number of channels. - * Variable maxNumChannels should contain the actual upper limit after the call. - * Thanks to John Lazzaro and Heiko Purnhagen for suggestions. - */ - maxNumChannels = 0; - for( numChannels = 1; numChannels <= 16; numChannels++ ) - { - int temp = numChannels; - if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 ) - { - busy = EAGAIN == errno || EBUSY == errno; - /* ioctl() failed so bail out if we already have stereo */ - if( maxNumChannels >= 2 ) - break; - } - else - { - /* ioctl() worked but bail out if it does not support numChannels. - * We don't want to leave gaps in the numChannels supported. - */ - if( (numChannels > 2) && (temp != numChannels) ) - break; - if( temp > maxNumChannels ) - maxNumChannels = temp; /* Save maximum. */ - } - } - /* A: We're able to open a device for capture if it's busy playing back and vice versa, - * but we can't configure anything */ - if( 0 == maxNumChannels && busy ) - { - result = paDeviceUnavailable; - goto error; - } - - /* The above negotiation may fail for an old driver so try this older technique. */ - if( maxNumChannels < 1 ) - { - int stereo = 1; - if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 ) - { - maxNumChannels = 1; - } - else - { - maxNumChannels = (stereo) ? 2 : 1; - } - PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels )) - } - - /* During channel negotiation, the last ioctl() may have failed. This can - * also cause sample rate negotiation to fail. Hence the following, to return - * to a supported number of channels. SG20011005 */ - { - /* use most reasonable default value */ - int temp = PA_MIN( maxNumChannels, 2 ); - ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError ); - } - - /* Get supported sample rate closest to 44100 Hz */ - if( *defaultSampleRate < 0 ) - { - sr = 44100; - if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 ) - { - result = paUnanticipatedHostError; - goto error; - } - - *defaultSampleRate = sr; - } - - *maxChannelCount = maxNumChannels; - /* TODO */ - *defaultLowLatency = 512. / *defaultSampleRate; - *defaultHighLatency = 2048. / *defaultSampleRate; - -error: - if( devHandle >= 0 ) - close( devHandle ); - - return result; -} - -/** Query OSS device. - * - * This is where PaDeviceInfo objects are constructed and filled in with relevant information. - * - * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed - * in place. - */ -static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo ) -{ - PaError result = paNoError; - double sampleRate = -1.; - int maxInputChannels, maxOutputChannels; - PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency; - PaError tmpRes = paNoError; - int busy = 0; - *deviceInfo = NULL; - - /* douglas: - we have to do this querying in a slightly different order. apparently - some sound cards will give you different info based on their settins. - e.g. a card might give you stereo at 22kHz but only mono at 44kHz. - the correct order for OSS is: format, channels, sample rate - */ - - /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is - * opened in, it may have more channels available for capture than playback and vice versa. Therefore - * we will open the device in both read- and write-only mode to determine the supported number. - */ - if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency, - &defaultHighInputLatency )) != paNoError ) - { - if( tmpRes != paDeviceUnavailable ) - { - PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName )); - /* PA_ENSURE( tmpRes ); */ - } - ++busy; - } - if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency, - &defaultHighOutputLatency )) != paNoError ) - { - if( tmpRes != paDeviceUnavailable ) - { - PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName )); - /* PA_ENSURE( tmpRes ); */ - } - ++busy; - } - assert( 0 <= busy && busy <= 2 ); - if( 2 == busy ) /* Both directions are unavailable to us */ - { - result = paDeviceUnavailable; - goto error; - } - - PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory ); - PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels, - defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate, - ossApi->allocations ) ); - -error: - return result; -} - -/** Query host devices. - * - * Loop over host devices and query their capabilitiesu - * - * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object - * per device, these are placed in the host api representation's deviceInfos array. - */ -static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep; - int i; - int numDevices = 0, maxDeviceInfos = 1; - PaDeviceInfo **deviceInfos = NULL; - - /* These two will be set to the first working input and output device, respectively */ - commonApi->info.defaultInputDevice = paNoDevice; - commonApi->info.defaultOutputDevice = paNoDevice; - - /* Find devices by calling QueryDevice on each - * potential device names. When we find a valid one, - * add it to a linked list. - * A: Can there only be 10 devices? */ - - for( i = 0; i < 10; i++ ) - { - char deviceName[32]; - PaDeviceInfo *deviceInfo; - int testResult; - struct stat stbuf; - - if( i == 0 ) - snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE); - else - snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i); - - /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */ - if( stat( deviceName, &stbuf ) < 0 ) - { - if( ENOENT != errno ) - PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) )); - continue; - } - if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError ) - { - if( testResult != paDeviceUnavailable ) - PA_ENSURE( testResult ); - - continue; - } - - ++numDevices; - if( !deviceInfos || numDevices > maxDeviceInfos ) - { - maxDeviceInfos *= 2; - PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ), - paInsufficientMemory ); - } - deviceInfos[numDevices - 1] = deviceInfo; - - if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 ) - commonApi->info.defaultInputDevice = i; - if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 ) - commonApi->info.defaultOutputDevice = i; - } - - /* Make an array of PaDeviceInfo pointers out of the linked list */ - - PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices)); - - commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices ); - memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) ); - - commonApi->info.deviceCount = numDevices; - -error: - free( deviceInfos ); - - return result; -} - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi; - - if( ossHostApi->allocations ) - { - PaUtil_FreeAllAllocations( ossHostApi->allocations ); - PaUtil_DestroyAllocationGroup( ossHostApi->allocations ); - } - - PaUtil_FreeMemory( ossHostApi ); -} - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaError result = paNoError; - PaDeviceIndex device; - PaDeviceInfo *deviceInfo; - char *deviceName; - int inputChannelCount, outputChannelCount; - int tempDevHandle = -1; - int flags; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - if (inputChannelCount == 0 && outputChannelCount == 0) - return paInvalidChannelCount; - - /* if full duplex, make sure that they're the same device */ - - if (inputChannelCount > 0 && outputChannelCount > 0 && - inputParameters->device != outputParameters->device) - return paInvalidDevice; - - /* if full duplex, also make sure that they're the same number of channels */ - - if (inputChannelCount > 0 && outputChannelCount > 0 && - inputChannelCount != outputChannelCount) - return paInvalidChannelCount; - - /* open the device so we can do more tests */ - - if( inputChannelCount > 0 ) - { - result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi); - if (result != paNoError) - return result; - } - else - { - result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi); - if (result != paNoError) - return result; - } - - deviceInfo = hostApi->deviceInfos[device]; - deviceName = (char *)deviceInfo->name; - - flags = O_NONBLOCK; - if (inputChannelCount > 0 && outputChannelCount > 0) - flags |= O_RDWR; - else if (inputChannelCount > 0) - flags |= O_RDONLY; - else - flags |= O_WRONLY; - - ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable ); - - /* PaOssStream_Configure will do the rest of the checking for us */ - /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */ - - /* everything succeeded! */ - - error: - if( tempDevHandle >= 0 ) - close( tempDevHandle ); - - return result; -} - -/** Validate stream parameters. - * - * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device - */ -static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode ) -{ - int maxChans; - - assert( parameters ); - - if( parameters->device == paUseHostApiSpecificDeviceSpecification ) - { - return paInvalidDevice; - } - - maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels : - deviceInfo->maxOutputChannels); - if( parameters->channelCount > maxChans ) - { - return paInvalidChannelCount; - } - - return paNoError; -} - -static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters, - int callbackMode, int fd, const char *deviceName ) -{ - PaError result = paNoError; - assert( component ); - - memset( component, 0, sizeof (PaOssStreamComponent) ); - - component->fd = fd; - component->devName = deviceName; - component->userChannelCount = parameters->channelCount; - component->userFormat = parameters->sampleFormat; - component->latency = parameters->suggestedLatency; - component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved); - - if( !callbackMode && !component->userInterleaved ) - { - /* Pre-allocate non-interleaved user provided buffers */ - PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ), - paInsufficientMemory ); - } - -error: - return result; -} - -static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component ) -{ - assert( component ); - - if( component->fd >= 0 ) - close( component->fd ); - if( component->buffer ) - PaUtil_FreeMemory( component->buffer ); - - if( component->userBuffers ) - PaUtil_FreeMemory( component->userBuffers ); - - PaUtil_FreeMemory( component ); -} - -static PaError ModifyBlocking( int fd, int blocking ) -{ - PaError result = paNoError; - int fflags; - - ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError ); - - if( blocking ) - fflags &= ~O_NONBLOCK; - else - fflags |= O_NONBLOCK; - - ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError ); - -error: - return result; -} - -static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev ) -{ - PaError result = paNoError; - int flags = O_NONBLOCK, duplex = 0; - int enableBits = 0; - *idev = *odev = -1; - - if( idevName && odevName ) - { - duplex = 1; - flags |= O_RDWR; - } - else if( idevName ) - flags |= O_RDONLY; - else - flags |= O_WRONLY; - - /* open first in nonblocking mode, in case it's busy... - * A: then unset the non-blocking attribute */ - assert( flags & O_NONBLOCK ); - if( idevName ) - { - ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable ); - PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */ - - /* Initially disable */ - enableBits = ~PCM_ENABLE_INPUT; - ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - if( odevName ) - { - if( !idevName ) - { - ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable ); - PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */ - - /* Initially disable */ - enableBits = ~PCM_ENABLE_OUTPUT; - ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - else - { - ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError ); - } - } - -error: - return result; -} - -static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, - PaStreamCallback callback, void *userData, PaStreamFlags streamFlags, - PaOSSHostApiRepresentation *ossApi ) -{ - PaError result = paNoError; - int idev, odev; - PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep; - const char *idevName = NULL, *odevName = NULL; - - assert( stream ); - - memset( stream, 0, sizeof (PaOssStream) ); - stream->isStopped = 1; - - PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) ); - - if( inputParameters && outputParameters ) - { - if( inputParameters->device == outputParameters->device ) - stream->sharedDevice = 1; - } - - if( inputParameters ) - idevName = hostApi->deviceInfos[inputParameters->device]->name; - if( outputParameters ) - odevName = hostApi->deviceInfos[outputParameters->device]->name; - PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) ); - if( inputParameters ) - { - PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory ); - PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) ); - } - if( outputParameters ) - { - PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory ); - PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) ); - } - - if( callback != NULL ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &ossApi->callbackStreamInterface, callback, userData ); - stream->callbackMode = 1; - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &ossApi->blockingStreamInterface, callback, userData ); - } - - ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError ); - -error: - return result; -} - -static void PaOssStream_Terminate( PaOssStream *stream ) -{ - assert( stream ); - - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_TerminateThreading( &stream->threading ); - - if( stream->capture ) - PaOssStreamComponent_Terminate( stream->capture ); - if( stream->playback ) - PaOssStreamComponent_Terminate( stream->playback ); - - sem_destroy( &stream->semaphore ); - - PaUtil_FreeMemory( stream ); -} - -/** Translate from PA format to OSS native. - * - */ -static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat ) -{ - switch( paFormat ) - { - case paUInt8: - *ossFormat = AFMT_U8; - break; - case paInt8: - *ossFormat = AFMT_S8; - break; - case paInt16: - *ossFormat = AFMT_S16_NE; - break; - default: - return paInternalError; /* This shouldn't happen */ - } - - return paNoError; -} - -/** Return the PA-compatible formats that this device can support. - * - */ -static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats ) -{ - PaError result = paNoError; - int mask = 0; - PaSampleFormat frmts = 0; - - ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError ); - if( mask & AFMT_U8 ) - frmts |= paUInt8; - if( mask & AFMT_S8 ) - frmts |= paInt8; - if( mask & AFMT_S16_NE ) - frmts |= paInt16; - else - result = paSampleFormatNotSupported; - - *availableFormats = frmts; - -error: - return result; -} - -static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component ) -{ - return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount; -} - -/** Buffer size in bytes. - * - */ -static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component ) -{ - return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs; -} - -static int CalcHigherLogTwo( int n ) -{ - int log2 = 0; - while( (1<userChannelCount; - int frgmt; - int numBufs; - int bytesPerBuf; - double bufSz; - unsigned long fragSz; - audio_buf_info bufInfo; - - /* We may have a situation where only one component (the master) is configured, if both point to the same device. - * In that case, the second component will copy settings from the other */ - if( !master ) - { - /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size. - * The hardware need not respect the requested fragment size, so we may have to adapt. - */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) - { - bufSz = component->latency * sampleRate; - fragSz = bufSz / 4; - } - else - { - fragSz = framesPerBuffer; - bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */ - } - - PA_ENSURE( GetAvailableFormats( component, &availableFormats ) ); - hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat ); - - /* OSS demands at least 2 buffers, and 16 bytes per buffer */ - numBufs = PA_MAX( bufSz / fragSz, 2 ); - bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 ); - - /* The fragment parameters are encoded like this: - * Most significant byte: number of fragments - * Least significant byte: exponent of fragment size (i.e., for 256, 8) - */ - frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff); - ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError ); - - /* A: according to the OSS programmer's guide parameters should be set in this order: - * format, channels, rate */ - - /* This format should be deemed good before we get this far */ - PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) ); - nativeFormat = temp; - ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError ); - PA_UNLESS( temp == nativeFormat, paInternalError ); - - /* try to set the number of channels */ - ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */ - /* It's possible that the minimum number of host channels is greater than what the user requested */ - PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount ); - - /* try to set the sample rate */ - ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate ); - - /* reject if there's no sample rate within 1% of the one requested */ - if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 ) - { - PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr )); - PA_ENSURE( paInvalidSampleRate ); - } - - ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ), - paUnanticipatedHostError ); - component->numBufs = bufInfo.fragstotal; - - /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */ - ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError ); - - component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans; - component->hostChannelCount = chans; - component->hostFormat = hostFormat; - } - else - { - component->hostFormat = master->hostFormat; - component->hostFrames = master->hostFrames; - component->hostChannelCount = master->hostChannelCount; - component->numBufs = master->numBufs; - } - - PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ), - paInsufficientMemory ); - -error: - return result; -} - -static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames ) -{ - PaError result = paNoError; - size_t len = *frames * PaOssStreamComponent_FrameSize( component ); - ssize_t bytesRead; - - ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError ); - *frames = bytesRead / PaOssStreamComponent_FrameSize( component ); - -error: - return result; -} - -static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames ) -{ - PaError result = paNoError; - size_t len = *frames * PaOssStreamComponent_FrameSize( component ); - ssize_t bytesWritten; - - ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError ); - *frames = bytesWritten / PaOssStreamComponent_FrameSize( component ); - -error: - return result; -} - -/** Configure the stream according to input/output parameters. - * - * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by - * the user, if so we'll record the actual number of host channels and adapt later. - */ -static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer, - double *inputLatency, double *outputLatency ) -{ - PaError result = paNoError; - int duplex = stream->capture && stream->playback; - unsigned long framesPerHostBuffer = 0; - - /* We should request full duplex first thing after opening the device */ - if( duplex && stream->sharedDevice ) - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError ); - - if( stream->capture ) - { - PaOssStreamComponent *component = stream->capture; - PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL ); - - assert( component->hostChannelCount > 0 ); - assert( component->hostFrames > 0 ); - - *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; - } - if( stream->playback ) - { - PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL; - PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out, - master ) ); - - assert( component->hostChannelCount > 0 ); - assert( component->hostFrames > 0 ); - - *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; - } - - if( duplex ) - framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames ); - else if( stream->capture ) - framesPerHostBuffer = stream->capture->hostFrames; - else if( stream->playback ) - framesPerHostBuffer = stream->playback->hostFrames; - - stream->framesPerHostBuffer = framesPerHostBuffer; - stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */ - - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - -error: - return result; -} - -/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ - -/** Open a PA OSS stream. - * - * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the - * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both - * directions are the same device we will demand the same number of channels. The number of channels can range - * from 1 to the maximum supported by the device. - * - * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback - * must reflect this, in addition the host latency per device should approximate the corresponding - * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that - * both capture and playback can agree on (they can be different devices), the buffer processor can adapt - * between host and user buffer size, but the ratio should preferably be integral. - */ -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi; - PaOssStream *stream = NULL; - int inputChannelCount = 0, outputChannelCount = 0; - PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0; - const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0; - int bpInitialized = 0; - double inLatency, outLatency; - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - if( inputParameters ) - { - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - inputDeviceInfo = hostApi->deviceInfos[inputParameters->device]; - PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) ); - - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - } - if( outputParameters ) - { - outputDeviceInfo = hostApi->deviceInfos[outputParameters->device]; - PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) ); - - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - } - - /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same - * device is opened for both directions - */ - if( inputChannelCount > 0 && outputChannelCount > 0 ) - { - if( inputParameters->device == outputParameters->device ) - { - if( inputParameters->channelCount != outputParameters->channelCount ) - return paInvalidChannelCount; - } - } - - /* allocate and do basic initialization of the stream structure */ - PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory ); - PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ); - - PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) ); - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - if( inputParameters ) - { - inputHostFormat = stream->capture->hostFormat; - stream->streamRepresentation.streamInfo.inputLatency = inLatency + - PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate; - } - if( outputParameters ) - { - outputHostFormat = stream->playback->hostFormat; - stream->streamRepresentation.streamInfo.outputLatency = outLatency + - PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate; - } - - /* Initialize buffer processor with fixed host buffer size. - * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will - * convert between the two. - */ - PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat, - outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer, - paUtilFixedHostBufferSize, streamCallback, userData ) ); - bpInitialized = 1; - - *s = (PaStream*)stream; - - return result; - -error: - if( bpInitialized ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - if( stream ) - PaOssStream_Terminate( stream ); - - return result; -} - -/*! Poll on I/O filedescriptors. - - Poll till we've determined there's data for read or write. In the full-duplex case, - we don't want to hang around forever waiting for either input or output frames, so - whenever we have a timed out filedescriptor we check if we're nearing under/overrun - for the other direction (critical limit set at one buffer). If so, we exit the waiting - state, and go on with what we got. We align the number of frames on a host buffer - boundary because it is possible that the buffer size differs for the two directions and - the host buffer size is a compromise between the two. - */ -static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames ) -{ - PaError result = paNoError; - int pollPlayback = 0, pollCapture = 0; - int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail; - audio_buf_info bufInfo; - /* int ofs = 0, nfds = stream->nfds; */ - fd_set readFds, writeFds; - int nfds = 0; - struct timeval selectTimeval = {0, 0}; - unsigned long timeout = stream->pollTimeout; /* In usecs */ - int captureFd = -1, playbackFd = -1; - - assert( stream ); - assert( frames ); - - if( stream->capture ) - { - pollCapture = 1; - captureFd = stream->capture->fd; - /* stream->capture->pfd->events = POLLIN; */ - } - if( stream->playback ) - { - pollPlayback = 1; - playbackFd = stream->playback->fd; - /* stream->playback->pfd->events = POLLOUT; */ - } - - FD_ZERO( &readFds ); - FD_ZERO( &writeFds ); - - while( pollPlayback || pollCapture ) - { - pthread_testcancel(); - - /* select may modify the timeout parameter */ - selectTimeval.tv_usec = timeout; - nfds = 0; - - if( pollCapture ) - { - FD_SET( captureFd, &readFds ); - nfds = captureFd + 1; - } - if( pollPlayback ) - { - FD_SET( playbackFd, &writeFds ); - nfds = PA_MAX( nfds, playbackFd + 1 ); - } - ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError ); - /* - if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 ) - { - - ENSURE_( -1, paUnanticipatedHostError ); - } - */ - pthread_testcancel(); - - if( pollCapture ) - { - if( FD_ISSET( captureFd, &readFds ) ) - { - FD_CLR( captureFd, &readFds ); - pollCapture = 0; - } - /* - if( stream->capture->pfd->revents & POLLIN ) - { - --nfds; - ++ofs; - pollCapture = 0; - } - */ - else if( stream->playback ) /* Timed out, go on with playback? */ - { - /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n", - __FUNCTION__, stream->pollTimeout ));*/ - } - } - if( pollPlayback ) - { - if( FD_ISSET( playbackFd, &writeFds ) ) - { - FD_CLR( playbackFd, &writeFds ); - pollPlayback = 0; - } - /* - if( stream->playback->pfd->revents & POLLOUT ) - { - --nfds; - pollPlayback = 0; - } - */ - else if( stream->capture ) /* Timed out, go on with capture? */ - { - /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n", - __FUNCTION__, stream->pollTimeout ));*/ - } - } - } - - if( stream->capture ) - { - ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError ); - captureAvail = bufInfo.fragments * stream->capture->hostFrames; - if( !captureAvail ) - PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ )); - - captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */ - } - if( stream->playback ) - { - ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError ); - playbackAvail = bufInfo.fragments * stream->playback->hostFrames; - if( !playbackAvail ) - { - PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ )); - } - - playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */ - } - - commonAvail = PA_MIN( captureAvail, playbackAvail ); - if( commonAvail == INT_MAX ) - commonAvail = 0; - commonAvail -= commonAvail % stream->framesPerHostBuffer; - - assert( commonAvail != INT_MAX ); - assert( commonAvail >= 0 ); - *frames = commonAvail; - -error: - return result; -} - -/** Prepare stream for capture/playback. - * - * In order to synchronize capture and playback properly we use the SETTRIGGER command. - */ -static PaError PaOssStream_Prepare( PaOssStream *stream ) -{ - PaError result = paNoError; - int enableBits = 0; - - if( stream->triggered ) - return result; - - if( stream->playback ) - { - size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback ); - memset( stream->playback->buffer, 0, bufSz ); - - /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer - * OSS will complain. */ - PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); - while (1) - { - if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 ) - break; - } - PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) ); - } - - if( stream->sharedDevice ) - { - enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - else - { - if( stream->capture ) - { - enableBits = PCM_ENABLE_INPUT; - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - if( stream->playback ) - { - enableBits = PCM_ENABLE_OUTPUT; - ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); - } - } - - /* Ok, we have triggered the stream */ - stream->triggered = 1; - -error: - return result; -} - -/** Stop audio processing - * - */ -static PaError PaOssStream_Stop( PaOssStream *stream, int abort ) -{ - PaError result = paNoError; - - /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST. - * Also disable capture/playback till the stream is started again */ - if( stream->capture ) - { - ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError ); - } - if( stream->playback && !stream->sharedDevice ) - { - ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError ); - } - -error: - return result; -} - -/** Clean up after thread exit. - * - * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here - */ -static void OnExit( void *data ) -{ - PaOssStream *stream = (PaOssStream *) data; - assert( data ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - PaOssStream_Stop( stream, stream->callbackAbort ); - - PA_DEBUG(( "OnExit: Stoppage\n" )); - - /* Eventually notify user all buffers have played */ - if( stream->streamRepresentation.streamFinishedCallback ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - - stream->callbackAbort = 0; /* Clear state */ - stream->isActive = 0; -} - -static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail ) -{ - PaError result = paNoError; - - if( stream->capture ) - { - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, - stream->capture->hostChannelCount ); - PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail ); - } - if( stream->playback ) - { - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, - stream->playback->hostChannelCount ); - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail ); - } - - return result; -} - -/** Thread procedure for callback processing. - * - * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the - * callback should be used for buffer priming. When the stream is cancelled a separate function will - * take care of the transition to the Callback Finished state (the stream isn't considered Stopped - * before StopStream() or AbortStream() are called). - */ -static void *PaOSS_AudioThreadProc( void *userData ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)userData; - unsigned long framesAvail, framesProcessed; - int callbackResult = paContinue; - int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */ - int initiateProcessing = triggered; /* Already triggered? */ - PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */ - - /* -#if ( SOUND_VERSION > 0x030904 ) - audio_errinfo errinfo; -#endif -*/ - - assert( stream ); - - pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */ - - /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and - * playback in sync, when the stream is restarted after being stopped we simply start by reading/ - * writing. - */ - PA_ENSURE( PaOssStream_Prepare( stream ) ); - - /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */ - if( initiateProcessing ) - { - /* Make sure devices are in blocking mode */ - if( stream->capture ) - ModifyBlocking( stream->capture->fd, 1 ); - if( stream->playback ) - ModifyBlocking( stream->playback->fd, 1 ); - } - - while( 1 ) - { - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */ - - pthread_testcancel(); - - if( stream->callbackStop && callbackResult == paContinue ) - { - PA_DEBUG(( "Setting callbackResult to paComplete\n" )); - callbackResult = paComplete; - } - - /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless - * the stream has been recently started, we will have to go right ahead and read/write in blocking - * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch - * to non-blocking mode. - */ - if( !initiateProcessing ) - { - PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */ - assert( framesAvail % stream->framesPerHostBuffer == 0 ); - } - else - { - framesAvail = stream->framesPerHostBuffer; - } - - while( framesAvail > 0 ) - { - unsigned long frames = framesAvail; - - pthread_testcancel(); - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* Read data */ - if ( stream->capture ) - { - PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) ); - assert( frames == framesAvail ); - } - -#if ( SOUND_VERSION >= 0x030904 ) - /* - Check with OSS to see if there have been any under/overruns - since last time we checked. - */ - /* - if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 ) - { - if( errinfo.play_underruns ) - cbFlags |= paOutputUnderflow ; - if( errinfo.record_underruns ) - cbFlags |= paInputUnderflow ; - } - else - PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) )); - */ -#endif - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, - cbFlags ); - cbFlags = 0; - PA_ENSURE( SetUpBuffers( stream, framesAvail ) ); - - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, - &callbackResult ); - assert( framesProcessed == framesAvail ); - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if ( stream->playback ) - { - frames = framesAvail; - - PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) ); - assert( frames == framesAvail ); - - /* TODO: handle bytesWritten != bytesRequested (slippage?) */ - } - - framesAvail -= framesProcessed; - stream->framesProcessed += framesProcessed; - - if( callbackResult != paContinue ) - break; - } - - if( initiateProcessing || !triggered ) - { - /* Non-blocking */ - if( stream->capture ) - PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) ); - if( stream->playback && !stream->sharedDevice ) - PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); - - initiateProcessing = 0; - sem_post( &stream->semaphore ); - } - - if( callbackResult != paContinue ) - { - stream->callbackAbort = callbackResult == paAbort; - if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) - break; - } - } - - pthread_cleanup_pop( 1 ); - -error: - pthread_exit( NULL ); -} - -/** Close the stream. - * - */ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - assert( stream ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaOssStream_Terminate( stream ); - - return result; -} - -/** Start the stream. - * - * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual - * callback will be repeatedly called in a separate thread. If a separate thread is started this function - * will block untill it has started processing audio, otherwise audio processing is started directly. - */ -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaOssStream *stream = (PaOssStream*)s; - - stream->isActive = 1; - stream->isStopped = 0; - stream->lastPosPtr = 0; - stream->lastStreamBytes = 0; - stream->framesProcessed = 0; - - /* only use the thread for callback streams */ - if( stream->bufferProcessor.streamCallback ) - { - PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) ); - sem_wait( &stream->semaphore ); - } - else - PA_ENSURE( PaOssStream_Prepare( stream ) ); - -error: - return result; -} - -static PaError RealStop( PaOssStream *stream, int abort ) -{ - PaError result = paNoError; - - if( stream->callbackMode ) - { - if( abort ) - stream->callbackAbort = 1; - else - stream->callbackStop = 1; - - PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) ); - - stream->callbackStop = stream->callbackAbort = 0; - } - else - PA_ENSURE( PaOssStream_Stop( stream, abort ) ); - - stream->isStopped = 1; - -error: - return result; -} - -/** Stop the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued - * buffers. - */ -static PaError StopStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 0 ); -} - -/** Abort the stream. - * - * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued - * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing - * the OSS device. - */ -static PaError AbortStream( PaStream *s ) -{ - return RealStop( (PaOssStream *)s, 1 ); -} - -/** Is the stream in the Stopped state. - * - */ -static PaError IsStreamStopped( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return (stream->isStopped); -} - -/** Is the stream in the Active state. - * - */ -static PaError IsStreamActive( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return (stream->isActive); -} - -static PaTime GetStreamTime( PaStream *s ) -{ - PaOssStream *stream = (PaOssStream*)s; - count_info info; - int delta; - - if( stream->playback ) { - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) { - delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF; - return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate; - } - } - else { - if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) { - delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF; - return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate; - } - } - - /* the ioctl failed, but we can still give a coarse estimate */ - - return stream->framesProcessed / stream->sampleRate; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaOssStream *stream = (PaOssStream*)s; - int bytesRequested, bytesRead; - unsigned long framesRequested; - void *userBuffer; - - /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userInputIsInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->capture->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount ); - } - - while( frames ) - { - framesRequested = PA_MIN( frames, stream->capture->hostFrames ); - - bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture ); - bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested ); - if ( bytesRequested != bytesRead ) - return paUnanticipatedHostError; - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount ); - PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested ); - frames -= framesRequested; - } - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaOssStream *stream = (PaOssStream*)s; - int bytesRequested, bytesWritten; - unsigned long framesConverted; - const void *userBuffer; - - /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers, - * so we copy the user provided pointers */ - if( stream->bufferProcessor.userOutputIsInterleaved ) - userBuffer = buffer; - else /* Copy channels into local array */ - { - userBuffer = stream->playback->userBuffers; - memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount ); - } - - while( frames ) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames ); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount ); - - framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames ); - frames -= framesConverted; - - bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback ); - bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested ); - - if ( bytesRequested != bytesWritten ) - return paUnanticipatedHostError; - } - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - audio_buf_info info; - - if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 ) - return paUnanticipatedHostError; - return info.fragments * stream->capture->hostFrames; -} - - -/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaOssStream *stream = (PaOssStream*)s; - int delay = 0; - - if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 ) - return paUnanticipatedHostError; - - return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback ); -} - +/* + * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * OSS implementation by: + * Douglas Repetto + * Phil Burk + * Dominic Mazzoni + * Arve Knudsen + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +# include +# define DEVICE_NAME_BASE "/dev/dsp" +#else +# include /* JH20010905 */ +# define DEVICE_NAME_BASE "/dev/audio" +#endif + +#include "portaudio.h" +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" +#include "pa_unix_util.h" + +static int sysErr_; +static pthread_t mainThread_; + +/* Check return value of system call, and map it to PaError */ +#define ENSURE_(expr, code) \ + do { \ + if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \ + { \ + /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ + if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ + { \ + PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \ + } \ + \ + PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + result = (code); \ + goto error; \ + } \ + } while( 0 ); + +#ifndef AFMT_S16_NE +#define AFMT_S16_NE Get_AFMT_S16_NE() +/********************************************************************* + * Some versions of OSS do not define AFMT_S16_NE. So check CPU. + * PowerPC is Big Endian. X86 is Little Endian. + */ +static int Get_AFMT_S16_NE( void ) +{ + long testData = 1; + char *ptr = (char *) &testData; + int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */ + return isLittle ? AFMT_S16_LE : AFMT_S16_BE; +} +#endif + +/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + PaHostApiIndex hostApiIndex; +} +PaOSSHostApiRepresentation; + +/** Per-direction structure for PaOssStream. + * + * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback, + * but with different number of channels we will have to adapt between the number of user and host + * channels for at least one direction, since the configuration space is the same for both directions + * of an OSS device. + */ +typedef struct +{ + int fd; + const char *devName; + int userChannelCount, hostChannelCount; + int userInterleaved; + void *buffer; + PaSampleFormat userFormat, hostFormat; + double latency; + unsigned long hostFrames, numBufs; + void **userBuffers; /* For non-interleaved blocking */ +} PaOssStreamComponent; + +/** Implementation specific representation of a PaStream. + * + */ +typedef struct PaOssStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + PaUtilThreading threading; + + int sharedDevice; + unsigned long framesPerHostBuffer; + int triggered; /* Have the devices been triggered yet (first start) */ + + int isActive; + int isStopped; + + int lastPosPtr; + double lastStreamBytes; + + int framesProcessed; + + double sampleRate; + + int callbackMode; + int callbackStop, callbackAbort; + + PaOssStreamComponent *capture, *playback; + unsigned long pollTimeout; + sem_t semaphore; +} +PaOssStream; + +typedef enum { + StreamMode_In, + StreamMode_Out +} StreamMode; + +/* prototypes for functions declared in this file */ + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); +static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi ); + + +/** Initialize the OSS API implementation. + * + * This function will initialize host API datastructures and query host devices for information. + * + * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here + * + * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function, + * this happens with the usual "error" label. + */ +PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + PaOSSHostApiRepresentation *ossHostApi = NULL; + + PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ), + paInsufficientMemory ); + PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); + ossHostApi->hostApiIndex = hostApiIndex; + + /* Initialize host API structure */ + *hostApi = &ossHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paOSS; + (*hostApi)->info.name = "OSS"; + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PA_ENSURE( BuildDeviceList( ossHostApi ) ); + + PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyRead, PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, + PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + mainThread_ = pthread_self(); + + return result; + +error: + if( ossHostApi ) + { + if( ossHostApi->allocations ) + { + PaUtil_FreeAllAllocations( ossHostApi->allocations ); + PaUtil_DestroyAllocationGroup( ossHostApi->allocations ); + } + + PaUtil_FreeMemory( ossHostApi ); + } + return result; +} + +PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels, + int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency, + PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations ) +{ + PaError result = paNoError; + + deviceInfo->structVersion = 2; + if( allocations ) + { + size_t len = strlen( name ) + 1; + PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory ); + strncpy( (char *)deviceInfo->name, name, len ); + } + else + deviceInfo->name = name; + + deviceInfo->hostApi = hostApiIndex; + deviceInfo->maxInputChannels = maxInputChannels; + deviceInfo->maxOutputChannels = maxOutputChannels; + deviceInfo->defaultLowInputLatency = defaultLowInputLatency; + deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency; + deviceInfo->defaultHighInputLatency = defaultHighInputLatency; + deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency; + deviceInfo->defaultSampleRate = defaultSampleRate; + +error: + return result; +} + +static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount, + double *defaultLowLatency, double *defaultHighLatency ) +{ + PaError result = paNoError; + int numChannels, maxNumChannels; + int busy = 0; + int devHandle = -1; + int sr; + *maxChannelCount = 0; /* Default value in case this fails */ + + if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 ) + { + if( errno == EBUSY || errno == EAGAIN ) + { + PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName )); + } + else + { + PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) )); + } + + return paDeviceUnavailable; + } + + /* Negotiate for the maximum number of channels for this device. PLB20010927 + * Consider up to 16 as the upper number of channels. + * Variable maxNumChannels should contain the actual upper limit after the call. + * Thanks to John Lazzaro and Heiko Purnhagen for suggestions. + */ + maxNumChannels = 0; + for( numChannels = 1; numChannels <= 16; numChannels++ ) + { + int temp = numChannels; + if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 ) + { + busy = EAGAIN == errno || EBUSY == errno; + /* ioctl() failed so bail out if we already have stereo */ + if( maxNumChannels >= 2 ) + break; + } + else + { + /* ioctl() worked but bail out if it does not support numChannels. + * We don't want to leave gaps in the numChannels supported. + */ + if( (numChannels > 2) && (temp != numChannels) ) + break; + if( temp > maxNumChannels ) + maxNumChannels = temp; /* Save maximum. */ + } + } + /* A: We're able to open a device for capture if it's busy playing back and vice versa, + * but we can't configure anything */ + if( 0 == maxNumChannels && busy ) + { + result = paDeviceUnavailable; + goto error; + } + + /* The above negotiation may fail for an old driver so try this older technique. */ + if( maxNumChannels < 1 ) + { + int stereo = 1; + if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 ) + { + maxNumChannels = 1; + } + else + { + maxNumChannels = (stereo) ? 2 : 1; + } + PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels )) + } + + /* During channel negotiation, the last ioctl() may have failed. This can + * also cause sample rate negotiation to fail. Hence the following, to return + * to a supported number of channels. SG20011005 */ + { + /* use most reasonable default value */ + int temp = PA_MIN( maxNumChannels, 2 ); + ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError ); + } + + /* Get supported sample rate closest to 44100 Hz */ + if( *defaultSampleRate < 0 ) + { + sr = 44100; + if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 ) + { + result = paUnanticipatedHostError; + goto error; + } + + *defaultSampleRate = sr; + } + + *maxChannelCount = maxNumChannels; + /* TODO */ + *defaultLowLatency = 512. / *defaultSampleRate; + *defaultHighLatency = 2048. / *defaultSampleRate; + +error: + if( devHandle >= 0 ) + close( devHandle ); + + return result; +} + +/** Query OSS device. + * + * This is where PaDeviceInfo objects are constructed and filled in with relevant information. + * + * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed + * in place. + */ +static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo ) +{ + PaError result = paNoError; + double sampleRate = -1.; + int maxInputChannels, maxOutputChannels; + PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency; + PaError tmpRes = paNoError; + int busy = 0; + *deviceInfo = NULL; + + /* douglas: + we have to do this querying in a slightly different order. apparently + some sound cards will give you different info based on their settins. + e.g. a card might give you stereo at 22kHz but only mono at 44kHz. + the correct order for OSS is: format, channels, sample rate + */ + + /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is + * opened in, it may have more channels available for capture than playback and vice versa. Therefore + * we will open the device in both read- and write-only mode to determine the supported number. + */ + if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency, + &defaultHighInputLatency )) != paNoError ) + { + if( tmpRes != paDeviceUnavailable ) + { + PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName )); + /* PA_ENSURE( tmpRes ); */ + } + ++busy; + } + if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency, + &defaultHighOutputLatency )) != paNoError ) + { + if( tmpRes != paDeviceUnavailable ) + { + PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName )); + /* PA_ENSURE( tmpRes ); */ + } + ++busy; + } + assert( 0 <= busy && busy <= 2 ); + if( 2 == busy ) /* Both directions are unavailable to us */ + { + result = paDeviceUnavailable; + goto error; + } + + PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory ); + PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels, + defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate, + ossApi->allocations ) ); + +error: + return result; +} + +/** Query host devices. + * + * Loop over host devices and query their capabilitiesu + * + * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object + * per device, these are placed in the host api representation's deviceInfos array. + */ +static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi ) +{ + PaError result = paNoError; + PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep; + int i; + int numDevices = 0, maxDeviceInfos = 1; + PaDeviceInfo **deviceInfos = NULL; + + /* These two will be set to the first working input and output device, respectively */ + commonApi->info.defaultInputDevice = paNoDevice; + commonApi->info.defaultOutputDevice = paNoDevice; + + /* Find devices by calling QueryDevice on each + * potential device names. When we find a valid one, + * add it to a linked list. + * A: Can there only be 10 devices? */ + + for( i = 0; i < 10; i++ ) + { + char deviceName[32]; + PaDeviceInfo *deviceInfo; + int testResult; + struct stat stbuf; + + if( i == 0 ) + snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE); + else + snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i); + + /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */ + if( stat( deviceName, &stbuf ) < 0 ) + { + if( ENOENT != errno ) + PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) )); + continue; + } + if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError ) + { + if( testResult != paDeviceUnavailable ) + PA_ENSURE( testResult ); + + continue; + } + + ++numDevices; + if( !deviceInfos || numDevices > maxDeviceInfos ) + { + maxDeviceInfos *= 2; + PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ), + paInsufficientMemory ); + } + deviceInfos[numDevices - 1] = deviceInfo; + + if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 ) + commonApi->info.defaultInputDevice = i; + if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 ) + commonApi->info.defaultOutputDevice = i; + } + + /* Make an array of PaDeviceInfo pointers out of the linked list */ + + PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices)); + + commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices ); + memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) ); + + commonApi->info.deviceCount = numDevices; + +error: + free( deviceInfos ); + + return result; +} + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi; + + if( ossHostApi->allocations ) + { + PaUtil_FreeAllAllocations( ossHostApi->allocations ); + PaUtil_DestroyAllocationGroup( ossHostApi->allocations ); + } + + PaUtil_FreeMemory( ossHostApi ); +} + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + PaError result = paNoError; + PaDeviceIndex device; + PaDeviceInfo *deviceInfo; + char *deviceName; + int inputChannelCount, outputChannelCount; + int tempDevHandle = -1; + int flags; + PaSampleFormat inputSampleFormat, outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + } + + if (inputChannelCount == 0 && outputChannelCount == 0) + return paInvalidChannelCount; + + /* if full duplex, make sure that they're the same device */ + + if (inputChannelCount > 0 && outputChannelCount > 0 && + inputParameters->device != outputParameters->device) + return paInvalidDevice; + + /* if full duplex, also make sure that they're the same number of channels */ + + if (inputChannelCount > 0 && outputChannelCount > 0 && + inputChannelCount != outputChannelCount) + return paInvalidChannelCount; + + /* open the device so we can do more tests */ + + if( inputChannelCount > 0 ) + { + result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi); + if (result != paNoError) + return result; + } + else + { + result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi); + if (result != paNoError) + return result; + } + + deviceInfo = hostApi->deviceInfos[device]; + deviceName = (char *)deviceInfo->name; + + flags = O_NONBLOCK; + if (inputChannelCount > 0 && outputChannelCount > 0) + flags |= O_RDWR; + else if (inputChannelCount > 0) + flags |= O_RDONLY; + else + flags |= O_WRONLY; + + ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable ); + + /* PaOssStream_Configure will do the rest of the checking for us */ + /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */ + + /* everything succeeded! */ + + error: + if( tempDevHandle >= 0 ) + close( tempDevHandle ); + + return result; +} + +/** Validate stream parameters. + * + * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device + */ +static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode ) +{ + int maxChans; + + assert( parameters ); + + if( parameters->device == paUseHostApiSpecificDeviceSpecification ) + { + return paInvalidDevice; + } + + maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels : + deviceInfo->maxOutputChannels); + if( parameters->channelCount > maxChans ) + { + return paInvalidChannelCount; + } + + return paNoError; +} + +static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters, + int callbackMode, int fd, const char *deviceName ) +{ + PaError result = paNoError; + assert( component ); + + memset( component, 0, sizeof (PaOssStreamComponent) ); + + component->fd = fd; + component->devName = deviceName; + component->userChannelCount = parameters->channelCount; + component->userFormat = parameters->sampleFormat; + component->latency = parameters->suggestedLatency; + component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved); + + if( !callbackMode && !component->userInterleaved ) + { + /* Pre-allocate non-interleaved user provided buffers */ + PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ), + paInsufficientMemory ); + } + +error: + return result; +} + +static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component ) +{ + assert( component ); + + if( component->fd >= 0 ) + close( component->fd ); + if( component->buffer ) + PaUtil_FreeMemory( component->buffer ); + + if( component->userBuffers ) + PaUtil_FreeMemory( component->userBuffers ); + + PaUtil_FreeMemory( component ); +} + +static PaError ModifyBlocking( int fd, int blocking ) +{ + PaError result = paNoError; + int fflags; + + ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError ); + + if( blocking ) + fflags &= ~O_NONBLOCK; + else + fflags |= O_NONBLOCK; + + ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError ); + +error: + return result; +} + +static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev ) +{ + PaError result = paNoError; + int flags = O_NONBLOCK, duplex = 0; + int enableBits = 0; + *idev = *odev = -1; + + if( idevName && odevName ) + { + duplex = 1; + flags |= O_RDWR; + } + else if( idevName ) + flags |= O_RDONLY; + else + flags |= O_WRONLY; + + /* open first in nonblocking mode, in case it's busy... + * A: then unset the non-blocking attribute */ + assert( flags & O_NONBLOCK ); + if( idevName ) + { + ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable ); + PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */ + + /* Initially disable */ + enableBits = ~PCM_ENABLE_INPUT; + ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); + } + if( odevName ) + { + if( !idevName ) + { + ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable ); + PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */ + + /* Initially disable */ + enableBits = ~PCM_ENABLE_OUTPUT; + ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); + } + else + { + ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError ); + } + } + +error: + return result; +} + +static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, + PaStreamCallback callback, void *userData, PaStreamFlags streamFlags, + PaOSSHostApiRepresentation *ossApi ) +{ + PaError result = paNoError; + int idev, odev; + PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep; + const char *idevName = NULL, *odevName = NULL; + + assert( stream ); + + memset( stream, 0, sizeof (PaOssStream) ); + stream->isStopped = 1; + + PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) ); + + if( inputParameters && outputParameters ) + { + if( inputParameters->device == outputParameters->device ) + stream->sharedDevice = 1; + } + + if( inputParameters ) + idevName = hostApi->deviceInfos[inputParameters->device]->name; + if( outputParameters ) + odevName = hostApi->deviceInfos[outputParameters->device]->name; + PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) ); + if( inputParameters ) + { + PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory ); + PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) ); + } + if( outputParameters ) + { + PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory ); + PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) ); + } + + if( callback != NULL ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &ossApi->callbackStreamInterface, callback, userData ); + stream->callbackMode = 1; + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &ossApi->blockingStreamInterface, callback, userData ); + } + + ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError ); + +error: + return result; +} + +static void PaOssStream_Terminate( PaOssStream *stream ) +{ + assert( stream ); + + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_TerminateThreading( &stream->threading ); + + if( stream->capture ) + PaOssStreamComponent_Terminate( stream->capture ); + if( stream->playback ) + PaOssStreamComponent_Terminate( stream->playback ); + + sem_destroy( &stream->semaphore ); + + PaUtil_FreeMemory( stream ); +} + +/** Translate from PA format to OSS native. + * + */ +static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat ) +{ + switch( paFormat ) + { + case paUInt8: + *ossFormat = AFMT_U8; + break; + case paInt8: + *ossFormat = AFMT_S8; + break; + case paInt16: + *ossFormat = AFMT_S16_NE; + break; + default: + return paInternalError; /* This shouldn't happen */ + } + + return paNoError; +} + +/** Return the PA-compatible formats that this device can support. + * + */ +static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats ) +{ + PaError result = paNoError; + int mask = 0; + PaSampleFormat frmts = 0; + + ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError ); + if( mask & AFMT_U8 ) + frmts |= paUInt8; + if( mask & AFMT_S8 ) + frmts |= paInt8; + if( mask & AFMT_S16_NE ) + frmts |= paInt16; + else + result = paSampleFormatNotSupported; + + *availableFormats = frmts; + +error: + return result; +} + +static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component ) +{ + return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount; +} + +/** Buffer size in bytes. + * + */ +static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component ) +{ + return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs; +} + +static int CalcHigherLogTwo( int n ) +{ + int log2 = 0; + while( (1<userChannelCount; + int frgmt; + int numBufs; + int bytesPerBuf; + double bufSz; + unsigned long fragSz; + audio_buf_info bufInfo; + + /* We may have a situation where only one component (the master) is configured, if both point to the same device. + * In that case, the second component will copy settings from the other */ + if( !master ) + { + /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size. + * The hardware need not respect the requested fragment size, so we may have to adapt. + */ + if( framesPerBuffer == paFramesPerBufferUnspecified ) + { + bufSz = component->latency * sampleRate; + fragSz = bufSz / 4; + } + else + { + fragSz = framesPerBuffer; + bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */ + } + + PA_ENSURE( GetAvailableFormats( component, &availableFormats ) ); + hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat ); + + /* OSS demands at least 2 buffers, and 16 bytes per buffer */ + numBufs = PA_MAX( bufSz / fragSz, 2 ); + bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 ); + + /* The fragment parameters are encoded like this: + * Most significant byte: number of fragments + * Least significant byte: exponent of fragment size (i.e., for 256, 8) + */ + frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff); + ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError ); + + /* A: according to the OSS programmer's guide parameters should be set in this order: + * format, channels, rate */ + + /* This format should be deemed good before we get this far */ + PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) ); + nativeFormat = temp; + ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError ); + PA_UNLESS( temp == nativeFormat, paInternalError ); + + /* try to set the number of channels */ + ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */ + /* It's possible that the minimum number of host channels is greater than what the user requested */ + PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount ); + + /* try to set the sample rate */ + ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate ); + + /* reject if there's no sample rate within 1% of the one requested */ + if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 ) + { + PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr )); + PA_ENSURE( paInvalidSampleRate ); + } + + ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ), + paUnanticipatedHostError ); + component->numBufs = bufInfo.fragstotal; + + /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */ + ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError ); + + component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans; + component->hostChannelCount = chans; + component->hostFormat = hostFormat; + } + else + { + component->hostFormat = master->hostFormat; + component->hostFrames = master->hostFrames; + component->hostChannelCount = master->hostChannelCount; + component->numBufs = master->numBufs; + } + + PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ), + paInsufficientMemory ); + +error: + return result; +} + +static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames ) +{ + PaError result = paNoError; + size_t len = *frames * PaOssStreamComponent_FrameSize( component ); + ssize_t bytesRead; + + ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError ); + *frames = bytesRead / PaOssStreamComponent_FrameSize( component ); + +error: + return result; +} + +static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames ) +{ + PaError result = paNoError; + size_t len = *frames * PaOssStreamComponent_FrameSize( component ); + ssize_t bytesWritten; + + ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError ); + *frames = bytesWritten / PaOssStreamComponent_FrameSize( component ); + +error: + return result; +} + +/** Configure the stream according to input/output parameters. + * + * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by + * the user, if so we'll record the actual number of host channels and adapt later. + */ +static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer, + double *inputLatency, double *outputLatency ) +{ + PaError result = paNoError; + int duplex = stream->capture && stream->playback; + unsigned long framesPerHostBuffer = 0; + + /* We should request full duplex first thing after opening the device */ + if( duplex && stream->sharedDevice ) + ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError ); + + if( stream->capture ) + { + PaOssStreamComponent *component = stream->capture; + PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL ); + + assert( component->hostChannelCount > 0 ); + assert( component->hostFrames > 0 ); + + *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; + } + if( stream->playback ) + { + PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL; + PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out, + master ) ); + + assert( component->hostChannelCount > 0 ); + assert( component->hostFrames > 0 ); + + *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate; + } + + if( duplex ) + framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames ); + else if( stream->capture ) + framesPerHostBuffer = stream->capture->hostFrames; + else if( stream->playback ) + framesPerHostBuffer = stream->playback->hostFrames; + + stream->framesPerHostBuffer = framesPerHostBuffer; + stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */ + + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + +error: + return result; +} + +/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ + +/** Open a PA OSS stream. + * + * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the + * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both + * directions are the same device we will demand the same number of channels. The number of channels can range + * from 1 to the maximum supported by the device. + * + * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback + * must reflect this, in addition the host latency per device should approximate the corresponding + * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that + * both capture and playback can agree on (they can be different devices), the buffer processor can adapt + * between host and user buffer size, but the ratio should preferably be integral. + */ +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result = paNoError; + PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi; + PaOssStream *stream = NULL; + int inputChannelCount = 0, outputChannelCount = 0; + PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0; + const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0; + int bpInitialized = 0; + double inLatency, outLatency; + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + if( inputParameters ) + { + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + inputDeviceInfo = hostApi->deviceInfos[inputParameters->device]; + PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) ); + + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + } + if( outputParameters ) + { + outputDeviceInfo = hostApi->deviceInfos[outputParameters->device]; + PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) ); + + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + } + + /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same + * device is opened for both directions + */ + if( inputChannelCount > 0 && outputChannelCount > 0 ) + { + if( inputParameters->device == outputParameters->device ) + { + if( inputParameters->channelCount != outputParameters->channelCount ) + return paInvalidChannelCount; + } + } + + /* allocate and do basic initialization of the stream structure */ + PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory ); + PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ); + + PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) ); + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + if( inputParameters ) + { + inputHostFormat = stream->capture->hostFormat; + stream->streamRepresentation.streamInfo.inputLatency = inLatency + + PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate; + } + if( outputParameters ) + { + outputHostFormat = stream->playback->hostFormat; + stream->streamRepresentation.streamInfo.outputLatency = outLatency + + PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate; + } + + /* Initialize buffer processor with fixed host buffer size. + * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will + * convert between the two. + */ + PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat, + outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer, + paUtilFixedHostBufferSize, streamCallback, userData ) ); + bpInitialized = 1; + + *s = (PaStream*)stream; + + return result; + +error: + if( bpInitialized ) + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + if( stream ) + PaOssStream_Terminate( stream ); + + return result; +} + +/*! Poll on I/O filedescriptors. + + Poll till we've determined there's data for read or write. In the full-duplex case, + we don't want to hang around forever waiting for either input or output frames, so + whenever we have a timed out filedescriptor we check if we're nearing under/overrun + for the other direction (critical limit set at one buffer). If so, we exit the waiting + state, and go on with what we got. We align the number of frames on a host buffer + boundary because it is possible that the buffer size differs for the two directions and + the host buffer size is a compromise between the two. + */ +static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames ) +{ + PaError result = paNoError; + int pollPlayback = 0, pollCapture = 0; + int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail; + audio_buf_info bufInfo; + /* int ofs = 0, nfds = stream->nfds; */ + fd_set readFds, writeFds; + int nfds = 0; + struct timeval selectTimeval = {0, 0}; + unsigned long timeout = stream->pollTimeout; /* In usecs */ + int captureFd = -1, playbackFd = -1; + + assert( stream ); + assert( frames ); + + if( stream->capture ) + { + pollCapture = 1; + captureFd = stream->capture->fd; + /* stream->capture->pfd->events = POLLIN; */ + } + if( stream->playback ) + { + pollPlayback = 1; + playbackFd = stream->playback->fd; + /* stream->playback->pfd->events = POLLOUT; */ + } + + FD_ZERO( &readFds ); + FD_ZERO( &writeFds ); + + while( pollPlayback || pollCapture ) + { + pthread_testcancel(); + + /* select may modify the timeout parameter */ + selectTimeval.tv_usec = timeout; + nfds = 0; + + if( pollCapture ) + { + FD_SET( captureFd, &readFds ); + nfds = captureFd + 1; + } + if( pollPlayback ) + { + FD_SET( playbackFd, &writeFds ); + nfds = PA_MAX( nfds, playbackFd + 1 ); + } + ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError ); + /* + if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 ) + { + + ENSURE_( -1, paUnanticipatedHostError ); + } + */ + pthread_testcancel(); + + if( pollCapture ) + { + if( FD_ISSET( captureFd, &readFds ) ) + { + FD_CLR( captureFd, &readFds ); + pollCapture = 0; + } + /* + if( stream->capture->pfd->revents & POLLIN ) + { + --nfds; + ++ofs; + pollCapture = 0; + } + */ + else if( stream->playback ) /* Timed out, go on with playback? */ + { + /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n", + __FUNCTION__, stream->pollTimeout ));*/ + } + } + if( pollPlayback ) + { + if( FD_ISSET( playbackFd, &writeFds ) ) + { + FD_CLR( playbackFd, &writeFds ); + pollPlayback = 0; + } + /* + if( stream->playback->pfd->revents & POLLOUT ) + { + --nfds; + pollPlayback = 0; + } + */ + else if( stream->capture ) /* Timed out, go on with capture? */ + { + /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n", + __FUNCTION__, stream->pollTimeout ));*/ + } + } + } + + if( stream->capture ) + { + ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError ); + captureAvail = bufInfo.fragments * stream->capture->hostFrames; + if( !captureAvail ) + PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ )); + + captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */ + } + if( stream->playback ) + { + ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError ); + playbackAvail = bufInfo.fragments * stream->playback->hostFrames; + if( !playbackAvail ) + { + PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ )); + } + + playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */ + } + + commonAvail = PA_MIN( captureAvail, playbackAvail ); + if( commonAvail == INT_MAX ) + commonAvail = 0; + commonAvail -= commonAvail % stream->framesPerHostBuffer; + + assert( commonAvail != INT_MAX ); + assert( commonAvail >= 0 ); + *frames = commonAvail; + +error: + return result; +} + +/** Prepare stream for capture/playback. + * + * In order to synchronize capture and playback properly we use the SETTRIGGER command. + */ +static PaError PaOssStream_Prepare( PaOssStream *stream ) +{ + PaError result = paNoError; + int enableBits = 0; + + if( stream->triggered ) + return result; + + if( stream->playback ) + { + size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback ); + memset( stream->playback->buffer, 0, bufSz ); + + /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer + * OSS will complain. */ + PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); + while (1) + { + if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 ) + break; + } + PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) ); + } + + if( stream->sharedDevice ) + { + enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; + ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); + } + else + { + if( stream->capture ) + { + enableBits = PCM_ENABLE_INPUT; + ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); + } + if( stream->playback ) + { + enableBits = PCM_ENABLE_OUTPUT; + ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError ); + } + } + + /* Ok, we have triggered the stream */ + stream->triggered = 1; + +error: + return result; +} + +/** Stop audio processing + * + */ +static PaError PaOssStream_Stop( PaOssStream *stream, int abort ) +{ + PaError result = paNoError; + + /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST. + * Also disable capture/playback till the stream is started again */ + if( stream->capture ) + { + ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError ); + } + if( stream->playback && !stream->sharedDevice ) + { + ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError ); + } + +error: + return result; +} + +/** Clean up after thread exit. + * + * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here + */ +static void OnExit( void *data ) +{ + PaOssStream *stream = (PaOssStream *) data; + assert( data ); + + PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); + + PaOssStream_Stop( stream, stream->callbackAbort ); + + PA_DEBUG(( "OnExit: Stoppage\n" )); + + /* Eventually notify user all buffers have played */ + if( stream->streamRepresentation.streamFinishedCallback ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + + stream->callbackAbort = 0; /* Clear state */ + stream->isActive = 0; +} + +static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail ) +{ + PaError result = paNoError; + + if( stream->capture ) + { + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, + stream->capture->hostChannelCount ); + PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail ); + } + if( stream->playback ) + { + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, + stream->playback->hostChannelCount ); + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail ); + } + + return result; +} + +/** Thread procedure for callback processing. + * + * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the + * callback should be used for buffer priming. When the stream is cancelled a separate function will + * take care of the transition to the Callback Finished state (the stream isn't considered Stopped + * before StopStream() or AbortStream() are called). + */ +static void *PaOSS_AudioThreadProc( void *userData ) +{ + PaError result = paNoError; + PaOssStream *stream = (PaOssStream*)userData; + unsigned long framesAvail, framesProcessed; + int callbackResult = paContinue; + int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */ + int initiateProcessing = triggered; /* Already triggered? */ + PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */ + + /* +#if ( SOUND_VERSION > 0x030904 ) + audio_errinfo errinfo; +#endif +*/ + + assert( stream ); + + pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */ + + /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and + * playback in sync, when the stream is restarted after being stopped we simply start by reading/ + * writing. + */ + PA_ENSURE( PaOssStream_Prepare( stream ) ); + + /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */ + if( initiateProcessing ) + { + /* Make sure devices are in blocking mode */ + if( stream->capture ) + ModifyBlocking( stream->capture->fd, 1 ); + if( stream->playback ) + ModifyBlocking( stream->playback->fd, 1 ); + } + + while( 1 ) + { + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */ + + pthread_testcancel(); + + if( stream->callbackStop && callbackResult == paContinue ) + { + PA_DEBUG(( "Setting callbackResult to paComplete\n" )); + callbackResult = paComplete; + } + + /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless + * the stream has been recently started, we will have to go right ahead and read/write in blocking + * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch + * to non-blocking mode. + */ + if( !initiateProcessing ) + { + PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */ + assert( framesAvail % stream->framesPerHostBuffer == 0 ); + } + else + { + framesAvail = stream->framesPerHostBuffer; + } + + while( framesAvail > 0 ) + { + unsigned long frames = framesAvail; + + pthread_testcancel(); + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + /* Read data */ + if ( stream->capture ) + { + PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) ); + assert( frames == framesAvail ); + } + +#if ( SOUND_VERSION >= 0x030904 ) + /* + Check with OSS to see if there have been any under/overruns + since last time we checked. + */ + /* + if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 ) + { + if( errinfo.play_underruns ) + cbFlags |= paOutputUnderflow ; + if( errinfo.record_underruns ) + cbFlags |= paInputUnderflow ; + } + else + PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) )); + */ +#endif + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, + cbFlags ); + cbFlags = 0; + PA_ENSURE( SetUpBuffers( stream, framesAvail ) ); + + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, + &callbackResult ); + assert( framesProcessed == framesAvail ); + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + if ( stream->playback ) + { + frames = framesAvail; + + PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) ); + assert( frames == framesAvail ); + + /* TODO: handle bytesWritten != bytesRequested (slippage?) */ + } + + framesAvail -= framesProcessed; + stream->framesProcessed += framesProcessed; + + if( callbackResult != paContinue ) + break; + } + + if( initiateProcessing || !triggered ) + { + /* Non-blocking */ + if( stream->capture ) + PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) ); + if( stream->playback && !stream->sharedDevice ) + PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) ); + + initiateProcessing = 0; + sem_post( &stream->semaphore ); + } + + if( callbackResult != paContinue ) + { + stream->callbackAbort = callbackResult == paAbort; + if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) ) + break; + } + } + + pthread_cleanup_pop( 1 ); + +error: + pthread_exit( NULL ); +} + +/** Close the stream. + * + */ +static PaError CloseStream( PaStream* s ) +{ + PaError result = paNoError; + PaOssStream *stream = (PaOssStream*)s; + + assert( stream ); + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaOssStream_Terminate( stream ); + + return result; +} + +/** Start the stream. + * + * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual + * callback will be repeatedly called in a separate thread. If a separate thread is started this function + * will block untill it has started processing audio, otherwise audio processing is started directly. + */ +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaOssStream *stream = (PaOssStream*)s; + + stream->isActive = 1; + stream->isStopped = 0; + stream->lastPosPtr = 0; + stream->lastStreamBytes = 0; + stream->framesProcessed = 0; + + /* only use the thread for callback streams */ + if( stream->bufferProcessor.streamCallback ) + { + PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) ); + sem_wait( &stream->semaphore ); + } + else + PA_ENSURE( PaOssStream_Prepare( stream ) ); + +error: + return result; +} + +static PaError RealStop( PaOssStream *stream, int abort ) +{ + PaError result = paNoError; + + if( stream->callbackMode ) + { + if( abort ) + stream->callbackAbort = 1; + else + stream->callbackStop = 1; + + PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) ); + + stream->callbackStop = stream->callbackAbort = 0; + } + else + PA_ENSURE( PaOssStream_Stop( stream, abort ) ); + + stream->isStopped = 1; + +error: + return result; +} + +/** Stop the stream. + * + * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued + * buffers. + */ +static PaError StopStream( PaStream *s ) +{ + return RealStop( (PaOssStream *)s, 0 ); +} + +/** Abort the stream. + * + * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued + * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing + * the OSS device. + */ +static PaError AbortStream( PaStream *s ) +{ + return RealStop( (PaOssStream *)s, 1 ); +} + +/** Is the stream in the Stopped state. + * + */ +static PaError IsStreamStopped( PaStream *s ) +{ + PaOssStream *stream = (PaOssStream*)s; + + return (stream->isStopped); +} + +/** Is the stream in the Active state. + * + */ +static PaError IsStreamActive( PaStream *s ) +{ + PaOssStream *stream = (PaOssStream*)s; + + return (stream->isActive); +} + +static PaTime GetStreamTime( PaStream *s ) +{ + PaOssStream *stream = (PaOssStream*)s; + count_info info; + int delta; + + if( stream->playback ) { + if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) { + delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF; + return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate; + } + } + else { + if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) { + delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF; + return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate; + } + } + + /* the ioctl failed, but we can still give a coarse estimate */ + + return stream->framesProcessed / stream->sampleRate; +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaOssStream *stream = (PaOssStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + + +/* + As separate stream interfaces are used for blocking and callback + streams, the following functions can be guaranteed to only be called + for blocking streams. +*/ + + +static PaError ReadStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaOssStream *stream = (PaOssStream*)s; + int bytesRequested, bytesRead; + unsigned long framesRequested; + void *userBuffer; + + /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers, + * so we copy the user provided pointers */ + if( stream->bufferProcessor.userInputIsInterleaved ) + userBuffer = buffer; + else /* Copy channels into local array */ + { + userBuffer = stream->capture->userBuffers; + memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount ); + } + + while( frames ) + { + framesRequested = PA_MIN( frames, stream->capture->hostFrames ); + + bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture ); + bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested ); + if ( bytesRequested != bytesRead ) + return paUnanticipatedHostError; + + PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames ); + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount ); + PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested ); + frames -= framesRequested; + } + return paNoError; +} + + +static PaError WriteStream( PaStream* s, + const void *buffer, + unsigned long frames ) +{ + PaOssStream *stream = (PaOssStream*)s; + int bytesRequested, bytesWritten; + unsigned long framesConverted; + const void *userBuffer; + + /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers, + * so we copy the user provided pointers */ + if( stream->bufferProcessor.userOutputIsInterleaved ) + userBuffer = buffer; + else /* Copy channels into local array */ + { + userBuffer = stream->playback->userBuffers; + memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount ); + } + + while( frames ) + { + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames ); + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount ); + + framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames ); + frames -= framesConverted; + + bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback ); + bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested ); + + if ( bytesRequested != bytesWritten ) + return paUnanticipatedHostError; + } + return paNoError; +} + + +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaOssStream *stream = (PaOssStream*)s; + audio_buf_info info; + + if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 ) + return paUnanticipatedHostError; + return info.fragments * stream->capture->hostFrames; +} + + +/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */ +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaOssStream *stream = (PaOssStream*)s; + int delay = 0; + + if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 ) + return paUnanticipatedHostError; + + return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback ); +} + diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_util.c b/pjmedia/src/pjmedia/portaudio/pa_unix_util.c index 596e295c7..f45848fb1 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.c +++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.c @@ -1,175 +1,175 @@ -/* - * $Id: pa_unix_util.c,v 1.1.2.7 2005/03/31 15:02:48 aknudsen Exp $ - * Portable Audio I/O Library - * UNIX platform-specific support functions - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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 -#include /* For memset */ - -#include "pa_util.h" -#include "pa_unix_util.h" - -/* - Track memory allocations to avoid leaks. - */ - -#if PA_TRACK_MEMORY -static int numAllocations_ = 0; -#endif - - -void *PaUtil_AllocateMemory( long size ) -{ - void *result = malloc( size ); - -#if PA_TRACK_MEMORY - if( result != NULL ) numAllocations_ += 1; -#endif - return result; -} - - -void PaUtil_FreeMemory( void *block ) -{ - if( block != NULL ) - { - free( block ); -#if PA_TRACK_MEMORY - numAllocations_ -= 1; -#endif - - } -} - - -int PaUtil_CountCurrentlyAllocatedBlocks( void ) -{ -#if PA_TRACK_MEMORY - return numAllocations_; -#else - return 0; -#endif -} - - -void Pa_Sleep( long msec ) -{ - while( msec > 999 ) /* For OpenBSD and IRIX, argument */ - { /* to usleep must be < 1000000. */ - usleep( 999000 ); - msec -= 999; - } - usleep( msec * 1000 ); -} - -/* *** NOT USED YET: *** -static int usePerformanceCounter_; -static double microsecondsPerTick_; -*/ - -void PaUtil_InitializeClock( void ) -{ - /* TODO */ -} - - -PaTime PaUtil_GetTime( void ) -{ - struct timeval tv; - gettimeofday( &tv, NULL ); - return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec; -} - -PaError PaUtil_InitializeThreading( PaUtilThreading *threading ) -{ - (void) paUtilErr_; - return paNoError; -} - -void PaUtil_TerminateThreading( PaUtilThreading *threading ) -{ -} - -PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data ) -{ - pthread_create( &threading->callbackThread, NULL, threadRoutine, data ); - return paNoError; -} - -PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult ) -{ - PaError result = paNoError; - void *pret; - - if( exitResult ) - *exitResult = paNoError; - - /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ - if( !wait ) - pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */ - pthread_join( threading->callbackThread, &pret ); - -#ifdef PTHREAD_CANCELED - if( pret && PTHREAD_CANCELED != pret ) -#else - /* !wait means the thread may have been canceled */ - if( pret && wait ) -#endif - { - if( exitResult ) - *exitResult = *(PaError *) pret; - free( pret ); - } - - return result; -} - -/* -static void *CanaryFunc( void *userData ) -{ - const unsigned intervalMsec = 1000; - PaUtilThreading *th = (PaUtilThreading *) userData; - - while( 1 ) - { - th->canaryTime = PaUtil_GetTime(); - - pthread_testcancel(); - Pa_Sleep( intervalMsec ); - } - - pthread_exit( NULL ); -} -*/ +/* + * $Id: pa_unix_util.c,v 1.1.2.7 2005/03/31 15:02:48 aknudsen Exp $ + * Portable Audio I/O Library + * UNIX platform-specific support functions + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2000 Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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 +#include /* For memset */ + +#include "pa_util.h" +#include "pa_unix_util.h" + +/* + Track memory allocations to avoid leaks. + */ + +#if PA_TRACK_MEMORY +static int numAllocations_ = 0; +#endif + + +void *PaUtil_AllocateMemory( long size ) +{ + void *result = malloc( size ); + +#if PA_TRACK_MEMORY + if( result != NULL ) numAllocations_ += 1; +#endif + return result; +} + + +void PaUtil_FreeMemory( void *block ) +{ + if( block != NULL ) + { + free( block ); +#if PA_TRACK_MEMORY + numAllocations_ -= 1; +#endif + + } +} + + +int PaUtil_CountCurrentlyAllocatedBlocks( void ) +{ +#if PA_TRACK_MEMORY + return numAllocations_; +#else + return 0; +#endif +} + + +void Pa_Sleep( long msec ) +{ + while( msec > 999 ) /* For OpenBSD and IRIX, argument */ + { /* to usleep must be < 1000000. */ + usleep( 999000 ); + msec -= 999; + } + usleep( msec * 1000 ); +} + +/* *** NOT USED YET: *** +static int usePerformanceCounter_; +static double microsecondsPerTick_; +*/ + +void PaUtil_InitializeClock( void ) +{ + /* TODO */ +} + + +PaTime PaUtil_GetTime( void ) +{ + struct timeval tv; + gettimeofday( &tv, NULL ); + return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec; +} + +PaError PaUtil_InitializeThreading( PaUtilThreading *threading ) +{ + (void) paUtilErr_; + return paNoError; +} + +void PaUtil_TerminateThreading( PaUtilThreading *threading ) +{ +} + +PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data ) +{ + pthread_create( &threading->callbackThread, NULL, threadRoutine, data ); + return paNoError; +} + +PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult ) +{ + PaError result = paNoError; + void *pret; + + if( exitResult ) + *exitResult = paNoError; + + /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */ + if( !wait ) + pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */ + pthread_join( threading->callbackThread, &pret ); + +#ifdef PTHREAD_CANCELED + if( pret && PTHREAD_CANCELED != pret ) +#else + /* !wait means the thread may have been canceled */ + if( pret && wait ) +#endif + { + if( exitResult ) + *exitResult = *(PaError *) pret; + free( pret ); + } + + return result; +} + +/* +static void *CanaryFunc( void *userData ) +{ + const unsigned intervalMsec = 1000; + PaUtilThreading *th = (PaUtilThreading *) userData; + + while( 1 ) + { + th->canaryTime = PaUtil_GetTime(); + + pthread_testcancel(); + Pa_Sleep( intervalMsec ); + } + + pthread_exit( NULL ); +} +*/ diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_util.h b/pjmedia/src/pjmedia/portaudio/pa_unix_util.h index f27624a00..01dda01e5 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.h +++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.h @@ -1,73 +1,73 @@ -#ifndef PA_UNIX_UTIL_H -#define PA_UNIX_UTIL_H - -#include "pa_cpuload.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) ) -#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) ) - -/* Utilize GCC branch prediction for error tests */ -#if defined __GNUC__ && __GNUC__ >= 3 -#define UNLIKELY(expr) __builtin_expect( (expr), 0 ) -#else -#define UNLIKELY(expr) (expr) -#endif - -#define STRINGIZE_HELPER(expr) #expr -#define STRINGIZE(expr) STRINGIZE_HELPER(expr) - -#define PA_UNLESS(expr, code) \ - do { \ - if( UNLIKELY( (expr) == 0 ) ) \ - { \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = (code); \ - goto error; \ - } \ - } while (0); - -static PaError paUtilErr_; /* Used with PA_ENSURE */ - -/* Check PaError */ -#define PA_ENSURE(expr) \ - do { \ - if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \ - { \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ - result = paUtilErr_; \ - goto error; \ - } \ - } while (0); - -typedef struct { - pthread_t callbackThread; -} PaUtilThreading; - -PaError PaUtil_InitializeThreading( PaUtilThreading *threading ); -void PaUtil_TerminateThreading( PaUtilThreading *threading ); -PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data ); -PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult ); - -/* State accessed by utility functions */ - -/* -void PaUnix_SetRealtimeScheduling( int rt ); - -void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm ); - -PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s ); - -PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult ); - -void PaUtil_CallbackUpdate( PaUtilThreading *th ); -*/ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +#ifndef PA_UNIX_UTIL_H +#define PA_UNIX_UTIL_H + +#include "pa_cpuload.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) ) +#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) ) + +/* Utilize GCC branch prediction for error tests */ +#if defined __GNUC__ && __GNUC__ >= 3 +#define UNLIKELY(expr) __builtin_expect( (expr), 0 ) +#else +#define UNLIKELY(expr) (expr) +#endif + +#define STRINGIZE_HELPER(expr) #expr +#define STRINGIZE(expr) STRINGIZE_HELPER(expr) + +#define PA_UNLESS(expr, code) \ + do { \ + if( UNLIKELY( (expr) == 0 ) ) \ + { \ + PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + result = (code); \ + goto error; \ + } \ + } while (0); + +static PaError paUtilErr_; /* Used with PA_ENSURE */ + +/* Check PaError */ +#define PA_ENSURE(expr) \ + do { \ + if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \ + { \ + PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + result = paUtilErr_; \ + goto error; \ + } \ + } while (0); + +typedef struct { + pthread_t callbackThread; +} PaUtilThreading; + +PaError PaUtil_InitializeThreading( PaUtilThreading *threading ); +void PaUtil_TerminateThreading( PaUtilThreading *threading ); +PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data ); +PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult ); + +/* State accessed by utility functions */ + +/* +void PaUnix_SetRealtimeScheduling( int rt ); + +void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm ); + +PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s ); + +PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult ); + +void PaUtil_CallbackUpdate( PaUtilThreading *th ); +*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/pjmedia/src/pjmedia/portaudio/pa_util.h b/pjmedia/src/pjmedia/portaudio/pa_util.h index 87bd5c8d2..149fbca3b 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_util.h +++ b/pjmedia/src/pjmedia/portaudio/pa_util.h @@ -1,167 +1,167 @@ -#ifndef PA_UTIL_H -#define PA_UTIL_H -/* - * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $ - * Portable Audio I/O Library implementation utilities header - * common implementation utilities and interfaces - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief Prototypes for utility functions used by PortAudio implementations. - - @todo Document and adhere to the alignment guarantees provided by - PaUtil_AllocateMemory(). -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -struct PaUtilHostApiRepresentation; - - -/** Retrieve a specific host API representation. This function can be used - by implementations to retrieve a pointer to their representation in - host api specific extension functions which aren't passed a rep pointer - by pa_front.c. - - @param hostApi A pointer to a host API represenation pointer. Apon success - this will receive the requested representation pointer. - - @param type A valid host API type identifier. - - @returns An error code. If the result is PaNoError then a pointer to the - requested host API representation will be stored in *hostApi. If the host API - specified by type is not found, this function returns paHostApiNotFound. -*/ -PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi, - PaHostApiTypeId type ); - - -/** Convert a PortAudio device index into a host API specific device index. - @param hostApiDevice Pointer to a device index, on success this will recieve the - converted device index value. - @param device The PortAudio device index to convert. - @param hostApi The host api which the index should be converted for. - - @returns On success returns PaNoError and places the converted index in the - hostApiDevice parameter. -*/ -PaError PaUtil_DeviceIndexToHostApiDeviceIndex( - PaDeviceIndex *hostApiDevice, PaDeviceIndex device, - struct PaUtilHostApiRepresentation *hostApi ); - - -/** Set the host error information returned by Pa_GetLastHostErrorInfo. This - function and the paUnanticipatedHostError error code should be used as a - last resort. Implementors should use existing PA error codes where possible, - or nominate new ones. Note that at it is always better to use - PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an - ambiguous or inaccurate PaError code. - - @param hostApiType The host API which encountered the error (ie of the caller) - - @param errorCode The error code returned by the native API function. - - @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo - makes a copy of the string, so it is not necessary for the pointer to remain - valid after the call to PaUtil_SetLastHostErrorInfo() returns. - -*/ -void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, - const char *errorText ); - - - -/** PA_DEBUG() provides a simple debug message printing facility. The macro - passes it's argument to a printf-like function called PaUtil_DebugPrint() - which prints to stderr and always flushes the stream after printing. - Because preprocessor macros cannot directly accept variable length argument - lists, calls to the macro must include an additional set of parenthesis, eg: - PA_DEBUG(("errorno: %d", 1001 )); -*/ - -void PaUtil_DebugPrint( const char *format, ... ); - -#if (0) /* set to 1 to print debug messages */ -#define PA_DEBUG(x) PaUtil_DebugPrint x ; -#else -#define PA_DEBUG(x) -#endif - - -/* the following functions are implemented in a platform platform specific - .c file -*/ - -/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */ -void *PaUtil_AllocateMemory( long size ); - - -/** Realease block if non-NULL. block may be NULL */ -void PaUtil_FreeMemory( void *block ); - - -/** Return the number of currently allocated blocks. This function can be - used for detecting memory leaks. - - @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If - it isn't, this function will always return 0. -*/ -int PaUtil_CountCurrentlyAllocatedBlocks( void ); - - -/** Initialize the clock used by PaUtil_GetTime(). Call this before calling - PaUtil_GetTime. - - @see PaUtil_GetTime -*/ -void PaUtil_InitializeClock( void ); - - -/** Return the system time in seconds. Used to implement CPU load functions - - @see PaUtil_InitializeClock -*/ -double PaUtil_GetTime( void ); - - -/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */ - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_UTIL_H */ +#ifndef PA_UTIL_H +#define PA_UTIL_H +/* + * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $ + * Portable Audio I/O Library implementation utilities header + * common implementation utilities and interfaces + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief Prototypes for utility functions used by PortAudio implementations. + + @todo Document and adhere to the alignment guarantees provided by + PaUtil_AllocateMemory(). +*/ + + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +struct PaUtilHostApiRepresentation; + + +/** Retrieve a specific host API representation. This function can be used + by implementations to retrieve a pointer to their representation in + host api specific extension functions which aren't passed a rep pointer + by pa_front.c. + + @param hostApi A pointer to a host API represenation pointer. Apon success + this will receive the requested representation pointer. + + @param type A valid host API type identifier. + + @returns An error code. If the result is PaNoError then a pointer to the + requested host API representation will be stored in *hostApi. If the host API + specified by type is not found, this function returns paHostApiNotFound. +*/ +PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi, + PaHostApiTypeId type ); + + +/** Convert a PortAudio device index into a host API specific device index. + @param hostApiDevice Pointer to a device index, on success this will recieve the + converted device index value. + @param device The PortAudio device index to convert. + @param hostApi The host api which the index should be converted for. + + @returns On success returns PaNoError and places the converted index in the + hostApiDevice parameter. +*/ +PaError PaUtil_DeviceIndexToHostApiDeviceIndex( + PaDeviceIndex *hostApiDevice, PaDeviceIndex device, + struct PaUtilHostApiRepresentation *hostApi ); + + +/** Set the host error information returned by Pa_GetLastHostErrorInfo. This + function and the paUnanticipatedHostError error code should be used as a + last resort. Implementors should use existing PA error codes where possible, + or nominate new ones. Note that at it is always better to use + PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an + ambiguous or inaccurate PaError code. + + @param hostApiType The host API which encountered the error (ie of the caller) + + @param errorCode The error code returned by the native API function. + + @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo + makes a copy of the string, so it is not necessary for the pointer to remain + valid after the call to PaUtil_SetLastHostErrorInfo() returns. + +*/ +void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, + const char *errorText ); + + + +/** PA_DEBUG() provides a simple debug message printing facility. The macro + passes it's argument to a printf-like function called PaUtil_DebugPrint() + which prints to stderr and always flushes the stream after printing. + Because preprocessor macros cannot directly accept variable length argument + lists, calls to the macro must include an additional set of parenthesis, eg: + PA_DEBUG(("errorno: %d", 1001 )); +*/ + +void PaUtil_DebugPrint( const char *format, ... ); + +#if (0) /* set to 1 to print debug messages */ +#define PA_DEBUG(x) PaUtil_DebugPrint x ; +#else +#define PA_DEBUG(x) +#endif + + +/* the following functions are implemented in a platform platform specific + .c file +*/ + +/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */ +void *PaUtil_AllocateMemory( long size ); + + +/** Realease block if non-NULL. block may be NULL */ +void PaUtil_FreeMemory( void *block ); + + +/** Return the number of currently allocated blocks. This function can be + used for detecting memory leaks. + + @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If + it isn't, this function will always return 0. +*/ +int PaUtil_CountCurrentlyAllocatedBlocks( void ); + + +/** Initialize the clock used by PaUtil_GetTime(). Call this before calling + PaUtil_GetTime. + + @see PaUtil_GetTime +*/ +void PaUtil_InitializeClock( void ); + + +/** Return the system time in seconds. Used to implement CPU load functions + + @see PaUtil_InitializeClock +*/ +double PaUtil_GetTime( void ); + + +/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */ + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_UTIL_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_ds.c b/pjmedia/src/pjmedia/portaudio/pa_win_ds.c index 128a8a17b..940867da4 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_win_ds.c +++ b/pjmedia/src/pjmedia/portaudio/pa_win_ds.c @@ -1,1828 +1,1828 @@ -/* - * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $ - * Portable Audio I/O Library DirectSound implementation - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - - @todo implement paInputOverflow callback status flag - - @todo implement paNeverDropInput. - - @todo implement host api specific extension to set i/o buffer sizes in frames - - @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.) - - @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable - - @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into - a native portaudio error code. Standard DirectSound result codes are documented at msdn. - - @todo implement IsFormatSupported - - @todo check that CoInitialize() CoUninitialize() are always correctly - paired, even in error cases. - - @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error"). - - @todo make sure all buffers have been played before stopping the stream - when the stream callback returns paComplete - - old TODOs from phil, need to work out if these have been done: - O- fix "patest_stop.c" -*/ - -#include -#include /* strlen() */ - -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "dsound_wrapper.h" - -#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ -#pragma comment( lib, "dsound.lib" ) -#pragma comment( lib, "winmm.lib" ) -#endif - - -#define PRINT(x) /* { printf x; fflush(stdout); } */ -#define ERR_RPT(x) PRINT(x) -#define DBUG(x) /* PRINT(x) */ -#define DBUGX(x) /* PRINT(x) */ - -#define PA_USE_HIGH_LATENCY (0) -#if PA_USE_HIGH_LATENCY -#define PA_WIN_9X_LATENCY (500) -#define PA_WIN_NT_LATENCY (600) -#else -#define PA_WIN_9X_LATENCY (140) -#define PA_WIN_NT_LATENCY (280) -#endif - -#define PA_WIN_WDM_LATENCY (120) - -#define SECONDS_PER_MSEC (0.001) -#define MSEC_PER_SECOND (1000) - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - - -/* FIXME: should convert hr to a string */ -#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \ - PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" ) - -/************************************************* DX Prototypes **********/ -static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID, - LPCTSTR lpszDesc, - LPCTSTR lpszDrvName, - LPVOID lpContext ); - -/************************************************************************************/ -/********************** Structures **************************************************/ -/************************************************************************************/ -/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct PaWinDsDeviceInfo -{ - GUID guid; - GUID *lpGUID; - double sampleRates[3]; -} PaWinDsDeviceInfo; - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - /* implementation specific data goes here */ - PaWinDsDeviceInfo *winDsDeviceInfos; - -} PaWinDsHostApiRepresentation; - -/* PaWinDsStream - a stream data structure specifically for this implementation */ - -typedef struct PaWinDsStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - -/* DirectSound specific data. */ - DSoundWrapper directSoundWrapper; - MMRESULT timerID; - BOOL ifInsideCallback; /* Test for reentrancy. */ - int framesPerDSBuffer; - double framesWritten; - double secondsPerHostByte; /* Used to optimize latency calculation for outTime */ - - PaStreamCallbackFlags callbackFlags; - -/* FIXME - move all below to PaUtilStreamRepresentation */ - volatile int isStarted; - volatile int isActive; - volatile int stopProcessing; /* stop thread once existing buffers have been returned */ - volatile int abortProcessing; /* stop thread immediately */ -} PaWinDsStream; - - -/************************************************************************************ -** Duplicate the input string using the allocations allocator. -** A NULL string is converted to a zero length string. -** If memory cannot be allocated, NULL is returned. -**/ -static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src ) -{ - char *result = 0; - - if( src != NULL ) - { - size_t len = strlen(src); - result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) ); - if( result ) - memcpy( (void *) result, src, len+1 ); - } - else - { - result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 ); - if( result ) - result[0] = '\0'; - } - - return result; -} - -/************************************************************************************ -** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary -** information during device enumeration. -*/ -typedef struct DSDeviceNameAndGUID{ - char *name; // allocated from parent's allocations, never deleted by this structure - GUID guid; - LPGUID lpGUID; -} DSDeviceNameAndGUID; - -typedef struct DSDeviceNameAndGUIDVector{ - PaUtilAllocationGroup *allocations; - PaError enumerationError; - - int count; - int free; - DSDeviceNameAndGUID *items; // Allocated using LocalAlloc() -} DSDeviceNameAndGUIDVector; - -static PaError InitializeDSDeviceNameAndGUIDVector( - DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations ) -{ - PaError result = paNoError; - - guidVector->allocations = allocations; - guidVector->enumerationError = paNoError; - - guidVector->count = 0; - guidVector->free = 8; - guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free ); - if( guidVector->items == NULL ) - result = paInsufficientMemory; - - return result; -} - -static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector ) -{ - PaError result = paNoError; - DSDeviceNameAndGUID *newItems; - int i; - - /* double size of vector */ - int size = guidVector->count + guidVector->free; - guidVector->free += size; - - newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 ); - if( newItems == NULL ) - { - result = paInsufficientMemory; - } - else - { - for( i=0; i < guidVector->count; ++i ) - { - newItems[i].name = guidVector->items[i].name; - if( guidVector->items[i].lpGUID == NULL ) - { - newItems[i].lpGUID = NULL; - } - else - { - newItems[i].lpGUID = &newItems[i].guid; - memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );; - } - } - - LocalFree( guidVector->items ); - guidVector->items = newItems; - } - - return result; -} - -/* - it's safe to call DSDeviceNameAndGUIDVector multiple times -*/ -static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector ) -{ - PaError result = paNoError; - - if( guidVector->items != NULL ) - { - if( LocalFree( guidVector->items ) != NULL ) - result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */ - - guidVector->items = NULL; - } - - return result; -} - -/************************************************************************************ -** Collect preliminary device information during DirectSound enumeration -*/ -static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID, - LPCTSTR lpszDesc, - LPCTSTR lpszDrvName, - LPVOID lpContext ) -{ - DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext; - PaError error; - - (void) lpszDrvName; /* unused variable */ - - if( namesAndGUIDs->free == 0 ) - { - error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs ); - if( error != paNoError ) - { - namesAndGUIDs->enumerationError = error; - return FALSE; - } - } - - /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */ - if( lpGUID == NULL ) - { - namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL; - } - else - { - namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = - &namesAndGUIDs->items[namesAndGUIDs->count].guid; - - memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) ); - } - - namesAndGUIDs->items[namesAndGUIDs->count].name = - DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc ); - if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL ) - { - namesAndGUIDs->enumerationError = paInsufficientMemory; - return FALSE; - } - - ++namesAndGUIDs->count; - --namesAndGUIDs->free; - - return TRUE; -} - - -#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */ -static double defaultSampleRateSearchOrder_[] = - { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0, - 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; - - -/************************************************************************************ -** Extract capabilities from an output device, and add it to the device info list -** if successful. This function assumes that there is enough room in the -** device info list to accomodate all entries. -** -** The device will not be added to the device list if any errors are encountered. -*/ -static PaError AddOutputDeviceInfoFromDirectSound( - PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID ) -{ - PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; - PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount]; - PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount]; - HRESULT hr; - LPDIRECTSOUND lpDirectSound; - DSCAPS caps; - int deviceOK = TRUE; - PaError result = paNoError; - int i; - - /* Copy GUID to the device info structure. Set pointer. */ - if( lpGUID == NULL ) - { - winDsDeviceInfo->lpGUID = NULL; - } - else - { - memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) ); - winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid; - } - - - /* Create a DirectSound object for the specified GUID - Note that using CoCreateInstance doesn't work on windows CE. - */ - hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL ); - - /** try using CoCreateInstance because DirectSoundCreate was hanging under - some circumstances - note this was probably related to the - #define BOOL short bug which has now been fixed - @todo delete this comment and the following code once we've ensured - there is no bug. - */ - /* - hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectSound, (void**)&lpDirectSound ); - - if( hr == S_OK ) - { - hr = IDirectSound_Initialize( lpDirectSound, lpGUID ); - } - */ - - if( hr != DS_OK ) - { - DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr )); - deviceOK = FALSE; - } - else - { - /* Query device characteristics. */ - memset( &caps, 0, sizeof(caps) ); - caps.dwSize = sizeof(caps); - hr = IDirectSound_GetCaps( lpDirectSound, &caps ); - if( hr != DS_OK ) - { - DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr )); - deviceOK = FALSE; - } - else - { - -#ifndef PA_NO_WMME - if( caps.dwFlags & DSCAPS_EMULDRIVER ) - { - /* If WMME supported, then reject Emulated drivers because they are lousy. */ - deviceOK = FALSE; - } -#endif - - if( deviceOK ) - { - deviceInfo->maxInputChannels = 0; - /* Mono or stereo device? */ - deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; - - deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */ - - /* initialize defaultSampleRate */ - - if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) - { - /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */ - deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; - - for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i ) - { - if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate - && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){ - - deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i]; - break; - } - } - } - else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate ) - { - if( caps.dwMinSecondarySampleRate == 0 ) - { - /* - ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !! - ** But it supports continuous sampling. - ** So fake range of rates, and hope it really supports it. - */ - deviceInfo->defaultSampleRate = 44100.0f; - - DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex )); - } - else - { - deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; - } - } - else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) ) - { - /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000. - ** But we know that they really support a range of rates! - ** So when we see a ridiculous set of rates, assume it is a range. - */ - deviceInfo->defaultSampleRate = 44100.0f; - DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex )); - } - else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; - - - //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate ); - // dwFlags | DSCAPS_CONTINUOUSRATE - } - } - - IDirectSound_Release( lpDirectSound ); - } - - if( deviceOK ) - { - deviceInfo->name = name; - - if( lpGUID == NULL ) - hostApi->info.defaultOutputDevice = hostApi->info.deviceCount; - - hostApi->info.deviceCount++; - } - - return result; -} - - -/************************************************************************************ -** Extract capabilities from an input device, and add it to the device info list -** if successful. This function assumes that there is enough room in the -** device info list to accomodate all entries. -** -** The device will not be added to the device list if any errors are encountered. -*/ -static PaError AddInputDeviceInfoFromDirectSoundCapture( - PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID ) -{ - PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; - PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount]; - PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount]; - HRESULT hr; - LPDIRECTSOUNDCAPTURE lpDirectSoundCapture; - DSCCAPS caps; - int deviceOK = TRUE; - PaError result = paNoError; - - /* Copy GUID to the device info structure. Set pointer. */ - if( lpGUID == NULL ) - { - winDsDeviceInfo->lpGUID = NULL; - } - else - { - winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid; - memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) ); - } - - - hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL ); - - /** try using CoCreateInstance because DirectSoundCreate was hanging under - some circumstances - note this was probably related to the - #define BOOL short bug which has now been fixed - @todo delete this comment and the following code once we've ensured - there is no bug. - */ - /* - hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER, - &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture ); - */ - if( hr != DS_OK ) - { - DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr )); - deviceOK = FALSE; - } - else - { - /* Query device characteristics. */ - memset( &caps, 0, sizeof(caps) ); - caps.dwSize = sizeof(caps); - hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps ); - if( hr != DS_OK ) - { - DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr )); - deviceOK = FALSE; - } - else - { -#ifndef PA_NO_WMME - if( caps.dwFlags & DSCAPS_EMULDRIVER ) - { - /* If WMME supported, then reject Emulated drivers because they are lousy. */ - deviceOK = FALSE; - } -#endif - - if( deviceOK ) - { - deviceInfo->maxInputChannels = caps.dwChannels; - deviceInfo->maxOutputChannels = 0; - - deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */ - -/* constants from a WINE patch by Francois Gouget, see: - http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html - - --- - Date: Fri, 14 May 2004 10:38:12 +0200 (CEST) - From: Francois Gouget - To: Ross Bencina - Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library - - [snip] - - I give you permission to use the patch below under the BSD license. - http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html - - [snip] -*/ -#ifndef WAVE_FORMAT_48M08 -#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */ -#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */ -#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */ -#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */ -#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ -#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */ -#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */ -#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ -#endif - - /* defaultSampleRate */ - if( caps.dwChannels == 2 ) - { - if( caps.dwFormats & WAVE_FORMAT_4S16 ) - deviceInfo->defaultSampleRate = 44100.0; - else if( caps.dwFormats & WAVE_FORMAT_48S16 ) - deviceInfo->defaultSampleRate = 48000.0; - else if( caps.dwFormats & WAVE_FORMAT_2S16 ) - deviceInfo->defaultSampleRate = 22050.0; - else if( caps.dwFormats & WAVE_FORMAT_1S16 ) - deviceInfo->defaultSampleRate = 11025.0; - else if( caps.dwFormats & WAVE_FORMAT_96S16 ) - deviceInfo->defaultSampleRate = 96000.0; - else - deviceInfo->defaultSampleRate = 0.; - } - else if( caps.dwChannels == 1 ) - { - if( caps.dwFormats & WAVE_FORMAT_4M16 ) - deviceInfo->defaultSampleRate = 44100.0; - else if( caps.dwFormats & WAVE_FORMAT_48M16 ) - deviceInfo->defaultSampleRate = 48000.0; - else if( caps.dwFormats & WAVE_FORMAT_2M16 ) - deviceInfo->defaultSampleRate = 22050.0; - else if( caps.dwFormats & WAVE_FORMAT_1M16 ) - deviceInfo->defaultSampleRate = 11025.0; - else if( caps.dwFormats & WAVE_FORMAT_96M16 ) - deviceInfo->defaultSampleRate = 96000.0; - else - deviceInfo->defaultSampleRate = 0.; - } - else deviceInfo->defaultSampleRate = 0.; - } - } - - IDirectSoundCapture_Release( lpDirectSoundCapture ); - } - - if( deviceOK ) - { - deviceInfo->name = name; - - if( lpGUID == NULL ) - hostApi->info.defaultInputDevice = hostApi->info.deviceCount; - - hostApi->info.deviceCount++; - } - - return result; -} - - -/***********************************************************************************/ -PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i, deviceCount; - PaWinDsHostApiRepresentation *winDsHostApi; - DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs; - PaDeviceInfo *deviceInfoArray; - - HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */ - if( FAILED(hr) ){ - return paUnanticipatedHostError; - } - - /* initialise guid vectors so they can be safely deleted on error */ - inputNamesAndGUIDs.items = NULL; - outputNamesAndGUIDs.items = NULL; - - DSW_InitializeDSoundEntryPoints(); - - winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) ); - if( !winDsHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - winDsHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !winDsHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &winDsHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paDirectSound; - (*hostApi)->info.name = "Windows DirectSound"; - - (*hostApi)->info.deviceCount = 0; - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - - -/* DSound - enumerate devices to count them and to gather their GUIDs */ - - - result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations ); - if( result != paNoError ) - goto error; - - result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations ); - if( result != paNoError ) - goto error; - - dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs ); - - dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs ); - - if( inputNamesAndGUIDs.enumerationError != paNoError ) - { - result = inputNamesAndGUIDs.enumerationError; - goto error; - } - - if( outputNamesAndGUIDs.enumerationError != paNoError ) - { - result = outputNamesAndGUIDs.enumerationError; - goto error; - } - - deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count; - - if( deviceCount > 0 ) - { - /* allocate array for pointers to PaDeviceInfo structs */ - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all PaDeviceInfo structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all DSound specific info structs in a contiguous block */ - winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory( - winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount ); - if( !winDsHostApi->winDsDeviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i < deviceCount; ++i ) - { - PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - deviceInfo->name = 0; - (*hostApi)->deviceInfos[i] = deviceInfo; - } - - for( i=0; i< inputNamesAndGUIDs.count; ++i ) - { - result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi, - inputNamesAndGUIDs.items[i].name, - inputNamesAndGUIDs.items[i].lpGUID ); - if( result != paNoError ) - goto error; - } - - for( i=0; i< outputNamesAndGUIDs.count; ++i ) - { - result = AddOutputDeviceInfoFromDirectSound( winDsHostApi, - outputNamesAndGUIDs.items[i].name, - outputNamesAndGUIDs.items[i].lpGUID ); - if( result != paNoError ) - goto error; - } - } - - result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs ); - if( result != paNoError ) - goto error; - - result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs ); - if( result != paNoError ) - goto error; - - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( winDsHostApi ) - { - if( winDsHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winDsHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); - } - - PaUtil_FreeMemory( winDsHostApi ); - } - - TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs ); - TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs ); - - return result; -} - - -/***********************************************************************************/ -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group - */ - - if( winDsHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winDsHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); - } - - PaUtil_FreeMemory( winDsHostApi ); - - DSW_TerminateDSoundEntryPoints(); - - CoUninitialize(); -} - - -/* Set minimal latency based on whether NT or Win95. - * NT has higher latency. - */ -static int PaWinDS_GetMinSystemLatency( void ) -{ - int minLatencyMsec; - /* Set minimal latency based on whether NT or other OS. - * NT has higher latency. - */ - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof( osvi ); - GetVersionEx( &osvi ); - DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); - DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); - DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); - /* Check for NT */ - if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) - { - minLatencyMsec = PA_WIN_NT_LATENCY; - } - else if(osvi.dwMajorVersion >= 5) - { - minLatencyMsec = PA_WIN_WDM_LATENCY; - } - else - { - minLatencyMsec = PA_WIN_9X_LATENCY; - } - return minLatencyMsec; -} - -/***********************************************************************************/ -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate inputStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - /* - IMPLEMENT ME: - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported if necessary - - - check that the device supports sampleRate - - Because the buffer adapter handles conversion between all standard - sample formats, the following checks are only required if paCustomFormat - is implemented, or under some other unusual conditions. - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - */ - - return paFormatIsSupported; -} - - -/************************************************************************* -** Determine minimum number of buffers required for this host based -** on minimum latency. Latency can be optionally set by user by setting -** an environment variable. For example, to set latency to 200 msec, put: -** -** set PA_MIN_LATENCY_MSEC=200 -** -** in the AUTOEXEC.BAT file and reboot. -** If the environment variable is not set, then the latency will be determined -** based on the OS. Windows NT has higher latency than Win95. -*/ -#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") -#define PA_ENV_BUF_SIZE (32) - -static int PaWinDs_GetMinLatencyFrames( double sampleRate ) -{ - char envbuf[PA_ENV_BUF_SIZE]; - DWORD hresult; - int minLatencyMsec = 0; - - /* Let user determine minimal latency by setting environment variable. */ - hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); - if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) - { - minLatencyMsec = atoi( envbuf ); - } - else - { - minLatencyMsec = PaWinDS_GetMinSystemLatency(); -#if PA_USE_HIGH_LATENCY - PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); -#endif - - } - - return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC); -} - -#ifndef NDEBUG -#define EZ = 0 -#else -#define EZ -#endif - -/***********************************************************************************/ -/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; - PaWinDsStream *stream = 0; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat EZ, outputSampleFormat EZ; - PaSampleFormat hostInputSampleFormat EZ, hostOutputSampleFormat EZ; - unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames; - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate); - - /* IDEA: the following 3 checks could be performed by default by pa_front - unless some flag indicated otherwise */ - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) - return paInvalidChannelCount; - - /* validate hostApiSpecificStreamInfo */ - if( inputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - suggestedInputLatencyFrames = 0; - } - - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate); - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - /* check that output device can support inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate hostApiSpecificStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - suggestedOutputLatencyFrames = 0; - } - - - /* - IMPLEMENT ME: - - ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() ) - - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - check that output device can support outputSampleFormat, or that - we have the capability to convert from outputSampleFormat to - a native format - - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - - - alter sampleRate to a close allowable rate if possible / necessary - - - validate suggestedInputLatency and suggestedOutputLatency parameters, - use default values where necessary - */ - - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - - stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - - if( streamCallback ) - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &winDsHostApi->callbackStreamInterface, streamCallback, userData ); - } - else - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &winDsHostApi->blockingStreamInterface, streamCallback, userData ); - } - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - if( inputParameters ) - { - /* IMPLEMENT ME - establish which host formats are available */ - hostInputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat ); - } - - if( outputParameters ) - { - /* IMPLEMENT ME - establish which host formats are available */ - hostOutputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat ); - } - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */ - /* This next mode is required because DS can split the host buffer when it wraps around. */ - paUtilVariableHostBufferSizePartialUsageAllowed, - streamCallback, userData ); - if( result != paNoError ) - goto error; - - - stream->streamRepresentation.streamInfo.inputLatency = - PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ - stream->streamRepresentation.streamInfo.outputLatency = - PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - -/* DirectSound specific initialization */ - { - HRESULT hr; - int bytesPerDirectSoundBuffer; - DSoundWrapper *dsw; - int userLatencyFrames; - int minLatencyFrames; - - stream->timerID = 0; - dsw = &stream->directSoundWrapper; - DSW_Init( dsw ); - - /* Get system minimum latency. */ - minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate ); - - /* Let user override latency by passing latency parameter. */ - userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames) - ? suggestedInputLatencyFrames - : suggestedOutputLatencyFrames; - if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames; - - /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) - { - /* App support variable framesPerBuffer */ - stream->framesPerDSBuffer = minLatencyFrames; - - stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate; - } - else - { - /* Round up to number of buffers needed to guarantee that latency. */ - int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer; - if( numUserBuffers < 1 ) numUserBuffers = 1; - numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */ - stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers; - - stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate; - } - - { - /** @todo REVIEW: this calculation seems incorrect to me - rossb. */ - int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate); - PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency )); - } - - - /* ------------------ OUTPUT */ - if( outputParameters ) - { - /* - PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ]; - DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device)); - */ - - bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short); - if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) - { - result = paBufferTooSmall; - goto error; - } - else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) - { - result = paBufferTooBig; - goto error; - } - - - hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID, - &dsw->dsw_pDirectSound, NULL ); - if( hr != DS_OK ) - { - ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n")); - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - hr = DSW_InitOutputBuffer( dsw, - (unsigned long) (sampleRate + 0.5), - (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer ); - DBUG(("DSW_InitOutputBuffer() returns %x\n", hr)); - if( hr != DS_OK ) - { - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - /* Calculate value used in latency calculation to avoid real-time divides. */ - stream->secondsPerHostByte = 1.0 / - (stream->bufferProcessor.bytesPerHostOutputSample * - outputChannelCount * sampleRate); - } - - /* ------------------ INPUT */ - if( inputParameters ) - { - /* - PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ]; - DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device)); - */ - - bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short); - if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) - { - result = paBufferTooSmall; - goto error; - } - else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) - { - result = paBufferTooBig; - goto error; - } - - hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID, - &dsw->dsw_pDirectSoundCapture, NULL ); - if( hr != DS_OK ) - { - ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n")); - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - hr = DSW_InitInputBuffer( dsw, - (unsigned long) (sampleRate + 0.5), - (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer ); - DBUG(("DSW_InitInputBuffer() returns %x\n", hr)); - if( hr != DS_OK ) - { - ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr)); - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - } - - } - - *s = (PaStream*)stream; - - return result; - -error: - if( stream ) - PaUtil_FreeMemory( stream ); - - return result; -} - - -/***********************************************************************************/ -static PaError Pa_TimeSlice( PaWinDsStream *stream ) -{ - PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/ - DSoundWrapper *dsw; - long numFrames = 0; - long bytesEmpty = 0; - long bytesFilled = 0; - long bytesToXfer = 0; - long framesToXfer = 0; - long numInFramesReady = 0; - long numOutFramesReady = 0; - long bytesProcessed; - HRESULT hresult; - double outputLatency = 0; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */ - -/* Input */ - LPBYTE lpInBuf1 = NULL; - LPBYTE lpInBuf2 = NULL; - DWORD dwInSize1 = 0; - DWORD dwInSize2 = 0; -/* Output */ - LPBYTE lpOutBuf1 = NULL; - LPBYTE lpOutBuf2 = NULL; - DWORD dwOutSize1 = 0; - DWORD dwOutSize2 = 0; - - dsw = &stream->directSoundWrapper; - - /* How much input data is available? */ - if( stream->bufferProcessor.inputChannelCount > 0 ) - { - DSW_QueryInputFilled( dsw, &bytesFilled ); - framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame; - outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; - - /** @todo Check for overflow */ - } - - /* How much output room is available? */ - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - UINT previousUnderflowCount = dsw->dsw_OutputUnderflows; - DSW_QueryOutputSpace( dsw, &bytesEmpty ); - framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame; - - /* Check for underflow */ - if( dsw->dsw_OutputUnderflows != previousUnderflowCount ) - stream->callbackFlags |= paOutputUnderflow; - } - - if( (numInFramesReady > 0) && (numOutFramesReady > 0) ) - { - framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady; - } - - if( framesToXfer > 0 ) - { - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - /* The outputBufferDacTime parameter should indicates the time at which - the first sample of the output buffer is heard at the DACs. */ - timeInfo.currentTime = PaUtil_GetTime(); - timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; - - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags ); - stream->callbackFlags = 0; - - /* Input */ - if( stream->bufferProcessor.inputChannelCount > 0 ) - { - bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame; - hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, - dsw->dsw_ReadOffset, bytesToXfer, - (void **) &lpInBuf1, &dwInSize1, - (void **) &lpInBuf2, &dwInSize2, 0); - if (hresult != DS_OK) - { - ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult)); - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); - goto error2; - } - - numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame; - PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames ); - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 ); - /* Is input split into two regions. */ - if( dwInSize2 > 0 ) - { - numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame; - PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames ); - PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 ); - } - } - - /* Output */ - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame; - hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer, - dsw->dsw_WriteOffset, bytesToXfer, - (void **) &lpOutBuf1, &dwOutSize1, - (void **) &lpOutBuf2, &dwOutSize2, 0); - if (hresult != DS_OK) - { - ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult)); - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); - goto error1; - } - - numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame; - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames ); - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 ); - - /* Is output split into two regions. */ - if( dwOutSize2 > 0 ) - { - numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame; - PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames ); - PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 ); - } - } - - result = paContinue; - numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result ); - stream->framesWritten += numFrames; - - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - /* FIXME: an underflow could happen here */ - - /* Update our buffer offset and unlock sound buffer */ - bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame; - dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize; - IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2); - dsw->dsw_FramesWritten += numFrames; - } - -error1: - if( stream->bufferProcessor.inputChannelCount > 0 ) - { - /* FIXME: an overflow could happen here */ - - /* Update our buffer offset and unlock sound buffer */ - bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame; - dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize; - IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2); - } -error2: - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames ); - - } - - return result; -} -/*******************************************************************/ -static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) -{ - PaWinDsStream *stream; - - /* suppress unused variable warnings */ - (void) uID; - (void) uMsg; - (void) dw1; - (void) dw2; - - stream = (PaWinDsStream *) dwUser; - if( stream == NULL ) return; - - if( stream->isActive ) - { - if( stream->abortProcessing ) - { - stream->isActive = 0; - } - else if( stream->stopProcessing ) - { - DSoundWrapper *dsw = &stream->directSoundWrapper; - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - DSW_ZeroEmptySpace( dsw ); - /* clear isActive when all sound played */ - if( dsw->dsw_FramesPlayed >= stream->framesWritten ) - { - stream->isActive = 0; - } - } - else - { - stream->isActive = 0; - } - } - else - { - if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */ - { - /* FIXME implement handling of paComplete and paAbort if possible */ - stream->stopProcessing = 1; - } - } - - if( !stream->isActive ){ - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - } -} - -/*********************************************************************************** - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaWinDsStream *stream = (PaWinDsStream*)s; - - DSW_Term( &stream->directSoundWrapper ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - - return result; -} - -/***********************************************************************************/ -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinDsStream *stream = (PaWinDsStream*)s; - HRESULT hr; - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - if( stream->bufferProcessor.inputChannelCount > 0 ) - { - hr = DSW_StartInput( &stream->directSoundWrapper ); - DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr)); - if( hr != DS_OK ) - { - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - } - - stream->framesWritten = 0; - stream->callbackFlags = 0; - - stream->abortProcessing = 0; - stream->stopProcessing = 0; - stream->isActive = 1; - - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */ - result = Pa_TimeSlice( stream ); - if( result != paNoError ) return result; // FIXME - what if finished? - - hr = DSW_StartOutput( &stream->directSoundWrapper ); - DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr)); - if( hr != DS_OK ) - { - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - } - - - /* Create timer that will wake us up so we can fill the DSound buffer. */ - { - int resolution; - int framesPerWakeup = stream->framesPerDSBuffer / 4; - int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate; - if( msecPerWakeup < 10 ) msecPerWakeup = 10; - else if( msecPerWakeup > 100 ) msecPerWakeup = 100; - resolution = msecPerWakeup/4; - stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback, - (DWORD) stream, TIME_PERIODIC ); - } - if( stream->timerID == 0 ) - { - stream->isActive = 0; - result = paUnanticipatedHostError; - PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); - goto error; - } - - stream->isStarted = TRUE; - -error: - return result; -} - - -/***********************************************************************************/ -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinDsStream *stream = (PaWinDsStream*)s; - HRESULT hr; - int timeoutMsec; - - stream->stopProcessing = 1; - /* Set timeout at 20% beyond maximum time we might wait. */ - timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate); - while( stream->isActive && (timeoutMsec > 0) ) - { - Sleep(10); - timeoutMsec -= 10; - } - if( stream->timerID != 0 ) - { - timeKillEvent(stream->timerID); /* Stop callback timer. */ - stream->timerID = 0; - } - - - if( stream->bufferProcessor.outputChannelCount > 0 ) - { - hr = DSW_StopOutput( &stream->directSoundWrapper ); - } - - if( stream->bufferProcessor.inputChannelCount > 0 ) - { - hr = DSW_StopInput( &stream->directSoundWrapper ); - } - - stream->isStarted = FALSE; - - return result; -} - - -/***********************************************************************************/ -static PaError AbortStream( PaStream *s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - stream->abortProcessing = 1; - return StopStream( s ); -} - - -/***********************************************************************************/ -static PaError IsStreamStopped( PaStream *s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - return !stream->isStarted; -} - - -/***********************************************************************************/ -static PaError IsStreamActive( PaStream *s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - return stream->isActive; -} - -/***********************************************************************************/ -static PaTime GetStreamTime( PaStream *s ) -{ - /* suppress unused variable warnings */ - (void) s; - - -/* - new behavior for GetStreamTime is to return a stream based seconds clock - used for the outTime parameter to the callback. - FIXME: delete this comment when the other unnecessary related code has - been cleaned from this file. - - PaWinDsStream *stream = (PaWinDsStream*)s; - DSoundWrapper *dsw; - dsw = &stream->directSoundWrapper; - return dsw->dsw_FramesPlayed; -*/ - return PaUtil_GetTime(); -} - - -/***********************************************************************************/ -static double GetStreamCpuLoad( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - - -/*********************************************************************************** - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -/***********************************************************************************/ -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) buffer; - (void) frames; - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return paNoError; -} - - -/***********************************************************************************/ -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - -/***********************************************************************************/ -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWinDsStream *stream = (PaWinDsStream*)s; - - /* suppress unused variable warnings */ - (void) stream; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - - return 0; -} - - - +/* + * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $ + * Portable Audio I/O Library DirectSound implementation + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + + @todo implement paInputOverflow callback status flag + + @todo implement paNeverDropInput. + + @todo implement host api specific extension to set i/o buffer sizes in frames + + @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.) + + @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable + + @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into + a native portaudio error code. Standard DirectSound result codes are documented at msdn. + + @todo implement IsFormatSupported + + @todo check that CoInitialize() CoUninitialize() are always correctly + paired, even in error cases. + + @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error"). + + @todo make sure all buffers have been played before stopping the stream + when the stream callback returns paComplete + + old TODOs from phil, need to work out if these have been done: + O- fix "patest_stop.c" +*/ + +#include +#include /* strlen() */ + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "dsound_wrapper.h" + +#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ +#pragma comment( lib, "dsound.lib" ) +#pragma comment( lib, "winmm.lib" ) +#endif + + +#define PRINT(x) /* { printf x; fflush(stdout); } */ +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +#define PA_USE_HIGH_LATENCY (0) +#if PA_USE_HIGH_LATENCY +#define PA_WIN_9X_LATENCY (500) +#define PA_WIN_NT_LATENCY (600) +#else +#define PA_WIN_9X_LATENCY (140) +#define PA_WIN_NT_LATENCY (280) +#endif + +#define PA_WIN_WDM_LATENCY (120) + +#define SECONDS_PER_MSEC (0.001) +#define MSEC_PER_SECOND (1000) + +/* prototypes for functions declared in this file */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + + +/* FIXME: should convert hr to a string */ +#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \ + PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" ) + +/************************************************* DX Prototypes **********/ +static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); + +/************************************************************************************/ +/********************** Structures **************************************************/ +/************************************************************************************/ +/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct PaWinDsDeviceInfo +{ + GUID guid; + GUID *lpGUID; + double sampleRates[3]; +} PaWinDsDeviceInfo; + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + /* implementation specific data goes here */ + PaWinDsDeviceInfo *winDsDeviceInfos; + +} PaWinDsHostApiRepresentation; + +/* PaWinDsStream - a stream data structure specifically for this implementation */ + +typedef struct PaWinDsStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + +/* DirectSound specific data. */ + DSoundWrapper directSoundWrapper; + MMRESULT timerID; + BOOL ifInsideCallback; /* Test for reentrancy. */ + int framesPerDSBuffer; + double framesWritten; + double secondsPerHostByte; /* Used to optimize latency calculation for outTime */ + + PaStreamCallbackFlags callbackFlags; + +/* FIXME - move all below to PaUtilStreamRepresentation */ + volatile int isStarted; + volatile int isActive; + volatile int stopProcessing; /* stop thread once existing buffers have been returned */ + volatile int abortProcessing; /* stop thread immediately */ +} PaWinDsStream; + + +/************************************************************************************ +** Duplicate the input string using the allocations allocator. +** A NULL string is converted to a zero length string. +** If memory cannot be allocated, NULL is returned. +**/ +static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src ) +{ + char *result = 0; + + if( src != NULL ) + { + size_t len = strlen(src); + result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) ); + if( result ) + memcpy( (void *) result, src, len+1 ); + } + else + { + result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 ); + if( result ) + result[0] = '\0'; + } + + return result; +} + +/************************************************************************************ +** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary +** information during device enumeration. +*/ +typedef struct DSDeviceNameAndGUID{ + char *name; // allocated from parent's allocations, never deleted by this structure + GUID guid; + LPGUID lpGUID; +} DSDeviceNameAndGUID; + +typedef struct DSDeviceNameAndGUIDVector{ + PaUtilAllocationGroup *allocations; + PaError enumerationError; + + int count; + int free; + DSDeviceNameAndGUID *items; // Allocated using LocalAlloc() +} DSDeviceNameAndGUIDVector; + +static PaError InitializeDSDeviceNameAndGUIDVector( + DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations ) +{ + PaError result = paNoError; + + guidVector->allocations = allocations; + guidVector->enumerationError = paNoError; + + guidVector->count = 0; + guidVector->free = 8; + guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free ); + if( guidVector->items == NULL ) + result = paInsufficientMemory; + + return result; +} + +static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector ) +{ + PaError result = paNoError; + DSDeviceNameAndGUID *newItems; + int i; + + /* double size of vector */ + int size = guidVector->count + guidVector->free; + guidVector->free += size; + + newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 ); + if( newItems == NULL ) + { + result = paInsufficientMemory; + } + else + { + for( i=0; i < guidVector->count; ++i ) + { + newItems[i].name = guidVector->items[i].name; + if( guidVector->items[i].lpGUID == NULL ) + { + newItems[i].lpGUID = NULL; + } + else + { + newItems[i].lpGUID = &newItems[i].guid; + memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );; + } + } + + LocalFree( guidVector->items ); + guidVector->items = newItems; + } + + return result; +} + +/* + it's safe to call DSDeviceNameAndGUIDVector multiple times +*/ +static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector ) +{ + PaError result = paNoError; + + if( guidVector->items != NULL ) + { + if( LocalFree( guidVector->items ) != NULL ) + result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */ + + guidVector->items = NULL; + } + + return result; +} + +/************************************************************************************ +** Collect preliminary device information during DirectSound enumeration +*/ +static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext; + PaError error; + + (void) lpszDrvName; /* unused variable */ + + if( namesAndGUIDs->free == 0 ) + { + error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs ); + if( error != paNoError ) + { + namesAndGUIDs->enumerationError = error; + return FALSE; + } + } + + /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */ + if( lpGUID == NULL ) + { + namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL; + } + else + { + namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = + &namesAndGUIDs->items[namesAndGUIDs->count].guid; + + memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) ); + } + + namesAndGUIDs->items[namesAndGUIDs->count].name = + DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc ); + if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL ) + { + namesAndGUIDs->enumerationError = paInsufficientMemory; + return FALSE; + } + + ++namesAndGUIDs->count; + --namesAndGUIDs->free; + + return TRUE; +} + + +#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */ +static double defaultSampleRateSearchOrder_[] = + { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0, + 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; + + +/************************************************************************************ +** Extract capabilities from an output device, and add it to the device info list +** if successful. This function assumes that there is enough room in the +** device info list to accomodate all entries. +** +** The device will not be added to the device list if any errors are encountered. +*/ +static PaError AddOutputDeviceInfoFromDirectSound( + PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID ) +{ + PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount]; + PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount]; + HRESULT hr; + LPDIRECTSOUND lpDirectSound; + DSCAPS caps; + int deviceOK = TRUE; + PaError result = paNoError; + int i; + + /* Copy GUID to the device info structure. Set pointer. */ + if( lpGUID == NULL ) + { + winDsDeviceInfo->lpGUID = NULL; + } + else + { + memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) ); + winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid; + } + + + /* Create a DirectSound object for the specified GUID + Note that using CoCreateInstance doesn't work on windows CE. + */ + hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL ); + + /** try using CoCreateInstance because DirectSoundCreate was hanging under + some circumstances - note this was probably related to the + #define BOOL short bug which has now been fixed + @todo delete this comment and the following code once we've ensured + there is no bug. + */ + /* + hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectSound, (void**)&lpDirectSound ); + + if( hr == S_OK ) + { + hr = IDirectSound_Initialize( lpDirectSound, lpGUID ); + } + */ + + if( hr != DS_OK ) + { + DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr )); + deviceOK = FALSE; + } + else + { + /* Query device characteristics. */ + memset( &caps, 0, sizeof(caps) ); + caps.dwSize = sizeof(caps); + hr = IDirectSound_GetCaps( lpDirectSound, &caps ); + if( hr != DS_OK ) + { + DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr )); + deviceOK = FALSE; + } + else + { + +#ifndef PA_NO_WMME + if( caps.dwFlags & DSCAPS_EMULDRIVER ) + { + /* If WMME supported, then reject Emulated drivers because they are lousy. */ + deviceOK = FALSE; + } +#endif + + if( deviceOK ) + { + deviceInfo->maxInputChannels = 0; + /* Mono or stereo device? */ + deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; + + deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */ + + /* initialize defaultSampleRate */ + + if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) + { + /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */ + deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; + + for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i ) + { + if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate + && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){ + + deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i]; + break; + } + } + } + else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate ) + { + if( caps.dwMinSecondarySampleRate == 0 ) + { + /* + ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !! + ** But it supports continuous sampling. + ** So fake range of rates, and hope it really supports it. + */ + deviceInfo->defaultSampleRate = 44100.0f; + + DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex )); + } + else + { + deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; + } + } + else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) ) + { + /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000. + ** But we know that they really support a range of rates! + ** So when we see a ridiculous set of rates, assume it is a range. + */ + deviceInfo->defaultSampleRate = 44100.0f; + DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex )); + } + else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate; + + + //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate ); + // dwFlags | DSCAPS_CONTINUOUSRATE + } + } + + IDirectSound_Release( lpDirectSound ); + } + + if( deviceOK ) + { + deviceInfo->name = name; + + if( lpGUID == NULL ) + hostApi->info.defaultOutputDevice = hostApi->info.deviceCount; + + hostApi->info.deviceCount++; + } + + return result; +} + + +/************************************************************************************ +** Extract capabilities from an input device, and add it to the device info list +** if successful. This function assumes that there is enough room in the +** device info list to accomodate all entries. +** +** The device will not be added to the device list if any errors are encountered. +*/ +static PaError AddInputDeviceInfoFromDirectSoundCapture( + PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID ) +{ + PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount]; + PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount]; + HRESULT hr; + LPDIRECTSOUNDCAPTURE lpDirectSoundCapture; + DSCCAPS caps; + int deviceOK = TRUE; + PaError result = paNoError; + + /* Copy GUID to the device info structure. Set pointer. */ + if( lpGUID == NULL ) + { + winDsDeviceInfo->lpGUID = NULL; + } + else + { + winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid; + memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) ); + } + + + hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL ); + + /** try using CoCreateInstance because DirectSoundCreate was hanging under + some circumstances - note this was probably related to the + #define BOOL short bug which has now been fixed + @todo delete this comment and the following code once we've ensured + there is no bug. + */ + /* + hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER, + &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture ); + */ + if( hr != DS_OK ) + { + DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr )); + deviceOK = FALSE; + } + else + { + /* Query device characteristics. */ + memset( &caps, 0, sizeof(caps) ); + caps.dwSize = sizeof(caps); + hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps ); + if( hr != DS_OK ) + { + DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr )); + deviceOK = FALSE; + } + else + { +#ifndef PA_NO_WMME + if( caps.dwFlags & DSCAPS_EMULDRIVER ) + { + /* If WMME supported, then reject Emulated drivers because they are lousy. */ + deviceOK = FALSE; + } +#endif + + if( deviceOK ) + { + deviceInfo->maxInputChannels = caps.dwChannels; + deviceInfo->maxOutputChannels = 0; + + deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */ + +/* constants from a WINE patch by Francois Gouget, see: + http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html + + --- + Date: Fri, 14 May 2004 10:38:12 +0200 (CEST) + From: Francois Gouget + To: Ross Bencina + Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library + + [snip] + + I give you permission to use the patch below under the BSD license. + http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html + + [snip] +*/ +#ifndef WAVE_FORMAT_48M08 +#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */ +#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */ +#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */ +#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */ +#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ +#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */ +#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */ +#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ +#endif + + /* defaultSampleRate */ + if( caps.dwChannels == 2 ) + { + if( caps.dwFormats & WAVE_FORMAT_4S16 ) + deviceInfo->defaultSampleRate = 44100.0; + else if( caps.dwFormats & WAVE_FORMAT_48S16 ) + deviceInfo->defaultSampleRate = 48000.0; + else if( caps.dwFormats & WAVE_FORMAT_2S16 ) + deviceInfo->defaultSampleRate = 22050.0; + else if( caps.dwFormats & WAVE_FORMAT_1S16 ) + deviceInfo->defaultSampleRate = 11025.0; + else if( caps.dwFormats & WAVE_FORMAT_96S16 ) + deviceInfo->defaultSampleRate = 96000.0; + else + deviceInfo->defaultSampleRate = 0.; + } + else if( caps.dwChannels == 1 ) + { + if( caps.dwFormats & WAVE_FORMAT_4M16 ) + deviceInfo->defaultSampleRate = 44100.0; + else if( caps.dwFormats & WAVE_FORMAT_48M16 ) + deviceInfo->defaultSampleRate = 48000.0; + else if( caps.dwFormats & WAVE_FORMAT_2M16 ) + deviceInfo->defaultSampleRate = 22050.0; + else if( caps.dwFormats & WAVE_FORMAT_1M16 ) + deviceInfo->defaultSampleRate = 11025.0; + else if( caps.dwFormats & WAVE_FORMAT_96M16 ) + deviceInfo->defaultSampleRate = 96000.0; + else + deviceInfo->defaultSampleRate = 0.; + } + else deviceInfo->defaultSampleRate = 0.; + } + } + + IDirectSoundCapture_Release( lpDirectSoundCapture ); + } + + if( deviceOK ) + { + deviceInfo->name = name; + + if( lpGUID == NULL ) + hostApi->info.defaultInputDevice = hostApi->info.deviceCount; + + hostApi->info.deviceCount++; + } + + return result; +} + + +/***********************************************************************************/ +PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i, deviceCount; + PaWinDsHostApiRepresentation *winDsHostApi; + DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs; + PaDeviceInfo *deviceInfoArray; + + HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */ + if( FAILED(hr) ){ + return paUnanticipatedHostError; + } + + /* initialise guid vectors so they can be safely deleted on error */ + inputNamesAndGUIDs.items = NULL; + outputNamesAndGUIDs.items = NULL; + + DSW_InitializeDSoundEntryPoints(); + + winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) ); + if( !winDsHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + winDsHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !winDsHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &winDsHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paDirectSound; + (*hostApi)->info.name = "Windows DirectSound"; + + (*hostApi)->info.deviceCount = 0; + (*hostApi)->info.defaultInputDevice = paNoDevice; + (*hostApi)->info.defaultOutputDevice = paNoDevice; + + +/* DSound - enumerate devices to count them and to gather their GUIDs */ + + + result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations ); + if( result != paNoError ) + goto error; + + result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations ); + if( result != paNoError ) + goto error; + + dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs ); + + dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs ); + + if( inputNamesAndGUIDs.enumerationError != paNoError ) + { + result = inputNamesAndGUIDs.enumerationError; + goto error; + } + + if( outputNamesAndGUIDs.enumerationError != paNoError ) + { + result = outputNamesAndGUIDs.enumerationError; + goto error; + } + + deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count; + + if( deviceCount > 0 ) + { + /* allocate array for pointers to PaDeviceInfo structs */ + (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); + if( !(*hostApi)->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all PaDeviceInfo structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all DSound specific info structs in a contiguous block */ + winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory( + winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount ); + if( !winDsHostApi->winDsDeviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + for( i=0; i < deviceCount; ++i ) + { + PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + deviceInfo->name = 0; + (*hostApi)->deviceInfos[i] = deviceInfo; + } + + for( i=0; i< inputNamesAndGUIDs.count; ++i ) + { + result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi, + inputNamesAndGUIDs.items[i].name, + inputNamesAndGUIDs.items[i].lpGUID ); + if( result != paNoError ) + goto error; + } + + for( i=0; i< outputNamesAndGUIDs.count; ++i ) + { + result = AddOutputDeviceInfoFromDirectSound( winDsHostApi, + outputNamesAndGUIDs.items[i].name, + outputNamesAndGUIDs.items[i].lpGUID ); + if( result != paNoError ) + goto error; + } + } + + result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs ); + if( result != paNoError ) + goto error; + + result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs ); + if( result != paNoError ) + goto error; + + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyRead, PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + return result; + +error: + if( winDsHostApi ) + { + if( winDsHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winDsHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); + } + + PaUtil_FreeMemory( winDsHostApi ); + } + + TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs ); + TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs ); + + return result; +} + + +/***********************************************************************************/ +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; + + /* + IMPLEMENT ME: + - clean up any resources not handled by the allocation group + */ + + if( winDsHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winDsHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); + } + + PaUtil_FreeMemory( winDsHostApi ); + + DSW_TerminateDSoundEntryPoints(); + + CoUninitialize(); +} + + +/* Set minimal latency based on whether NT or Win95. + * NT has higher latency. + */ +static int PaWinDS_GetMinSystemLatency( void ) +{ + int minLatencyMsec; + /* Set minimal latency based on whether NT or other OS. + * NT has higher latency. + */ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof( osvi ); + GetVersionEx( &osvi ); + DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); + DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); + DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); + /* Check for NT */ + if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) + { + minLatencyMsec = PA_WIN_NT_LATENCY; + } + else if(osvi.dwMajorVersion >= 5) + { + minLatencyMsec = PA_WIN_WDM_LATENCY; + } + else + { + minLatencyMsec = PA_WIN_9X_LATENCY; + } + return minLatencyMsec; +} + +/***********************************************************************************/ +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + } + + /* + IMPLEMENT ME: + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported if necessary + + - check that the device supports sampleRate + + Because the buffer adapter handles conversion between all standard + sample formats, the following checks are only required if paCustomFormat + is implemented, or under some other unusual conditions. + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + */ + + return paFormatIsSupported; +} + + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the AUTOEXEC.BAT file and reboot. +** If the environment variable is not set, then the latency will be determined +** based on the OS. Windows NT has higher latency than Win95. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") +#define PA_ENV_BUF_SIZE (32) + +static int PaWinDs_GetMinLatencyFrames( double sampleRate ) +{ + char envbuf[PA_ENV_BUF_SIZE]; + DWORD hresult; + int minLatencyMsec = 0; + + /* Let user determine minimal latency by setting environment variable. */ + hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + minLatencyMsec = atoi( envbuf ); + } + else + { + minLatencyMsec = PaWinDS_GetMinSystemLatency(); +#if PA_USE_HIGH_LATENCY + PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); +#endif + + } + + return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC); +} + +#ifndef NDEBUG +#define EZ = 0 +#else +#define EZ +#endif + +/***********************************************************************************/ +/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ + +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result = paNoError; + PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; + PaWinDsStream *stream = 0; + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat EZ, outputSampleFormat EZ; + PaSampleFormat hostInputSampleFormat EZ, hostOutputSampleFormat EZ; + unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate); + + /* IDEA: the following 3 checks could be performed by default by pa_front + unless some flag indicated otherwise */ + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate hostApiSpecificStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + suggestedInputLatencyFrames = 0; + } + + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate); + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate hostApiSpecificStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + suggestedOutputLatencyFrames = 0; + } + + + /* + IMPLEMENT ME: + + ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() ) + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + + - alter sampleRate to a close allowable rate if possible / necessary + + - validate suggestedInputLatency and suggestedOutputLatency parameters, + use default values where necessary + */ + + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + + stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + if( streamCallback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &winDsHostApi->callbackStreamInterface, streamCallback, userData ); + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &winDsHostApi->blockingStreamInterface, streamCallback, userData ); + } + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + + if( inputParameters ) + { + /* IMPLEMENT ME - establish which host formats are available */ + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat ); + } + + if( outputParameters ) + { + /* IMPLEMENT ME - establish which host formats are available */ + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat ); + } + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */ + /* This next mode is required because DS can split the host buffer when it wraps around. */ + paUtilVariableHostBufferSizePartialUsageAllowed, + streamCallback, userData ); + if( result != paNoError ) + goto error; + + + stream->streamRepresentation.streamInfo.inputLatency = + PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ + stream->streamRepresentation.streamInfo.outputLatency = + PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */ + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + +/* DirectSound specific initialization */ + { + HRESULT hr; + int bytesPerDirectSoundBuffer; + DSoundWrapper *dsw; + int userLatencyFrames; + int minLatencyFrames; + + stream->timerID = 0; + dsw = &stream->directSoundWrapper; + DSW_Init( dsw ); + + /* Get system minimum latency. */ + minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate ); + + /* Let user override latency by passing latency parameter. */ + userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames) + ? suggestedInputLatencyFrames + : suggestedOutputLatencyFrames; + if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames; + + /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */ + if( framesPerBuffer == paFramesPerBufferUnspecified ) + { + /* App support variable framesPerBuffer */ + stream->framesPerDSBuffer = minLatencyFrames; + + stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate; + } + else + { + /* Round up to number of buffers needed to guarantee that latency. */ + int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer; + if( numUserBuffers < 1 ) numUserBuffers = 1; + numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */ + stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers; + + stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate; + } + + { + /** @todo REVIEW: this calculation seems incorrect to me - rossb. */ + int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate); + PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency )); + } + + + /* ------------------ OUTPUT */ + if( outputParameters ) + { + /* + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ]; + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device)); + */ + + bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short); + if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) + { + result = paBufferTooSmall; + goto error; + } + else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) + { + result = paBufferTooBig; + goto error; + } + + + hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID, + &dsw->dsw_pDirectSound, NULL ); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n")); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + hr = DSW_InitOutputBuffer( dsw, + (unsigned long) (sampleRate + 0.5), + (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer ); + DBUG(("DSW_InitOutputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + /* Calculate value used in latency calculation to avoid real-time divides. */ + stream->secondsPerHostByte = 1.0 / + (stream->bufferProcessor.bytesPerHostOutputSample * + outputChannelCount * sampleRate); + } + + /* ------------------ INPUT */ + if( inputParameters ) + { + /* + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ]; + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device)); + */ + + bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short); + if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) + { + result = paBufferTooSmall; + goto error; + } + else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) + { + result = paBufferTooBig; + goto error; + } + + hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID, + &dsw->dsw_pDirectSoundCapture, NULL ); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n")); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + hr = DSW_InitInputBuffer( dsw, + (unsigned long) (sampleRate + 0.5), + (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer ); + DBUG(("DSW_InitInputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr)); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + } + + } + + *s = (PaStream*)stream; + + return result; + +error: + if( stream ) + PaUtil_FreeMemory( stream ); + + return result; +} + + +/***********************************************************************************/ +static PaError Pa_TimeSlice( PaWinDsStream *stream ) +{ + PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/ + DSoundWrapper *dsw; + long numFrames = 0; + long bytesEmpty = 0; + long bytesFilled = 0; + long bytesToXfer = 0; + long framesToXfer = 0; + long numInFramesReady = 0; + long numOutFramesReady = 0; + long bytesProcessed; + HRESULT hresult; + double outputLatency = 0; + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */ + +/* Input */ + LPBYTE lpInBuf1 = NULL; + LPBYTE lpInBuf2 = NULL; + DWORD dwInSize1 = 0; + DWORD dwInSize2 = 0; +/* Output */ + LPBYTE lpOutBuf1 = NULL; + LPBYTE lpOutBuf2 = NULL; + DWORD dwOutSize1 = 0; + DWORD dwOutSize2 = 0; + + dsw = &stream->directSoundWrapper; + + /* How much input data is available? */ + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + DSW_QueryInputFilled( dsw, &bytesFilled ); + framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame; + outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; + + /** @todo Check for overflow */ + } + + /* How much output room is available? */ + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + UINT previousUnderflowCount = dsw->dsw_OutputUnderflows; + DSW_QueryOutputSpace( dsw, &bytesEmpty ); + framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame; + + /* Check for underflow */ + if( dsw->dsw_OutputUnderflows != previousUnderflowCount ) + stream->callbackFlags |= paOutputUnderflow; + } + + if( (numInFramesReady > 0) && (numOutFramesReady > 0) ) + { + framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady; + } + + if( framesToXfer > 0 ) + { + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + /* The outputBufferDacTime parameter should indicates the time at which + the first sample of the output buffer is heard at the DACs. */ + timeInfo.currentTime = PaUtil_GetTime(); + timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; + + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags ); + stream->callbackFlags = 0; + + /* Input */ + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame; + hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, + dsw->dsw_ReadOffset, bytesToXfer, + (void **) &lpInBuf1, &dwInSize1, + (void **) &lpInBuf2, &dwInSize2, 0); + if (hresult != DS_OK) + { + ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult)); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); + goto error2; + } + + numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame; + PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 ); + /* Is input split into two regions. */ + if( dwInSize2 > 0 ) + { + numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame; + PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 ); + } + } + + /* Output */ + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame; + hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer, + dsw->dsw_WriteOffset, bytesToXfer, + (void **) &lpOutBuf1, &dwOutSize1, + (void **) &lpOutBuf2, &dwOutSize2, 0); + if (hresult != DS_OK) + { + ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult)); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); + goto error1; + } + + numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame; + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 ); + + /* Is output split into two regions. */ + if( dwOutSize2 > 0 ) + { + numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame; + PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 ); + } + } + + result = paContinue; + numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result ); + stream->framesWritten += numFrames; + + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + /* FIXME: an underflow could happen here */ + + /* Update our buffer offset and unlock sound buffer */ + bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame; + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2); + dsw->dsw_FramesWritten += numFrames; + } + +error1: + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + /* FIXME: an overflow could happen here */ + + /* Update our buffer offset and unlock sound buffer */ + bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame; + dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize; + IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2); + } +error2: + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames ); + + } + + return result; +} +/*******************************************************************/ +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + PaWinDsStream *stream; + + /* suppress unused variable warnings */ + (void) uID; + (void) uMsg; + (void) dw1; + (void) dw2; + + stream = (PaWinDsStream *) dwUser; + if( stream == NULL ) return; + + if( stream->isActive ) + { + if( stream->abortProcessing ) + { + stream->isActive = 0; + } + else if( stream->stopProcessing ) + { + DSoundWrapper *dsw = &stream->directSoundWrapper; + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + DSW_ZeroEmptySpace( dsw ); + /* clear isActive when all sound played */ + if( dsw->dsw_FramesPlayed >= stream->framesWritten ) + { + stream->isActive = 0; + } + } + else + { + stream->isActive = 0; + } + } + else + { + if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */ + { + /* FIXME implement handling of paComplete and paAbort if possible */ + stream->stopProcessing = 1; + } + } + + if( !stream->isActive ){ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + } + } +} + +/*********************************************************************************** + When CloseStream() is called, the multi-api layer ensures that + the stream has already been stopped or aborted. +*/ +static PaError CloseStream( PaStream* s ) +{ + PaError result = paNoError; + PaWinDsStream *stream = (PaWinDsStream*)s; + + DSW_Term( &stream->directSoundWrapper ); + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + + return result; +} + +/***********************************************************************************/ +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinDsStream *stream = (PaWinDsStream*)s; + HRESULT hr; + + PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); + + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + hr = DSW_StartInput( &stream->directSoundWrapper ); + DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + } + + stream->framesWritten = 0; + stream->callbackFlags = 0; + + stream->abortProcessing = 0; + stream->stopProcessing = 0; + stream->isActive = 1; + + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */ + result = Pa_TimeSlice( stream ); + if( result != paNoError ) return result; // FIXME - what if finished? + + hr = DSW_StartOutput( &stream->directSoundWrapper ); + DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + } + + + /* Create timer that will wake us up so we can fill the DSound buffer. */ + { + int resolution; + int framesPerWakeup = stream->framesPerDSBuffer / 4; + int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate; + if( msecPerWakeup < 10 ) msecPerWakeup = 10; + else if( msecPerWakeup > 100 ) msecPerWakeup = 100; + resolution = msecPerWakeup/4; + stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback, + (DWORD) stream, TIME_PERIODIC ); + } + if( stream->timerID == 0 ) + { + stream->isActive = 0; + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + + stream->isStarted = TRUE; + +error: + return result; +} + + +/***********************************************************************************/ +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinDsStream *stream = (PaWinDsStream*)s; + HRESULT hr; + int timeoutMsec; + + stream->stopProcessing = 1; + /* Set timeout at 20% beyond maximum time we might wait. */ + timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate); + while( stream->isActive && (timeoutMsec > 0) ) + { + Sleep(10); + timeoutMsec -= 10; + } + if( stream->timerID != 0 ) + { + timeKillEvent(stream->timerID); /* Stop callback timer. */ + stream->timerID = 0; + } + + + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + hr = DSW_StopOutput( &stream->directSoundWrapper ); + } + + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + hr = DSW_StopInput( &stream->directSoundWrapper ); + } + + stream->isStarted = FALSE; + + return result; +} + + +/***********************************************************************************/ +static PaError AbortStream( PaStream *s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + stream->abortProcessing = 1; + return StopStream( s ); +} + + +/***********************************************************************************/ +static PaError IsStreamStopped( PaStream *s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + return !stream->isStarted; +} + + +/***********************************************************************************/ +static PaError IsStreamActive( PaStream *s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + return stream->isActive; +} + +/***********************************************************************************/ +static PaTime GetStreamTime( PaStream *s ) +{ + /* suppress unused variable warnings */ + (void) s; + + +/* + new behavior for GetStreamTime is to return a stream based seconds clock + used for the outTime parameter to the callback. + FIXME: delete this comment when the other unnecessary related code has + been cleaned from this file. + + PaWinDsStream *stream = (PaWinDsStream*)s; + DSoundWrapper *dsw; + dsw = &stream->directSoundWrapper; + return dsw->dsw_FramesPlayed; +*/ + return PaUtil_GetTime(); +} + + +/***********************************************************************************/ +static double GetStreamCpuLoad( PaStream* s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + + + +/*********************************************************************************** + As separate stream interfaces are used for blocking and callback + streams, the following functions can be guaranteed to only be called + for blocking streams. +*/ + +static PaError ReadStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* suppress unused variable warnings */ + (void) buffer; + (void) frames; + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +/***********************************************************************************/ +static PaError WriteStream( PaStream* s, + const void *buffer, + unsigned long frames ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* suppress unused variable warnings */ + (void) buffer; + (void) frames; + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +/***********************************************************************************/ +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + +/***********************************************************************************/ +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* suppress unused variable warnings */ + (void) stream; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + + diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c b/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c index 4b6d1dc24..4e07fee27 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c +++ b/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c @@ -1,86 +1,86 @@ -/* - * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 rossbencina Exp $ - * Portable Audio I/O Library Windows initialization table - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2002 Ross Bencina, Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - Win32 host API initialization function table. - - @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what - the Unix version does, we should consider being consistent. -*/ - - -#include - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); -PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - - -PaUtilHostApiInitializer *paHostApiInitializers[] = - { - -#ifndef PA_NO_WMME - PaWinMme_Initialize, -#endif - -#ifndef PA_NO_DS - PaWinDs_Initialize, -#endif - -#ifndef PA_NO_ASIO - PaAsio_Initialize, -#endif - -/* -#ifndef PA_NO_WDMKS - PaWinWdm_Initialize, -#endif -*/ - - PaSkeleton_Initialize, /* just for testing */ - - 0 /* NULL terminated array */ - }; - - -int paDefaultHostApiIndex = 0; - +/* + * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 rossbencina Exp $ + * Portable Audio I/O Library Windows initialization table + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + Win32 host API initialization function table. + + @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what + the Unix version does, we should consider being consistent. +*/ + + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +PaUtilHostApiInitializer *paHostApiInitializers[] = + { + +#ifndef PA_NO_WMME + PaWinMme_Initialize, +#endif + +#ifndef PA_NO_DS + PaWinDs_Initialize, +#endif + +#ifndef PA_NO_ASIO + PaAsio_Initialize, +#endif + +/* +#ifndef PA_NO_WDMKS + PaWinWdm_Initialize, +#endif +*/ + + PaSkeleton_Initialize, /* just for testing */ + + 0 /* NULL terminated array */ + }; + + +int paDefaultHostApiIndex = 0; + diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_util.c b/pjmedia/src/pjmedia/portaudio/pa_win_util.c index 0d79d5d56..0395e5c84 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_win_util.c +++ b/pjmedia/src/pjmedia/portaudio/pa_win_util.c @@ -1,134 +1,134 @@ -/* - * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 rossbencina Exp $ - * Portable Audio I/O Library - * Win32 platform-specific support functions - * - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 1999-2000 Ross Bencina - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - Win32 platform-specific support functions. - - @todo Implement workaround for QueryPerformanceCounter() skipping forward - bug. (see msdn kb Q274323). -*/ - -#include -#include /* for timeGetTime() */ - -#include "pa_util.h" - - -/* - Track memory allocations to avoid leaks. - */ - -#if PA_TRACK_MEMORY -static int numAllocations_ = 0; -#endif - - -void *PaUtil_AllocateMemory( long size ) -{ - void *result = GlobalAlloc( GPTR, size ); - -#if PA_TRACK_MEMORY - if( result != NULL ) numAllocations_ += 1; -#endif - return result; -} - - -void PaUtil_FreeMemory( void *block ) -{ - if( block != NULL ) - { - GlobalFree( block ); -#if PA_TRACK_MEMORY - numAllocations_ -= 1; -#endif - - } -} - - -int PaUtil_CountCurrentlyAllocatedBlocks( void ) -{ -#if PA_TRACK_MEMORY - return numAllocations_; -#else - return 0; -#endif -} - - -void Pa_Sleep( long msec ) -{ - Sleep( msec ); -} - -static int usePerformanceCounter_; -static double secondsPerTick_; - -void PaUtil_InitializeClock( void ) -{ - LARGE_INTEGER ticksPerSecond; - - if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 ) - { - usePerformanceCounter_ = 1; - secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart; - } - else - { - usePerformanceCounter_ = 0; - } -} - - -double PaUtil_GetTime( void ) -{ - LARGE_INTEGER time; - - if( usePerformanceCounter_ ) - { - /* FIXME: - according to this knowledge-base article, QueryPerformanceCounter - can skip forward by seconds! - http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323& - - it may be better to use the rtdsc instruction using inline asm, - however then a method is needed to calculate a ticks/seconds ratio. - */ - QueryPerformanceCounter( &time ); - return time.QuadPart * secondsPerTick_; - } - else - { - return timeGetTime() * .001; - } -} +/* + * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 rossbencina Exp $ + * Portable Audio I/O Library + * Win32 platform-specific support functions + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2000 Ross Bencina + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + Win32 platform-specific support functions. + + @todo Implement workaround for QueryPerformanceCounter() skipping forward + bug. (see msdn kb Q274323). +*/ + +#include +#include /* for timeGetTime() */ + +#include "pa_util.h" + + +/* + Track memory allocations to avoid leaks. + */ + +#if PA_TRACK_MEMORY +static int numAllocations_ = 0; +#endif + + +void *PaUtil_AllocateMemory( long size ) +{ + void *result = GlobalAlloc( GPTR, size ); + +#if PA_TRACK_MEMORY + if( result != NULL ) numAllocations_ += 1; +#endif + return result; +} + + +void PaUtil_FreeMemory( void *block ) +{ + if( block != NULL ) + { + GlobalFree( block ); +#if PA_TRACK_MEMORY + numAllocations_ -= 1; +#endif + + } +} + + +int PaUtil_CountCurrentlyAllocatedBlocks( void ) +{ +#if PA_TRACK_MEMORY + return numAllocations_; +#else + return 0; +#endif +} + + +void Pa_Sleep( long msec ) +{ + Sleep( msec ); +} + +static int usePerformanceCounter_; +static double secondsPerTick_; + +void PaUtil_InitializeClock( void ) +{ + LARGE_INTEGER ticksPerSecond; + + if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 ) + { + usePerformanceCounter_ = 1; + secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart; + } + else + { + usePerformanceCounter_ = 0; + } +} + + +double PaUtil_GetTime( void ) +{ + LARGE_INTEGER time; + + if( usePerformanceCounter_ ) + { + /* FIXME: + according to this knowledge-base article, QueryPerformanceCounter + can skip forward by seconds! + http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323& + + it may be better to use the rtdsc instruction using inline asm, + however then a method is needed to calculate a ticks/seconds ratio. + */ + QueryPerformanceCounter( &time ); + return time.QuadPart * secondsPerTick_; + } + else + { + return timeGetTime() * .001; + } +} diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c index ab9d90076..c3d7fe6d6 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c +++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c @@ -1,3623 +1,3623 @@ -/* - * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $ - * pa_win_wmme.c - * Implementation of PortAudio for Windows MultiMedia Extensions (WMME) - * - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Authors: Ross Bencina and Phil Burk - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - * - */ - -/* Modification History: - PLB = Phil Burk - JM = Julien Maillard - RDB = Ross Bencina - PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer) - PLB20010413 - check for excessive numbers of channels - PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC - including conditional inclusion of memory.h, - and explicit typecasting on memory allocation - PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory - PLB20010816 - pass process instead of thread to SetPriorityClass() - PLB20010927 - use number of frames instead of real-time for CPULoad calculation. - JM20020118 - prevent hung thread when buffers underflow. - PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount - RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init - RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices - refactoring, renaming and fixed a few edge case bugs - RDB20020531 - converted to V19 framework - ** NOTE maintanance history is now stored in CVS ** -*/ - -/** @file - - @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now, - needs to be reviewed and tested.) - - @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput. - - @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may - be called asynchronously from the callback thread. this is bad. - - @todo implement inputBufferAdcTime in callback thread - - @todo review/fix error recovery and cleanup in marked functions - - @todo implement timeInfo for stream priming - - @todo handle the case where the callback returns paAbort or paComplete during stream priming. - - @todo review input overflow and output underflow handling in ReadStream and WriteStream - -Non-critical stuff for the future: - - @todo Investigate supporting host buffer formats > 16 bits - - @todo define UNICODE and _UNICODE in the project settings and see what breaks - -*/ - -/* - How it works: - - For both callback and blocking read/write streams we open the MME devices - in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever - it has finished with a buffer (either filled it for input, or played it - for output). Where necessary we block waiting for Event objects using - WaitMultipleObjects(). - - When implementing a PA callback stream, we set up a high priority thread - which waits on the MME buffer Events and drains/fills the buffers when - they are ready. - - When implementing a PA blocking read/write stream, we simply wait on these - Events (when necessary) inside the ReadStream() and WriteStream() functions. -*/ - -#include -#include -#include -#include -#include -#include -#include -/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ -#ifndef __MWERKS__ -#include -#include -#endif /* __MWERKS__ */ - -#include "portaudio.h" -#include "pa_trace.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" - -#include "pa_win_wmme.h" - -#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ -#pragma comment(lib, "winmm.lib") -#endif - -/************************************************* Constants ********/ - -#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */ - -#if PA_MME_USE_HIGH_DEFAULT_LATENCY_ - #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4) - #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4) - #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16) - #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */ - #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */ -#else - #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2) - #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3) - #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2) - #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16) - #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */ - #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */ -#endif - -/* Use higher latency for NT because it is even worse at real-time - operation than Win9x. -*/ -#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2) -#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_) - - -#define PA_MME_MIN_TIMEOUT_MSEC_ (1000) - -static const char constInputMapperSuffix_[] = " - Input"; -static const char constOutputMapperSuffix_[] = " - Output"; - -/********************************************************************/ - -typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */ - -/* prototypes for functions declared in this file */ - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - -PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - - -/* macros for setting last host error information */ - -#ifdef UNICODE - -#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ - { \ - wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ - WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ - mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ - { \ - wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ - WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ - mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#else /* !UNICODE */ - -#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ - { \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ - { \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#endif /* UNICODE */ - - -static void PaMme_SetLastSystemError( DWORD errorCode ) -{ - char *lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, - NULL - ); - PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf ); - LocalFree( lpMsgBuf ); -} - -#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \ - PaMme_SetLastSystemError( errorCode ) - - -/* PaError returning wrappers for some commonly used win32 functions - note that we allow passing a null ptr to have no effect. -*/ - -static PaError CreateEventWithPaError( HANDLE *handle, - LPSECURITY_ATTRIBUTES lpEventAttributes, - BOOL bManualReset, - BOOL bInitialState, - LPCTSTR lpName ) -{ - PaError result = paNoError; - - *handle = NULL; - - *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName ); - if( *handle == NULL ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - - return result; -} - - -static PaError ResetEventWithPaError( HANDLE handle ) -{ - PaError result = paNoError; - - if( handle ) - { - if( ResetEvent( handle ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - } - - return result; -} - - -static PaError CloseHandleWithPaError( HANDLE handle ) -{ - PaError result = paNoError; - - if( handle ) - { - if( CloseHandle( handle ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - } - - return result; -} - - -/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - int inputDeviceCount, outputDeviceCount; - - /** winMmeDeviceIds is an array of WinMme device ids. - fields in the range [0, inputDeviceCount) are input device ids, - and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output - device ids. - */ - UINT *winMmeDeviceIds; -} -PaWinMmeHostApiRepresentation; - - -typedef struct -{ - PaDeviceInfo inheritedDeviceInfo; - DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */ -} -PaWinMmeDeviceInfo; - - -/************************************************************************* - * Returns recommended device ID. - * On the PC, the recommended device can be specified by the user by - * setting an environment variable. For example, to use device #1. - * - * set PA_RECOMMENDED_OUTPUT_DEVICE=1 - * - * The user should first determine the available device ID by using - * the supplied application "pa_devs". - */ -#define PA_ENV_BUF_SIZE_ (32) -#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE") -#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE") -static PaDeviceIndex GetEnvDefaultDeviceID( char *envName ) -{ - PaDeviceIndex recommendedIndex = paNoDevice; - DWORD hresult; - char envbuf[PA_ENV_BUF_SIZE_]; - -#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */ - - /* Let user determine default device by setting environment variable. */ - hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ ); - if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) ) - { - recommendedIndex = atoi( envbuf ); - } -#endif - - return recommendedIndex; -} - - -static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi ) -{ - PaDeviceIndex device; - - /* input */ - device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ ); - if( device != paNoDevice && - ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && - hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 ) - { - hostApi->inheritedHostApiRep.info.defaultInputDevice = device; - } - - /* output */ - device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ ); - if( device != paNoDevice && - ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && - hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 ) - { - hostApi->inheritedHostApiRep.info.defaultOutputDevice = device; - } -} - - -/** Convert external PA ID to a windows multimedia device ID -*/ -static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device ) -{ - assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount ); - - return hostApi->winMmeDeviceIds[ device ]; -} - - -static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx ) -{ - MMRESULT mmresult; - - switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) ) - { - case MMSYSERR_NOERROR: - return paNoError; - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - return paDeviceUnavailable; - case MMSYSERR_NODRIVER: /* No device driver is present. */ - return paDeviceUnavailable; - case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ - return paInsufficientMemory; - case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - return paSampleFormatNotSupported; - - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - /* falls through */ - default: - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - return paUnanticipatedHostError; - } -} - - -static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx ) -{ - MMRESULT mmresult; - - switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) ) - { - case MMSYSERR_NOERROR: - return paNoError; - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - return paDeviceUnavailable; - case MMSYSERR_NODRIVER: /* No device driver is present. */ - return paDeviceUnavailable; - case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ - return paInsufficientMemory; - case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - return paSampleFormatNotSupported; - - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - /* falls through */ - default: - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - return paUnanticipatedHostError; - } -} - - -static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo, - PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), - int winMmeDeviceId, int channels, double sampleRate ) -{ - PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo; - WAVEFORMATEX waveFormatEx; - - if( sampleRate == 11025.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){ - - return paNoError; - } - - if( sampleRate == 22050.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){ - - return paNoError; - } - - if( sampleRate == 44100.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){ - - return paNoError; - } - - waveFormatEx.wFormatTag = WAVE_FORMAT_PCM; - waveFormatEx.nChannels = (WORD)channels; - waveFormatEx.nSamplesPerSec = (DWORD)sampleRate; - waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short); - waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short)); - waveFormatEx.wBitsPerSample = 16; - waveFormatEx.cbSize = 0; - - return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx ); -} - - -#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */ -static double defaultSampleRateSearchOrder_[] = - { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0, - 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; - -static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId, - PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels ) -{ - PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; - int i; - - deviceInfo->defaultSampleRate = 0.; - - for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i ) - { - double sampleRate = defaultSampleRateSearchOrder_[ i ]; - PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate ); - if( paerror == paNoError ) - { - deviceInfo->defaultSampleRate = sampleRate; - break; - } - } -} - - -static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success ) -{ - PaError result = paNoError; - char *deviceName; /* non-const ptr */ - MMRESULT mmresult; - WAVEINCAPS wic; - PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; - - *success = 0; - - mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) ); - if( mmresult == MMSYSERR_NOMEM ) - { - result = paInsufficientMemory; - goto error; - } - else if( mmresult != MMSYSERR_NOERROR ) - { - /* instead of returning paUnanticipatedHostError we return - paNoError, but leave success set as 0. This allows - Pa_Initialize to just ignore this device, without failing - the entire initialisation process. - */ - return paNoError; - } - - if( winMmeInputDeviceId == WAVE_MAPPER ) - { - /* Append I/O suffix to WAVE_MAPPER device. */ - deviceName = (char *)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, wic.szPname ); - strcat( deviceName, constInputMapperSuffix_ ); - } - else - { - deviceName = (char*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( wic.szPname ) + 1 ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, wic.szPname ); - } - deviceInfo->name = deviceName; - - deviceInfo->maxInputChannels = wic.wChannels; - /* Sometimes a device can return a rediculously large number of channels. - * This happened with an SBLive card on a Windows ME box. - * If that happens, then force it to 2 channels. PLB20010413 - */ - if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) ) - { - PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels )); - deviceInfo->maxInputChannels = 2; - } - - winMmeDeviceInfo->dwFormats = wic.dwFormats; - - DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId, - QueryInputWaveFormatEx, deviceInfo->maxInputChannels ); - - *success = 1; - -error: - return result; -} - - -static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success ) -{ - PaError result = paNoError; - char *deviceName; /* non-const ptr */ - MMRESULT mmresult; - WAVEOUTCAPS woc; - PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; - - *success = 0; - - mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) ); - if( mmresult == MMSYSERR_NOMEM ) - { - result = paInsufficientMemory; - goto error; - } - else if( mmresult != MMSYSERR_NOERROR ) - { - /* instead of returning paUnanticipatedHostError we return - paNoError, but leave success set as 0. This allows - Pa_Initialize to just ignore this device, without failing - the entire initialisation process. - */ - return paNoError; - } - - if( winMmeOutputDeviceId == WAVE_MAPPER ) - { - /* Append I/O suffix to WAVE_MAPPER device. */ - deviceName = (char *)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, woc.szPname ); - strcat( deviceName, constOutputMapperSuffix_ ); - } - else - { - deviceName = (char*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( woc.szPname ) + 1 ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, woc.szPname ); - } - deviceInfo->name = deviceName; - - deviceInfo->maxOutputChannels = woc.wChannels; - /* Sometimes a device can return a rediculously large number of channels. - * This happened with an SBLive card on a Windows ME box. - * It also happens on Win XP! - */ - if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) ) - { - PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels )); - deviceInfo->maxOutputChannels = 2; - } - - winMmeDeviceInfo->dwFormats = woc.dwFormats; - - DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId, - QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels ); - - *success = 1; - -error: - return result; -} - - -static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency ) -{ - OSVERSIONINFO osvi; - osvi.dwOSVersionInfoSize = sizeof( osvi ); - GetVersionEx( &osvi ); - - /* Check for NT */ - if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) - { - *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_; - } - else if(osvi.dwMajorVersion >= 5) - { - *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_; - } - else - { - *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_; - } - - *defaultHighLatency = *defaultLowLatency * 2; -} - - -PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i; - PaWinMmeHostApiRepresentation *winMmeHostApi; - int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount; - PaWinMmeDeviceInfo *deviceInfoArray; - int deviceInfoInitializationSucceeded; - PaTime defaultLowLatency, defaultHighLatency; - - winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) ); - if( !winMmeHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - winMmeHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !winMmeHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &winMmeHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paMME; - (*hostApi)->info.name = "MME"; - - - /* initialise device counts and default devices under the assumption that - there are no devices. These values are incremented below if and when - devices are successfully initialized. - */ - (*hostApi)->info.deviceCount = 0; - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - winMmeHostApi->inputDeviceCount = 0; - winMmeHostApi->outputDeviceCount = 0; - - - maximumPossibleDeviceCount = 0; - - inputDeviceCount = waveInGetNumDevs(); - if( inputDeviceCount > 0 ) - maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */ - - outputDeviceCount = waveOutGetNumDevs(); - if( outputDeviceCount > 0 ) - maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */ - - - if( maximumPossibleDeviceCount > 0 ){ - - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount ); - if( !winMmeHostApi->winMmeDeviceIds ) - { - result = paInsufficientMemory; - goto error; - } - - GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency ); - - if( inputDeviceCount > 0 ){ - /* -1 is the WAVE_MAPPER */ - for( i = -1; i < inputDeviceCount; ++i ){ - UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); - PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - - deviceInfo->defaultLowInputLatency = defaultLowLatency; - deviceInfo->defaultLowOutputLatency = defaultLowLatency; - deviceInfo->defaultHighInputLatency = defaultHighLatency; - deviceInfo->defaultHighOutputLatency = defaultHighLatency; - - result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, - winMmeDeviceId, &deviceInfoInitializationSucceeded ); - if( result != paNoError ) - goto error; - - if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultInputDevice == paNoDevice ) - (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; - - winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - - winMmeHostApi->inputDeviceCount++; - (*hostApi)->info.deviceCount++; - } - } - } - - if( outputDeviceCount > 0 ){ - /* -1 is the WAVE_MAPPER */ - for( i = -1; i < outputDeviceCount; ++i ){ - UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); - PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - - deviceInfo->defaultLowInputLatency = defaultLowLatency; - deviceInfo->defaultLowOutputLatency = defaultLowLatency; - deviceInfo->defaultHighInputLatency = defaultHighLatency; - deviceInfo->defaultHighOutputLatency = defaultHighLatency; - - result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, - winMmeDeviceId, &deviceInfoInitializationSucceeded ); - if( result != paNoError ) - goto error; - - if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultOutputDevice == paNoDevice ) - (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; - - winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - - winMmeHostApi->outputDeviceCount++; - (*hostApi)->info.deviceCount++; - } - } - } - } - - - InitializeDefaultDeviceIdsFromEnv( winMmeHostApi ); - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( winMmeHostApi ) - { - if( winMmeHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); - } - - PaUtil_FreeMemory( winMmeHostApi ); - } - - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - - if( winMmeHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); - } - - PaUtil_FreeMemory( winMmeHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo; - int inputChannelCount, outputChannelCount; - int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; - UINT winMmeInputDeviceId, winMmeOutputDeviceId; - unsigned int i; - PaError paerror; - - /* The calls to QueryFormatSupported below are intended to detect invalid - sample rates. If we assume that the channel count and format are OK, - then the only thing that could fail is the sample rate. This isn't - strictly true, but I can't think of a better way to test that the - sample rate is valid. - */ - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - inputStreamInfo = inputParameters->hostApiSpecificStreamInfo; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification - && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - { - inputMultipleDeviceChannelCount = 0; - for( i=0; i< inputStreamInfo->deviceCount; ++i ) - { - inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount; - - inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ]; - - /* check that input device can support inputChannelCount */ - if( inputStreamInfo->devices[i].channelCount <= 0 - || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device ); - paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - - if( inputMultipleDeviceChannelCount != inputChannelCount ) - return paIncompatibleHostApiSpecificStreamInfo; - } - else - { - if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */ - - inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ]; - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > inputDeviceInfo->maxInputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device ); - paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - outputStreamInfo = outputParameters->hostApiSpecificStreamInfo; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification - && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - { - outputMultipleDeviceChannelCount = 0; - for( i=0; i< outputStreamInfo->deviceCount; ++i ) - { - outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount; - - outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ]; - - /* check that output device can support outputChannelCount */ - if( outputStreamInfo->devices[i].channelCount <= 0 - || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device ); - paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - - if( outputMultipleDeviceChannelCount != outputChannelCount ) - return paIncompatibleHostApiSpecificStreamInfo; - } - else - { - if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) ) - return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */ - - outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ]; - - /* check that output device can support outputChannelCount */ - if( outputChannelCount > outputDeviceInfo->maxOutputChannels ) - return paInvalidChannelCount; - - /* test for valid sample rate, see comment above */ - winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device ); - paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate ); - if( paerror != paNoError ) - return paInvalidSampleRate; - } - } - - /* - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - - for mme all we can do is test that the input and output devices - support the requested sample rate and number of channels. we - cannot test for full duplex compatibility. - */ - - return paFormatIsSupported; -} - - - -static void SelectBufferSizeAndCount( unsigned long baseBufferSize, - unsigned long requestedLatency, - unsigned long baseBufferCount, unsigned long minimumBufferCount, - unsigned long maximumBufferSize, unsigned long *hostBufferSize, - unsigned long *hostBufferCount ) -{ - unsigned long sizeMultiplier, bufferCount, latency; - unsigned long nextLatency, nextBufferSize; - int baseBufferSizeIsPowerOfTwo; - - sizeMultiplier = 1; - bufferCount = baseBufferCount; - - /* count-1 below because latency is always determined by one less - than the total number of buffers. - */ - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - - if( latency > requestedLatency ) - { - - /* reduce number of buffers without falling below suggested latency */ - - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) - { - --bufferCount; - nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); - } - - }else if( latency < requestedLatency ){ - - baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1))); - if( baseBufferSizeIsPowerOfTwo ){ - - /* double size of buffers without exceeding requestedLatency */ - - nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); - nextLatency = nextBufferSize * (bufferCount-1); - while( nextBufferSize <= maximumBufferSize - && nextLatency < requestedLatency ) - { - sizeMultiplier *= 2; - nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); - nextLatency = nextBufferSize * (bufferCount-1); - } - - }else{ - - /* increase size of buffers upto first excess of requestedLatency */ - - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - while( nextBufferSize <= maximumBufferSize - && nextLatency < requestedLatency ) - { - ++sizeMultiplier; - nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); - nextLatency = nextBufferSize * (bufferCount-1); - } - - if( nextLatency < requestedLatency ) - ++sizeMultiplier; - } - - /* increase number of buffers until requestedLatency is reached */ - - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - while( latency < requestedLatency ) - { - ++bufferCount; - latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); - } - } - - *hostBufferSize = baseBufferSize * sizeMultiplier; - *hostBufferCount = bufferCount; -} - - -static void ReselectBufferCount( unsigned long bufferSize, - unsigned long requestedLatency, - unsigned long baseBufferCount, unsigned long minimumBufferCount, - unsigned long *hostBufferCount ) -{ - unsigned long bufferCount, latency; - unsigned long nextLatency; - - bufferCount = baseBufferCount; - - /* count-1 below because latency is always determined by one less - than the total number of buffers. - */ - latency = bufferSize * (bufferCount-1); - - if( latency > requestedLatency ) - { - /* reduce number of buffers without falling below suggested latency */ - - nextLatency = bufferSize * (bufferCount-2); - while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) - { - --bufferCount; - nextLatency = bufferSize * (bufferCount-2); - } - - }else if( latency < requestedLatency ){ - - /* increase number of buffers until requestedLatency is reached */ - - latency = bufferSize * (bufferCount-1); - while( latency < requestedLatency ) - { - ++bufferCount; - latency = bufferSize * (bufferCount-1); - } - } - - *hostBufferCount = bufferCount; -} - - -/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount, - framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values - of the other parameters. -*/ - -static PaError CalculateBufferSettings( - unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount, - unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount, - int inputChannelCount, PaSampleFormat hostInputSampleFormat, - PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo, - int outputChannelCount, PaSampleFormat hostOutputSampleFormat, - PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo, - double sampleRate, unsigned long framesPerBuffer ) -{ - PaError result = paNoError; - int effectiveInputChannelCount, effectiveOutputChannelCount; - int hostInputFrameSize = 0; - unsigned int i; - - if( inputChannelCount > 0 ) - { - int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat ); - if( hostInputSampleSize < 0 ) - { - result = hostInputSampleSize; - goto error; - } - - if( inputStreamInfo - && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) - { - /* set effectiveInputChannelCount to the largest number of - channels on any one device. - */ - effectiveInputChannelCount = 0; - for( i=0; i< inputStreamInfo->deviceCount; ++i ) - { - if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount ) - effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount; - } - } - else - { - effectiveInputChannelCount = inputChannelCount; - } - - hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount; - - if( inputStreamInfo - && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) - { - if( inputStreamInfo->bufferCount <= 0 - || inputStreamInfo->framesPerBuffer <= 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - - *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer; - *hostInputBufferCount = inputStreamInfo->bufferCount; - } - else - { - unsigned long hostBufferSizeBytes, hostBufferCount; - unsigned long minimumBufferCount = (outputChannelCount > 0) - ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ - : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_; - - unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize); - if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) - maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; - - /* compute the following in bytes, then convert back to frames */ - - SelectBufferSizeAndCount( - ((framesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize; - *hostInputBufferCount = hostBufferCount; - } - } - else - { - *framesPerHostInputBuffer = 0; - *hostInputBufferCount = 0; - } - - if( outputChannelCount > 0 ) - { - if( outputStreamInfo - && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) - { - if( outputStreamInfo->bufferCount <= 0 - || outputStreamInfo->framesPerBuffer <= 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - - *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer; - *hostOutputBufferCount = outputStreamInfo->bufferCount; - - - if( inputChannelCount > 0 ) /* full duplex */ - { - if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer ) - { - if( inputStreamInfo - && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) - { - /* a custom StreamInfo was used for specifying both input - and output buffer sizes, the larger buffer size - must be a multiple of the smaller buffer size */ - - if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer ) - { - if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - } - else - { - assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer ); - if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - } - } - else - { - /* a custom StreamInfo was not used for specifying the input buffer size, - so use the output buffer size, and approximately the same latency. */ - - *framesPerHostInputBuffer = *framesPerHostOutputBuffer; - *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1; - - if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ) - *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; - } - } - } - } - else - { - unsigned long hostBufferSizeBytes, hostBufferCount; - unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; - unsigned long maximumBufferSize; - int hostOutputFrameSize; - int hostOutputSampleSize; - - hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat ); - if( hostOutputSampleSize < 0 ) - { - result = hostOutputSampleSize; - goto error; - } - - if( outputStreamInfo - && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) - { - /* set effectiveOutputChannelCount to the largest number of - channels on any one device. - */ - effectiveOutputChannelCount = 0; - for( i=0; i< outputStreamInfo->deviceCount; ++i ) - { - if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount ) - effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount; - } - } - else - { - effectiveOutputChannelCount = outputChannelCount; - } - - hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount; - - maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize); - if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) - maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; - - - /* compute the following in bytes, then convert back to frames */ - - SelectBufferSizeAndCount( - ((framesPerBuffer == paFramesPerBufferUnspecified) - ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ - : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - maximumBufferSize, - &hostBufferSizeBytes, &hostBufferCount ); - - *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize; - *hostOutputBufferCount = hostBufferCount; - - - if( inputChannelCount > 0 ) - { - /* ensure that both input and output buffer sizes are the same. - if they don't match at this stage, choose the smallest one - and use that for input and output - */ - - if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer ) - { - if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) - { - unsigned long framesPerHostBuffer = *framesPerHostInputBuffer; - - minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; - ReselectBufferCount( - framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */ - ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - &hostBufferCount ); - - *framesPerHostOutputBuffer = framesPerHostBuffer; - *hostOutputBufferCount = hostBufferCount; - } - else - { - unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer; - - minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; - ReselectBufferCount( - framesPerHostBuffer * hostInputFrameSize, /* bufferSize */ - ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ - 4, /* baseBufferCount */ - minimumBufferCount, - &hostBufferCount ); - - *framesPerHostInputBuffer = framesPerHostBuffer; - *hostInputBufferCount = hostBufferCount; - } - } - } - } - } - else - { - *framesPerHostOutputBuffer = 0; - *hostOutputBufferCount = 0; - } - -error: - return result; -} - - -typedef struct -{ - HANDLE bufferEvent; - void *waveHandles; - unsigned int deviceCount; - /* unsigned int channelCount; */ - WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */ - unsigned int bufferCount; - unsigned int currentBufferIndex; - unsigned int framesPerBuffer; - unsigned int framesUsedInCurrentBuffer; -}PaWinMmeSingleDirectionHandlesAndBuffers; - -/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */ - -static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ); -static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long bytesPerHostSample, - double sampleRate, PaWinMmeDeviceAndChannelCount *devices, - unsigned int deviceCount, int isInput ); -static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError ); -static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long hostBufferCount, - PaSampleFormat hostSampleFormat, - unsigned long framesPerHostBuffer, - PaWinMmeDeviceAndChannelCount *devices, - int isInput ); -static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput ); - - -static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) -{ - handlesAndBuffers->bufferEvent = 0; - handlesAndBuffers->waveHandles = 0; - handlesAndBuffers->deviceCount = 0; - handlesAndBuffers->waveHeaders = 0; - handlesAndBuffers->bufferCount = 0; -} - -static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long bytesPerHostSample, - double sampleRate, PaWinMmeDeviceAndChannelCount *devices, - unsigned int deviceCount, int isInput ) -{ - PaError result; - MMRESULT mmresult; - unsigned long bytesPerFrame; - WAVEFORMATEX wfx; - signed int i; - - /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers() - has already been called to zero some fields */ - - result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL ); - if( result != paNoError ) goto error; - - if( isInput ) - handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount ); - else - handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount ); - if( !handlesAndBuffers->waveHandles ) - { - result = paInsufficientMemory; - goto error; - } - - handlesAndBuffers->deviceCount = deviceCount; - - for( i = 0; i < (signed int)deviceCount; ++i ) - { - if( isInput ) - ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0; - else - ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0; - } - - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nSamplesPerSec = (DWORD) sampleRate; - wfx.cbSize = 0; - - for( i = 0; i < (signed int)deviceCount; ++i ) - { - UINT winMmeDeviceId; - - winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device ); - wfx.nChannels = (WORD)devices[i].channelCount; - - bytesPerFrame = wfx.nChannels * bytesPerHostSample; - - wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate); - wfx.nBlockAlign = (WORD)bytesPerFrame; - wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8); - - /* REVIEW: consider not firing an event for input when a full duplex - stream is being used. this would probably depend on the - neverDropInput flag. */ - - if( isInput ) - mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, - (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT ); - else - mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, - (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT ); - - if( mmresult != MMSYSERR_NOERROR ) - { - switch( mmresult ) - { - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - result = paDeviceUnavailable; - break; - case MMSYSERR_NODRIVER: /* No device driver is present. */ - result = paDeviceUnavailable; - break; - case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ - result = paInsufficientMemory; - break; - - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - /* falls through */ - case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - /* falls through */ - default: - result = paUnanticipatedHostError; - if( isInput ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - else - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - } - goto error; - } - } - - return result; - -error: - TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ ); - - return result; -} - - -static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError ) -{ - PaError result = paNoError; - MMRESULT mmresult; - signed int i; - - if( handlesAndBuffers->waveHandles ) - { - for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i ) - { - if( isInput ) - { - if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] ) - mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] ); - } - else - { - if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ) - mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ); - } - - if( mmresult != MMSYSERR_NOERROR && - !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */ - { - result = paUnanticipatedHostError; - if( isInput ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - else - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - /* note that we don't break here, we try to continue closing devices */ - } - } - - PaUtil_FreeMemory( handlesAndBuffers->waveHandles ); - handlesAndBuffers->waveHandles = 0; - } - - if( handlesAndBuffers->bufferEvent ) - { - result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent ); - handlesAndBuffers->bufferEvent = 0; - } - - return result; -} - - -static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, - unsigned long hostBufferCount, - PaSampleFormat hostSampleFormat, - unsigned long framesPerHostBuffer, - PaWinMmeDeviceAndChannelCount *devices, - int isInput ) -{ - PaError result = paNoError; - MMRESULT mmresult; - WAVEHDR *deviceWaveHeaders; - signed int i, j; - - /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers() - has already been called to zero some fields */ - - - /* allocate an array of pointers to arrays of wave headers, one array of - wave headers per device */ - handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount ); - if( !handlesAndBuffers->waveHeaders ) - { - result = paInsufficientMemory; - goto error; - } - - for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i ) - handlesAndBuffers->waveHeaders[i] = 0; - - handlesAndBuffers->bufferCount = hostBufferCount; - - for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i ) - { - int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) * - framesPerHostBuffer * devices[i].channelCount; - if( bufferBytes < 0 ) - { - result = paInternalError; - goto error; - } - - /* Allocate an array of wave headers for device i */ - deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount ); - if( !deviceWaveHeaders ) - { - result = paInsufficientMemory; - goto error; - } - - for( j=0; j < (signed int)hostBufferCount; ++j ) - deviceWaveHeaders[j].lpData = 0; - - handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders; - - /* Allocate a buffer for each wave header */ - for( j=0; j < (signed int)hostBufferCount; ++j ) - { - deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes ); - if( !deviceWaveHeaders[j].lpData ) - { - result = paInsufficientMemory; - goto error; - } - deviceWaveHeaders[j].dwBufferLength = bufferBytes; - deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */ - - if( isInput ) - { - mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - else /* output */ - { - mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - deviceWaveHeaders[j].dwUser = devices[i].channelCount; - } - } - - return result; - -error: - TerminateWaveHeaders( handlesAndBuffers, isInput ); - - return result; -} - - -static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput ) -{ - signed int i, j; - WAVEHDR *deviceWaveHeaders; - - if( handlesAndBuffers->waveHeaders ) - { - for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i ) - { - deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */ - if( deviceWaveHeaders ) - { - for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j ) - { - if( deviceWaveHeaders[j].lpData ) - { - if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF ) - { - if( isInput ) - waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - else - waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); - } - - PaUtil_FreeMemory( deviceWaveHeaders[j].lpData ); - } - } - - PaUtil_FreeMemory( deviceWaveHeaders ); - } - } - - PaUtil_FreeMemory( handlesAndBuffers->waveHeaders ); - handlesAndBuffers->waveHeaders = 0; - } -} - - - -/* PaWinMmeStream - a stream data structure specifically for this implementation */ -/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */ -struct PaWinMmeStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - int primeStreamUsingCallback; - - PaWinMmeSingleDirectionHandlesAndBuffers input; - PaWinMmeSingleDirectionHandlesAndBuffers output; - - /* Processing thread management -------------- */ - HANDLE abortEvent; - HANDLE processingThread; - DWORD processingThreadId; - - char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */ - int processingThreadPriority; - int highThreadPriority; - int throttledThreadPriority; - unsigned long throttledSleepMsecs; - - int isStopped; - volatile int isActive; - volatile int stopProcessing; /* stop thread once existing buffers have been returned */ - volatile int abortProcessing; /* stop thread immediately */ - - DWORD allBuffersDurationMs; /* used to calculate timeouts */ -}; - -/* updates deviceCount if PaWinMmeUseMultipleDevices is used */ - -static PaError ValidateWinMmeSpecificStreamInfo( - const PaStreamParameters *streamParameters, - const PaWinMmeStreamInfo *streamInfo, - char *throttleProcessingThreadOnOverload, - unsigned long *deviceCount ) -{ - if( streamInfo ) - { - if( streamInfo->size != sizeof( PaWinMmeStreamInfo ) - || streamInfo->version != 1 ) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - - if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread ) - *throttleProcessingThreadOnOverload = 0; - - if( streamInfo->flags & paWinMmeUseMultipleDevices ) - { - if( streamParameters->device != paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - *deviceCount = streamInfo->deviceCount; - } - } - - return paNoError; -} - -static PaError RetrieveDevicesFromStreamParameters( - struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *streamParameters, - const PaWinMmeStreamInfo *streamInfo, - PaWinMmeDeviceAndChannelCount *devices, - unsigned long deviceCount ) -{ - PaError result = paNoError; - unsigned int i; - int totalChannelCount; - PaDeviceIndex hostApiDevice; - - if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices ) - { - totalChannelCount = 0; - for( i=0; i < deviceCount; ++i ) - { - /* validate that the device number is within range */ - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, - streamInfo->devices[i].device, hostApi ); - if( result != paNoError ) - return result; - - devices[i].device = hostApiDevice; - devices[i].channelCount = streamInfo->devices[i].channelCount; - - totalChannelCount += devices[i].channelCount; - } - - if( totalChannelCount != streamParameters->channelCount ) - { - /* channelCount must match total channels specified by multiple devices */ - return paInvalidChannelCount; /* REVIEW use of this error code */ - } - } - else - { - devices[0].device = streamParameters->device; - devices[0].channelCount = streamParameters->channelCount; - } - - return result; -} - -static PaError ValidateInputChannelCounts( - struct PaUtilHostApiRepresentation *hostApi, - PaWinMmeDeviceAndChannelCount *devices, - unsigned long deviceCount ) -{ - unsigned int i; - - for( i=0; i < deviceCount; ++i ) - { - if( devices[i].channelCount < 1 || devices[i].channelCount - > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels ) - return paInvalidChannelCount; - } - - return paNoError; -} - -static PaError ValidateOutputChannelCounts( - struct PaUtilHostApiRepresentation *hostApi, - PaWinMmeDeviceAndChannelCount *devices, - unsigned long deviceCount ) -{ - unsigned int i; - - for( i=0; i < deviceCount; ++i ) - { - if( devices[i].channelCount < 1 || devices[i].channelCount - > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels ) - return paInvalidChannelCount; - } - - return paNoError; -} - - -/* the following macros are intended to improve the readability of the following code */ -#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles ) -#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles ) -#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles ) -#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) ) - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result; - PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - PaWinMmeStream *stream = 0; - int bufferProcessorIsInitialized = 0; - int streamRepresentationIsInitialized = 0; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - double suggestedInputLatency, suggestedOutputLatency; - PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; - unsigned long framesPerHostInputBuffer; - unsigned long hostInputBufferCount; - unsigned long framesPerHostOutputBuffer; - unsigned long hostOutputBufferCount; - unsigned long framesPerBufferProcessorCall; - PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ - unsigned long inputDeviceCount = 0; - PaWinMmeDeviceAndChannelCount *outputDevices = 0; - unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ - char throttleProcessingThreadOnOverload = 1; - - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - suggestedInputLatency = inputParameters->suggestedLatency; - - inputDeviceCount = 1; - - /* validate input hostApiSpecificStreamInfo */ - inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo; - result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo, - &throttleProcessingThreadOnOverload, - &inputDeviceCount ); - if( result != paNoError ) return result; - - inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount ); - if( !inputDevices ) return paInsufficientMemory; - - result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount ); - if( result != paNoError ) return result; - - result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount ); - if( result != paNoError ) return result; - - hostInputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); - } - else - { - inputChannelCount = 0; - inputSampleFormat = 0; - suggestedInputLatency = 0.; - inputStreamInfo = 0; - hostInputSampleFormat = 0; - } - - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - suggestedOutputLatency = outputParameters->suggestedLatency; - - outputDeviceCount = 1; - - /* validate output hostApiSpecificStreamInfo */ - outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo; - result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo, - &throttleProcessingThreadOnOverload, - &outputDeviceCount ); - if( result != paNoError ) return result; - - outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount ); - if( !outputDevices ) return paInsufficientMemory; - - result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount ); - if( result != paNoError ) return result; - - result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount ); - if( result != paNoError ) return result; - - hostOutputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); - } - else - { - outputChannelCount = 0; - outputSampleFormat = 0; - outputStreamInfo = 0; - hostOutputSampleFormat = 0; - suggestedOutputLatency = 0.; - } - - - /* - IMPLEMENT ME: - - alter sampleRate to a close allowable rate if possible / necessary - */ - - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - - result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount, - &framesPerHostOutputBuffer, &hostOutputBufferCount, - inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo, - outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo, - sampleRate, framesPerBuffer ); - if( result != paNoError ) goto error; - - - stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - - InitializeSingleDirectionHandlesAndBuffers( &stream->input ); - InitializeSingleDirectionHandlesAndBuffers( &stream->output ); - - stream->abortEvent = 0; - stream->processingThread = 0; - - stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload; - - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - ( (streamCallback) - ? &winMmeHostApi->callbackStreamInterface - : &winMmeHostApi->blockingStreamInterface ), - streamCallback, userData ); - streamRepresentationIsInitialized = 1; - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - if( inputParameters && outputParameters ) /* full duplex */ - { - if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) - { - assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */ - - framesPerBufferProcessorCall = framesPerHostInputBuffer; - } - else - { - assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */ - - framesPerBufferProcessorCall = framesPerHostOutputBuffer; - } - } - else if( inputParameters ) - { - framesPerBufferProcessorCall = framesPerHostInputBuffer; - } - else if( outputParameters ) - { - framesPerBufferProcessorCall = framesPerHostOutputBuffer; - } - - stream->input.framesPerBuffer = framesPerHostInputBuffer; - stream->output.framesPerBuffer = framesPerHostOutputBuffer; - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerBufferProcessorCall, paUtilFixedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) goto error; - - bufferProcessorIsInitialized = 1; - - stream->streamRepresentation.streamInfo.inputLatency = - (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) - +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate; - stream->streamRepresentation.streamInfo.outputLatency = - (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) - +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0; - - /* time to sleep when throttling due to >100% cpu usage. - -a quater of a buffer's duration */ - stream->throttledSleepMsecs = - (unsigned long)(stream->bufferProcessor.framesPerHostBuffer * - stream->bufferProcessor.samplePeriod * .25 * 1000); - - stream->isStopped = 1; - stream->isActive = 0; - - - /* for maximum compatibility with multi-device multichannel drivers, - we first open all devices, then we prepare all buffers, finally - we start all devices ( in StartStream() ). teardown in reverse order. - */ - - if( inputParameters ) - { - result = InitializeWaveHandles( winMmeHostApi, &stream->input, - stream->bufferProcessor.bytesPerHostInputSample, sampleRate, - inputDevices, inputDeviceCount, 1 /* isInput */ ); - if( result != paNoError ) goto error; - } - - if( outputParameters ) - { - result = InitializeWaveHandles( winMmeHostApi, &stream->output, - stream->bufferProcessor.bytesPerHostOutputSample, sampleRate, - outputDevices, outputDeviceCount, 0 /* isInput */ ); - if( result != paNoError ) goto error; - } - - if( inputParameters ) - { - result = InitializeWaveHeaders( &stream->input, hostInputBufferCount, - hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ ); - if( result != paNoError ) goto error; - } - - if( outputParameters ) - { - result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount, - hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ ); - if( result != paNoError ) goto error; - - stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate); - } - else - { - stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate); - } - - - if( streamCallback ) - { - /* abort event is only needed for callback streams */ - result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL ); - if( result != paNoError ) goto error; - } - - *s = (PaStream*)stream; - - return result; - -error: - - if( stream ) - { - if( stream->abortEvent ) - CloseHandle( stream->abortEvent ); - - TerminateWaveHeaders( &stream->output, 0 /* not isInput */ ); - TerminateWaveHeaders( &stream->input, 1 /* isInput */ ); - - TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ ); - TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ ); - - if( bufferProcessorIsInitialized ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - - if( streamRepresentationIsInitialized ) - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - - PaUtil_FreeMemory( stream ); - } - - return result; -} - - -/* return non-zero if all current buffers are done */ -static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex ) -{ - unsigned int i; - - for( i=0; i < deviceCount; ++i ) - { - if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) ) - { - return 0; - } - } - - return 1; -} - -static int CurrentInputBuffersAreDone( PaWinMmeStream *stream ) -{ - return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex ); -} - -static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream ) -{ - return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex ); -} - - -/* return non-zero if any buffers are queued */ -static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) -{ - unsigned int i, j; - - if( handlesAndBuffers->waveHandles ) - { - for( i=0; i < handlesAndBuffers->bufferCount; ++i ) - { - for( j=0; j < handlesAndBuffers->deviceCount; ++j ) - { - if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) ) - { - return 0; - } - } - } - } - - return 1; -} - - -#define PA_CIRCULAR_INCREMENT_( current, max )\ - ( (((current) + 1) >= (max)) ? (0) : (current+1) ) - -#define PA_CIRCULAR_DECREMENT_( current, max )\ - ( ((current) == 0) ? ((max)-1) : (current-1) ) - - -static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) -{ - signed long result = 0; - unsigned int i; - - if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) ) - { - /* we could calculate the following in O(1) if we kept track of the - last done buffer */ - result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer; - - i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount ); - while( i != handlesAndBuffers->currentBufferIndex ) - { - if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) ) - { - result += handlesAndBuffers->framesPerBuffer; - i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount ); - } - else - break; - } - } - - return result; -} - - -static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - MMRESULT mmresult; - unsigned int i; - - for( i=0; i < stream->input.deviceCount; ++i ) - { - mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i], - &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ], - sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - } - - stream->input.currentBufferIndex = - PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount ); - - stream->input.framesUsedInCurrentBuffer = 0; - - return result; -} - - -static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - MMRESULT mmresult; - unsigned int i; - - for( i=0; i < stream->output.deviceCount; ++i ) - { - mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i], - &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ], - sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - } - - stream->output.currentBufferIndex = - PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount ); - - stream->output.framesUsedInCurrentBuffer = 0; - - return result; -} - - -/* requeue all but the most recent input with the driver. Used for catching - up after a total input buffer underrun */ -static PaError CatchUpInputBuffers( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - unsigned int i; - - for( i=0; i < stream->input.bufferCount - 1; ++i ) - { - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - break; - } - - return result; -} - - -/* take the most recent output and duplicate it to all other output buffers - and requeue them. Used for catching up after a total output buffer underrun. -*/ -static PaError CatchUpOutputBuffers( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - unsigned int i, j; - unsigned int previousBufferIndex = - PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount ); - - for( i=0; i < stream->output.bufferCount - 1; ++i ) - { - for( j=0; j < stream->output.deviceCount; ++j ) - { - if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData - != stream->output.waveHeaders[j][ previousBufferIndex ].lpData ) - { - CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData, - stream->output.waveHeaders[j][ previousBufferIndex ].lpData, - stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength ); - } - } - - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - break; - } - - return result; -} - - -static DWORD WINAPI ProcessingThreadProc( void *pArg ) -{ - PaWinMmeStream *stream = (PaWinMmeStream *)pArg; - HANDLE events[3]; - int eventCount = 0; - DWORD result = paNoError; - DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); - int hostBuffersAvailable; - signed int hostInputBufferIndex, hostOutputBufferIndex; - PaStreamCallbackFlags statusFlags; - int callbackResult; - int done = 0; - unsigned int channel, i; - unsigned long framesProcessed; - - /* prepare event array for call to WaitForMultipleObjects() */ - if( stream->input.bufferEvent ) - events[eventCount++] = stream->input.bufferEvent; - if( stream->output.bufferEvent ) - events[eventCount++] = stream->output.bufferEvent; - events[eventCount++] = stream->abortEvent; - - statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */ - - /* loop until something causes us to stop */ - do{ - /* wait for MME to signal that a buffer is available, or for - the PA abort event to be signaled. - - When this indicates that one or more buffers are available - NoBuffersAreQueued() and Current*BuffersAreDone are used below to - poll for additional done buffers. NoBuffersAreQueued() will fail - to identify an underrun/overflow if the driver doesn't mark all done - buffers prior to signalling the event. Some drivers do this - (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a - huge problem, it just means that we won't always be able to detect - underflow/overflow. - */ - waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout ); - if( waitResult == WAIT_FAILED ) - { - result = paUnanticipatedHostError; - /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */ - done = 1; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* if a timeout is encountered, continue */ - } - - if( stream->abortProcessing ) - { - /* Pa_AbortStream() has been called, stop processing immediately */ - done = 1; - } - else if( stream->stopProcessing ) - { - /* Pa_StopStream() has been called or the user callback returned - non-zero, processing will continue until all output buffers - are marked as done. The stream will stop immediately if it - is input-only. - */ - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - if( NoBuffersAreQueued( &stream->output ) ) - done = 1; /* Will cause thread to return. */ - } - else - { - /* input only stream */ - done = 1; /* Will cause thread to return. */ - } - } - else - { - hostBuffersAvailable = 1; - - /* process all available host buffers */ - do - { - hostInputBufferIndex = -1; - hostOutputBufferIndex = -1; - - if( PA_IS_INPUT_STREAM_(stream) ) - { - if( CurrentInputBuffersAreDone( stream ) ) - { - if( NoBuffersAreQueued( &stream->input ) ) - { - /** @todo - if all of the other buffers are also ready then - we discard all but the most recent. This is an - input buffer overflow. FIXME: these buffers should - be passed to the callback in a paNeverDropInput - stream. - - note that it is also possible for an input overflow - to happen while the callback is processing a buffer. - that is handled further down. - */ - result = CatchUpInputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paInputOverflow; - } - - hostInputBufferIndex = stream->input.currentBufferIndex; - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - if( CurrentOutputBuffersAreDone( stream ) ) - { - /* ok, we have an output buffer */ - - if( NoBuffersAreQueued( &stream->output ) ) - { - /* - if all of the other buffers are also ready, catch up by copying - the most recently generated buffer into all but one of the output - buffers. - - note that this catch up code only handles the case where all - buffers have been played out due to this thread not having - woken up at all. a more common case occurs when this thread - is woken up, processes one buffer, but takes too long, and as - a result all the other buffers have become un-queued. that - case is handled further down. - */ - - result = CatchUpOutputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paOutputUnderflow; - } - - hostOutputBufferIndex = stream->output.currentBufferIndex; - } - } - - - if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) || - (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) ) - { - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */ - - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime - from the current wave out position */ - MMTIME mmtime; - double timeBeforeGetPosition, timeAfterGetPosition; - double time; - long framesInBufferRing; - long writePosition; - long playbackPosition; - HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0]; - - mmtime.wType = TIME_SAMPLES; - timeBeforeGetPosition = PaUtil_GetTime(); - waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) ); - timeAfterGetPosition = PaUtil_GetTime(); - - timeInfo.currentTime = timeAfterGetPosition; - - /* approximate time at which wave out position was measured - as half way between timeBeforeGetPosition and timeAfterGetPosition */ - time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5; - - framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer; - playbackPosition = mmtime.u.sample % framesInBufferRing; - - writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer - + stream->output.framesUsedInCurrentBuffer; - - if( playbackPosition >= writePosition ){ - timeInfo.outputBufferDacTime = - time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod ); - }else{ - timeInfo.outputBufferDacTime = - time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod ); - } - } - - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags ); - - /* reset status flags once they have been passed to the buffer processor */ - statusFlags = 0; - - if( PA_IS_INPUT_STREAM_(stream) ) - { - PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( i=0; iinput.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; - - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, - stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + - stream->input.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostInputSample, - channelCount ); - - - channel += channelCount; - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( i=0; ioutput.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - channel += channelCount; - } - } - - callbackResult = paContinue; - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - - stream->input.framesUsedInCurrentBuffer += framesProcessed; - stream->output.framesUsedInCurrentBuffer += framesProcessed; - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if( callbackResult == paContinue ) - { - /* nothing special to do */ - } - else if( callbackResult == paAbort ) - { - stream->abortProcessing = 1; - done = 1; - /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */ - result = paNoError; - } - else - { - /* User callback has asked us to stop with paComplete or other non-zero value */ - stream->stopProcessing = 1; /* stop once currently queued audio has finished */ - result = paNoError; - } - - - if( PA_IS_INPUT_STREAM_(stream) - && stream->stopProcessing == 0 && stream->abortProcessing == 0 - && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer ) - { - if( NoBuffersAreQueued( &stream->input ) ) - { - /** @todo need to handle PaNeverDropInput here where necessary */ - result = CatchUpInputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paInputOverflow; - } - - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - done = 1; - } - - - if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing ) - { - if( stream->stopProcessing && - stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer ) - { - /* zero remaining samples in output output buffer and flush */ - - stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - /* we send the entire buffer to the output devices, but we could - just send a partial buffer, rather than zeroing the unused - samples. - */ - } - - if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer ) - { - /* check for underflow before enquing the just-generated buffer, - but recover from underflow after enquing it. This ensures - that the most recent audio segment is repeated */ - int outputUnderflow = NoBuffersAreQueued( &stream->output ); - - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - done = 1; - - if( outputUnderflow && !done && !stream->stopProcessing ) - { - /* Recover from underflow in the case where the - underflow occured while processing the buffer - we just finished */ - - result = CatchUpOutputBuffers( stream ); - if( result != paNoError ) - done = 1; - - statusFlags |= paOutputUnderflow; - } - } - } - - if( stream->throttleProcessingThreadOnOverload != 0 ) - { - if( stream->stopProcessing || stream->abortProcessing ) - { - if( stream->processingThreadPriority != stream->highThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->highThreadPriority ); - stream->processingThreadPriority = stream->highThreadPriority; - } - } - else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. ) - { - if( stream->processingThreadPriority != stream->throttledThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->throttledThreadPriority ); - stream->processingThreadPriority = stream->throttledThreadPriority; - } - - /* sleep to give other processes a go */ - Sleep( stream->throttledSleepMsecs ); - } - else - { - if( stream->processingThreadPriority != stream->highThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->highThreadPriority ); - stream->processingThreadPriority = stream->highThreadPriority; - } - } - } - } - else - { - hostBuffersAvailable = 0; - } - } - while( hostBuffersAvailable && - stream->stopProcessing == 0 && - stream->abortProcessing == 0 && - !done ); - } - } - while( !done ); - - stream->isActive = 0; - - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - - PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); - - return result; -} - - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - PaError result; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - result = CloseHandleWithPaError( stream->abortEvent ); - if( result != paNoError ) goto error; - - TerminateWaveHeaders( &stream->output, 0 /* not isInput */ ); - TerminateWaveHeaders( &stream->input, 1 /* isInput */ ); - - TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ ); - TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - -error: - /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */ - return result; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError result; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - MMRESULT mmresult; - unsigned int i, j; - int callbackResult; - unsigned int channel; - unsigned long framesProcessed; - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */ - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; iinput.bufferCount; ++i ) - { - for( j=0; jinput.deviceCount; ++j ) - { - mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - } - stream->input.currentBufferIndex = 0; - stream->input.framesUsedInCurrentBuffer = 0; - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i=0; ioutput.deviceCount; ++i ) - { - if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - - for( i=0; ioutput.bufferCount; ++i ) - { - if( stream->primeStreamUsingCallback ) - { - - stream->output.framesUsedInCurrentBuffer = 0; - do{ - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, - &timeInfo, - paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0)); - - if( stream->input.bufferCount > 0 ) - PaUtil_SetNoInput( &stream->bufferProcessor ); - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( j=0; joutput.deviceCount; ++j ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[j][i].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[j][i].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - /* we have stored the number of channels in the buffer in dwUser */ - channel += channelCount; - } - - callbackResult = paContinue; - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - stream->output.framesUsedInCurrentBuffer += framesProcessed; - - if( callbackResult != paContinue ) - { - /** @todo fix this, what do we do if callback result is non-zero during stream - priming? - - for complete: play out primed waveHeaders as usual - for abort: clean up immediately. - */ - } - - }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer ); - - } - else - { - for( j=0; joutput.deviceCount; ++j ) - { - ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength ); - } - } - - /* we queue all channels of a single buffer frame (accross all - devices, because some multidevice multichannel drivers work - better this way */ - for( j=0; joutput.deviceCount; ++j ) - { - mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - } - stream->output.currentBufferIndex = 0; - stream->output.framesUsedInCurrentBuffer = 0; - } - - - stream->isStopped = 0; - stream->isActive = 1; - stream->stopProcessing = 0; - stream->abortProcessing = 0; - - result = ResetEventWithPaError( stream->input.bufferEvent ); - if( result != paNoError ) goto error; - - result = ResetEventWithPaError( stream->output.bufferEvent ); - if( result != paNoError ) goto error; - - - if( stream->streamRepresentation.streamCallback ) - { - /* callback stream */ - - result = ResetEventWithPaError( stream->abortEvent ); - if( result != paNoError ) goto error; - - /* Create thread that waits for audio buffers to be ready for processing. */ - stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ); - if( !stream->processingThread ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - - /** @todo could have mme specific stream parameters to allow the user - to set the callback thread priorities */ - stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL; - stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL; - - if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - stream->processingThreadPriority = stream->highThreadPriority; - } - else - { - /* blocking read/write stream */ - - } - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i < stream->input.deviceCount; ++i ) - { - mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] ); - PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult)); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i=0; i < stream->output.deviceCount; ++i ) - { - if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - } - - return result; - -error: - /** @todo FIXME: implement recovery as best we can - This should involve rolling back to a state as-if this function had never been called - */ - return result; -} - - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - int timeout; - DWORD waitResult; - MMRESULT mmresult; - signed int hostOutputBufferIndex; - unsigned int channel, waitCount, i; - - /** @todo - REVIEW: the error checking in this function needs review. the basic - idea is to return from this function in a known state - for example - there is no point avoiding calling waveInReset just because - the thread times out. - */ - - if( stream->processingThread ) - { - /* callback stream */ - - /* Tell processing thread to stop generating more data and to let current data play out. */ - stream->stopProcessing = 1; - - /* Calculate timeOut longer than longest time it could take to return all buffers. */ - timeout = (int)(stream->allBuffersDurationMs * 1.5); - if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MME_MIN_TIMEOUT_MSEC_; - - PA_DEBUG(("WinMME StopStream: waiting for background thread.\n")); - - waitResult = WaitForSingleObject( stream->processingThread, timeout ); - if( waitResult == WAIT_TIMEOUT ) - { - /* try to abort */ - stream->abortProcessing = 1; - SetEvent( stream->abortEvent ); - waitResult = WaitForSingleObject( stream->processingThread, timeout ); - if( waitResult == WAIT_TIMEOUT ) - { - PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n")); - result = paTimedOut; - } - } - - CloseHandle( stream->processingThread ); - stream->processingThread = NULL; - } - else - { - /* blocking read / write stream */ - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - if( stream->output.framesUsedInCurrentBuffer > 0 ) - { - /* there are still unqueued frames in the current buffer, so flush them */ - - hostOutputBufferIndex = stream->output.currentBufferIndex; - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - channel = 0; - for( i=0; ioutput.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - channel += channelCount; - } - - PaUtil_ZeroOutput( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - /* we send the entire buffer to the output devices, but we could - just send a partial buffer, rather than zeroing the unused - samples. - */ - AdvanceToNextOutputBuffer( stream ); - } - - - timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1; - if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MME_MIN_TIMEOUT_MSEC_; - - waitCount = 0; - while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount ) - { - /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout ); - if( waitResult == WAIT_FAILED ) - { - break; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* keep waiting */ - } - - ++waitCount; - } - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i =0; i < stream->output.deviceCount; ++i ) - { - mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - } - } - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i < stream->input.deviceCount; ++i ) - { - mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - } - } - - stream->isStopped = 1; - stream->isActive = 0; - - return result; -} - - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - int timeout; - DWORD waitResult; - MMRESULT mmresult; - unsigned int i; - - /** @todo - REVIEW: the error checking in this function needs review. the basic - idea is to return from this function in a known state - for example - there is no point avoiding calling waveInReset just because - the thread times out. - */ - - if( stream->processingThread ) - { - /* callback stream */ - - /* Tell processing thread to abort immediately */ - stream->abortProcessing = 1; - SetEvent( stream->abortEvent ); - } - - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i =0; i < stream->output.deviceCount; ++i ) - { - mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - return paUnanticipatedHostError; - } - } - } - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i < stream->input.deviceCount; ++i ) - { - mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - return paUnanticipatedHostError; - } - } - } - - - if( stream->processingThread ) - { - /* callback stream */ - - PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n")); - - /* Calculate timeOut longer than longest time it could take to return all buffers. */ - timeout = (int)(stream->allBuffersDurationMs * 1.5); - if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MME_MIN_TIMEOUT_MSEC_; - - waitResult = WaitForSingleObject( stream->processingThread, timeout ); - if( waitResult == WAIT_TIMEOUT ) - { - PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n")); - return paTimedOut; - } - - CloseHandle( stream->processingThread ); - stream->processingThread = NULL; - } - - stream->isStopped = 1; - stream->isActive = 0; - - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return stream->isStopped; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return stream->isActive; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - (void) s; /* unused parameter */ - - return PaUtil_GetTime(); -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - void *userBuffer; - unsigned long framesRead = 0; - unsigned long framesProcessed; - signed int hostInputBufferIndex; - DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); - unsigned int channel, i; - - if( PA_IS_INPUT_STREAM_(stream) ) - { - /* make a local copy of the user buffer pointer(s). this is necessary - because PaUtil_CopyInput() advances these pointers every time - it is called. - */ - if( stream->bufferProcessor.userInputIsInterleaved ) - { - userBuffer = buffer; - } - else - { - userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount ); - if( !userBuffer ) - return paInsufficientMemory; - for( i = 0; ibufferProcessor.inputChannelCount; ++i ) - ((void**)userBuffer)[i] = ((void**)buffer)[i]; - } - - do{ - if( CurrentInputBuffersAreDone( stream ) ) - { - if( NoBuffersAreQueued( &stream->input ) ) - { - /** @todo REVIEW: consider what to do if the input overflows. - do we requeue all of the buffers? should we be running - a thread to make sure they are always queued? */ - - result = paInputOverflowed; - } - - hostInputBufferIndex = stream->input.currentBufferIndex; - - PaUtil_SetInputFrameCount( &stream->bufferProcessor, - stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer ); - - channel = 0; - for( i=0; iinput.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; - - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, - stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + - stream->input.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostInputSample, - channelCount ); - - channel += channelCount; - } - - framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead ); - - stream->input.framesUsedInCurrentBuffer += framesProcessed; - if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer ) - { - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - break; - } - - framesRead += framesProcessed; - - }else{ - /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout ); - if( waitResult == WAIT_FAILED ) - { - result = paUnanticipatedHostError; - break; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* if a timeout is encountered, continue, - perhaps we should give up eventually - */ - } - } - }while( framesRead < frames ); - } - else - { - result = paCanNotReadFromAnOutputOnlyStream; - } - - return result; -} - - -static PaError WriteStream( PaStream* s, - const void *buffer, - unsigned long frames ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - const void *userBuffer; - unsigned long framesWritten = 0; - unsigned long framesProcessed; - signed int hostOutputBufferIndex; - DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); - unsigned int channel, i; - - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - /* make a local copy of the user buffer pointer(s). this is necessary - because PaUtil_CopyOutput() advances these pointers every time - it is called. - */ - if( stream->bufferProcessor.userOutputIsInterleaved ) - { - userBuffer = buffer; - } - else - { - userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount ); - if( !userBuffer ) - return paInsufficientMemory; - for( i = 0; ibufferProcessor.outputChannelCount; ++i ) - ((const void**)userBuffer)[i] = ((const void**)buffer)[i]; - } - - do{ - if( CurrentOutputBuffersAreDone( stream ) ) - { - if( NoBuffersAreQueued( &stream->output ) ) - { - /** @todo REVIEW: consider what to do if the output - underflows. do we requeue all the existing buffers with - zeros? should we run a separate thread to keep the buffers - enqueued at all times? */ - - result = paOutputUnderflowed; - } - - hostOutputBufferIndex = stream->output.currentBufferIndex; - - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, - stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); - - channel = 0; - for( i=0; ioutput.deviceCount; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + - stream->output.framesUsedInCurrentBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - channel += channelCount; - } - - framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten ); - - stream->output.framesUsedInCurrentBuffer += framesProcessed; - if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer ) - { - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - break; - } - - framesWritten += framesProcessed; - } - else - { - /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout ); - if( waitResult == WAIT_FAILED ) - { - result = paUnanticipatedHostError; - break; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* if a timeout is encountered, continue, - perhaps we should give up eventually - */ - } - } - }while( framesWritten < frames ); - } - else - { - result = paCanNotWriteToAnInputOnlyStream; - } - - return result; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - if( PA_IS_INPUT_STREAM_(stream) ) - return GetAvailableFrames( &stream->input ); - else - return paCanNotReadFromAnOutputOnlyStream; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - if( PA_IS_OUTPUT_STREAM_(stream) ) - return GetAvailableFrames( &stream->output ); - else - return paCanNotWriteToAnInputOnlyStream; -} - - -/* NOTE: the following functions are MME-stream specific, and are called directly - by client code. We need to check for many more error conditions here because - we don't have the benefit of pa_front.c's parameter checking. -*/ - -static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s ) -{ - PaError result; - PaUtilHostApiRepresentation *hostApi; - PaWinMmeHostApiRepresentation *winMmeHostApi; - - result = PaUtil_ValidateStreamPointer( s ); - if( result != paNoError ) - return result; - - result = PaUtil_GetHostApiRepresentation( &hostApi, paMME ); - if( result != paNoError ) - return result; - - winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - - /* note, the following would be easier if there was a generic way of testing - that a stream belongs to a specific host API */ - - if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface - || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface ) - { - /* s is a WinMME stream */ - *stream = (PaWinMmeStream *)s; - return paNoError; - } - else - { - return paIncompatibleStreamHostApi; - } -} - - -int PaWinMME_GetStreamInputHandleCount( PaStream* s ) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError ) - return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0; - else - return result; -} - - -HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex ) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError - && PA_IS_INPUT_STREAM_(stream) - && handleIndex >= 0 - && (unsigned int)handleIndex < stream->input.deviceCount ) - return ((HWAVEIN*)stream->input.waveHandles)[handleIndex]; - else - return 0; -} - - -int PaWinMME_GetStreamOutputHandleCount( PaStream* s) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError ) - return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0; - else - return result; -} - - -HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex ) -{ - PaWinMmeStream *stream; - PaError result = GetWinMMEStreamPointer( &stream, s ); - - if( result == paNoError - && PA_IS_OUTPUT_STREAM_(stream) - && handleIndex >= 0 - && (unsigned int)handleIndex < stream->output.deviceCount ) - return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex]; - else - return 0; -} - - - - - +/* + * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $ + * pa_win_wmme.c + * Implementation of PortAudio for Windows MultiMedia Extensions (WMME) + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Authors: Ross Bencina and Phil Burk + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + * + */ + +/* Modification History: + PLB = Phil Burk + JM = Julien Maillard + RDB = Ross Bencina + PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer) + PLB20010413 - check for excessive numbers of channels + PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC + including conditional inclusion of memory.h, + and explicit typecasting on memory allocation + PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory + PLB20010816 - pass process instead of thread to SetPriorityClass() + PLB20010927 - use number of frames instead of real-time for CPULoad calculation. + JM20020118 - prevent hung thread when buffers underflow. + PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount + RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init + RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices + refactoring, renaming and fixed a few edge case bugs + RDB20020531 - converted to V19 framework + ** NOTE maintanance history is now stored in CVS ** +*/ + +/** @file + + @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now, + needs to be reviewed and tested.) + + @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput. + + @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may + be called asynchronously from the callback thread. this is bad. + + @todo implement inputBufferAdcTime in callback thread + + @todo review/fix error recovery and cleanup in marked functions + + @todo implement timeInfo for stream priming + + @todo handle the case where the callback returns paAbort or paComplete during stream priming. + + @todo review input overflow and output underflow handling in ReadStream and WriteStream + +Non-critical stuff for the future: + + @todo Investigate supporting host buffer formats > 16 bits + + @todo define UNICODE and _UNICODE in the project settings and see what breaks + +*/ + +/* + How it works: + + For both callback and blocking read/write streams we open the MME devices + in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever + it has finished with a buffer (either filled it for input, or played it + for output). Where necessary we block waiting for Event objects using + WaitMultipleObjects(). + + When implementing a PA callback stream, we set up a high priority thread + which waits on the MME buffer Events and drains/fills the buffers when + they are ready. + + When implementing a PA blocking read/write stream, we simply wait on these + Events (when necessary) inside the ReadStream() and WriteStream() functions. +*/ + +#include +#include +#include +#include +#include +#include +#include +/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ +#ifndef __MWERKS__ +#include +#include +#endif /* __MWERKS__ */ + +#include "portaudio.h" +#include "pa_trace.h" +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "pa_win_wmme.h" + +#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ +#pragma comment(lib, "winmm.lib") +#endif + +/************************************************* Constants ********/ + +#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */ + +#if PA_MME_USE_HIGH_DEFAULT_LATENCY_ + #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4) + #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4) + #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4) + #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4) + #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16) + #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */ + #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */ +#else + #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2) + #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2) + #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3) + #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2) + #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16) + #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */ + #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */ +#endif + +/* Use higher latency for NT because it is even worse at real-time + operation than Win9x. +*/ +#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2) +#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_) + + +#define PA_MME_MIN_TIMEOUT_MSEC_ (1000) + +static const char constInputMapperSuffix_[] = " - Input"; +static const char constOutputMapperSuffix_[] = " - Output"; + +/********************************************************************/ + +typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */ + +/* prototypes for functions declared in this file */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + + +/* macros for setting last host error information */ + +#ifdef UNICODE + +#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ + { \ + wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ + WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ + mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ + { \ + wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ + WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ + mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#else /* !UNICODE */ + +#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ + { \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ + { \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#endif /* UNICODE */ + + +static void PaMme_SetLastSystemError( DWORD errorCode ) +{ + char *lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf ); + LocalFree( lpMsgBuf ); +} + +#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \ + PaMme_SetLastSystemError( errorCode ) + + +/* PaError returning wrappers for some commonly used win32 functions + note that we allow passing a null ptr to have no effect. +*/ + +static PaError CreateEventWithPaError( HANDLE *handle, + LPSECURITY_ATTRIBUTES lpEventAttributes, + BOOL bManualReset, + BOOL bInitialState, + LPCTSTR lpName ) +{ + PaError result = paNoError; + + *handle = NULL; + + *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName ); + if( *handle == NULL ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + } + + return result; +} + + +static PaError ResetEventWithPaError( HANDLE handle ) +{ + PaError result = paNoError; + + if( handle ) + { + if( ResetEvent( handle ) == 0 ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + } + } + + return result; +} + + +static PaError CloseHandleWithPaError( HANDLE handle ) +{ + PaError result = paNoError; + + if( handle ) + { + if( CloseHandle( handle ) == 0 ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + } + } + + return result; +} + + +/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + int inputDeviceCount, outputDeviceCount; + + /** winMmeDeviceIds is an array of WinMme device ids. + fields in the range [0, inputDeviceCount) are input device ids, + and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output + device ids. + */ + UINT *winMmeDeviceIds; +} +PaWinMmeHostApiRepresentation; + + +typedef struct +{ + PaDeviceInfo inheritedDeviceInfo; + DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */ +} +PaWinMmeDeviceInfo; + + +/************************************************************************* + * Returns recommended device ID. + * On the PC, the recommended device can be specified by the user by + * setting an environment variable. For example, to use device #1. + * + * set PA_RECOMMENDED_OUTPUT_DEVICE=1 + * + * The user should first determine the available device ID by using + * the supplied application "pa_devs". + */ +#define PA_ENV_BUF_SIZE_ (32) +#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE") +#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE") +static PaDeviceIndex GetEnvDefaultDeviceID( char *envName ) +{ + PaDeviceIndex recommendedIndex = paNoDevice; + DWORD hresult; + char envbuf[PA_ENV_BUF_SIZE_]; + +#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */ + + /* Let user determine default device by setting environment variable. */ + hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) ) + { + recommendedIndex = atoi( envbuf ); + } +#endif + + return recommendedIndex; +} + + +static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi ) +{ + PaDeviceIndex device; + + /* input */ + device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ ); + if( device != paNoDevice && + ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && + hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 ) + { + hostApi->inheritedHostApiRep.info.defaultInputDevice = device; + } + + /* output */ + device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ ); + if( device != paNoDevice && + ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && + hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 ) + { + hostApi->inheritedHostApiRep.info.defaultOutputDevice = device; + } +} + + +/** Convert external PA ID to a windows multimedia device ID +*/ +static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device ) +{ + assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount ); + + return hostApi->winMmeDeviceIds[ device ]; +} + + +static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx ) +{ + MMRESULT mmresult; + + switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) ) + { + case MMSYSERR_NOERROR: + return paNoError; + case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ + return paDeviceUnavailable; + case MMSYSERR_NODRIVER: /* No device driver is present. */ + return paDeviceUnavailable; + case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ + return paInsufficientMemory; + case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ + return paSampleFormatNotSupported; + + case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ + /* falls through */ + default: + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + return paUnanticipatedHostError; + } +} + + +static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx ) +{ + MMRESULT mmresult; + + switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) ) + { + case MMSYSERR_NOERROR: + return paNoError; + case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ + return paDeviceUnavailable; + case MMSYSERR_NODRIVER: /* No device driver is present. */ + return paDeviceUnavailable; + case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ + return paInsufficientMemory; + case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ + return paSampleFormatNotSupported; + + case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ + /* falls through */ + default: + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + return paUnanticipatedHostError; + } +} + + +static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo, + PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), + int winMmeDeviceId, int channels, double sampleRate ) +{ + PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo; + WAVEFORMATEX waveFormatEx; + + if( sampleRate == 11025.0 + && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16)) + || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){ + + return paNoError; + } + + if( sampleRate == 22050.0 + && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16)) + || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){ + + return paNoError; + } + + if( sampleRate == 44100.0 + && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16)) + || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){ + + return paNoError; + } + + waveFormatEx.wFormatTag = WAVE_FORMAT_PCM; + waveFormatEx.nChannels = (WORD)channels; + waveFormatEx.nSamplesPerSec = (DWORD)sampleRate; + waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short); + waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short)); + waveFormatEx.wBitsPerSample = 16; + waveFormatEx.cbSize = 0; + + return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx ); +} + + +#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */ +static double defaultSampleRateSearchOrder_[] = + { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0, + 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; + +static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId, + PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels ) +{ + PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; + int i; + + deviceInfo->defaultSampleRate = 0.; + + for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i ) + { + double sampleRate = defaultSampleRateSearchOrder_[ i ]; + PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate ); + if( paerror == paNoError ) + { + deviceInfo->defaultSampleRate = sampleRate; + break; + } + } +} + + +static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, + PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success ) +{ + PaError result = paNoError; + char *deviceName; /* non-const ptr */ + MMRESULT mmresult; + WAVEINCAPS wic; + PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; + + *success = 0; + + mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) ); + if( mmresult == MMSYSERR_NOMEM ) + { + result = paInsufficientMemory; + goto error; + } + else if( mmresult != MMSYSERR_NOERROR ) + { + /* instead of returning paUnanticipatedHostError we return + paNoError, but leave success set as 0. This allows + Pa_Initialize to just ignore this device, without failing + the entire initialisation process. + */ + return paNoError; + } + + if( winMmeInputDeviceId == WAVE_MAPPER ) + { + /* Append I/O suffix to WAVE_MAPPER device. */ + deviceName = (char *)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, wic.szPname ); + strcat( deviceName, constInputMapperSuffix_ ); + } + else + { + deviceName = (char*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( wic.szPname ) + 1 ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, wic.szPname ); + } + deviceInfo->name = deviceName; + + deviceInfo->maxInputChannels = wic.wChannels; + /* Sometimes a device can return a rediculously large number of channels. + * This happened with an SBLive card on a Windows ME box. + * If that happens, then force it to 2 channels. PLB20010413 + */ + if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) ) + { + PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels )); + deviceInfo->maxInputChannels = 2; + } + + winMmeDeviceInfo->dwFormats = wic.dwFormats; + + DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId, + QueryInputWaveFormatEx, deviceInfo->maxInputChannels ); + + *success = 1; + +error: + return result; +} + + +static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, + PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success ) +{ + PaError result = paNoError; + char *deviceName; /* non-const ptr */ + MMRESULT mmresult; + WAVEOUTCAPS woc; + PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; + + *success = 0; + + mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) ); + if( mmresult == MMSYSERR_NOMEM ) + { + result = paInsufficientMemory; + goto error; + } + else if( mmresult != MMSYSERR_NOERROR ) + { + /* instead of returning paUnanticipatedHostError we return + paNoError, but leave success set as 0. This allows + Pa_Initialize to just ignore this device, without failing + the entire initialisation process. + */ + return paNoError; + } + + if( winMmeOutputDeviceId == WAVE_MAPPER ) + { + /* Append I/O suffix to WAVE_MAPPER device. */ + deviceName = (char *)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, woc.szPname ); + strcat( deviceName, constOutputMapperSuffix_ ); + } + else + { + deviceName = (char*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( woc.szPname ) + 1 ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, woc.szPname ); + } + deviceInfo->name = deviceName; + + deviceInfo->maxOutputChannels = woc.wChannels; + /* Sometimes a device can return a rediculously large number of channels. + * This happened with an SBLive card on a Windows ME box. + * It also happens on Win XP! + */ + if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) ) + { + PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels )); + deviceInfo->maxOutputChannels = 2; + } + + winMmeDeviceInfo->dwFormats = woc.dwFormats; + + DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId, + QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels ); + + *success = 1; + +error: + return result; +} + + +static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency ) +{ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof( osvi ); + GetVersionEx( &osvi ); + + /* Check for NT */ + if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) + { + *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_; + } + else if(osvi.dwMajorVersion >= 5) + { + *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_; + } + else + { + *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_; + } + + *defaultHighLatency = *defaultLowLatency * 2; +} + + +PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i; + PaWinMmeHostApiRepresentation *winMmeHostApi; + int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount; + PaWinMmeDeviceInfo *deviceInfoArray; + int deviceInfoInitializationSucceeded; + PaTime defaultLowLatency, defaultHighLatency; + + winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) ); + if( !winMmeHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + winMmeHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !winMmeHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &winMmeHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paMME; + (*hostApi)->info.name = "MME"; + + + /* initialise device counts and default devices under the assumption that + there are no devices. These values are incremented below if and when + devices are successfully initialized. + */ + (*hostApi)->info.deviceCount = 0; + (*hostApi)->info.defaultInputDevice = paNoDevice; + (*hostApi)->info.defaultOutputDevice = paNoDevice; + winMmeHostApi->inputDeviceCount = 0; + winMmeHostApi->outputDeviceCount = 0; + + + maximumPossibleDeviceCount = 0; + + inputDeviceCount = waveInGetNumDevs(); + if( inputDeviceCount > 0 ) + maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */ + + outputDeviceCount = waveOutGetNumDevs(); + if( outputDeviceCount > 0 ) + maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */ + + + if( maximumPossibleDeviceCount > 0 ){ + + (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount ); + if( !(*hostApi)->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + goto error; + } + + winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount ); + if( !winMmeHostApi->winMmeDeviceIds ) + { + result = paInsufficientMemory; + goto error; + } + + GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency ); + + if( inputDeviceCount > 0 ){ + /* -1 is the WAVE_MAPPER */ + for( i = -1; i < inputDeviceCount; ++i ){ + UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); + PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; + PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + + deviceInfo->defaultLowInputLatency = defaultLowLatency; + deviceInfo->defaultLowOutputLatency = defaultLowLatency; + deviceInfo->defaultHighInputLatency = defaultHighLatency; + deviceInfo->defaultHighOutputLatency = defaultHighLatency; + + result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, + winMmeDeviceId, &deviceInfoInitializationSucceeded ); + if( result != paNoError ) + goto error; + + if( deviceInfoInitializationSucceeded ){ + if( (*hostApi)->info.defaultInputDevice == paNoDevice ) + (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; + + winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; + (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; + + winMmeHostApi->inputDeviceCount++; + (*hostApi)->info.deviceCount++; + } + } + } + + if( outputDeviceCount > 0 ){ + /* -1 is the WAVE_MAPPER */ + for( i = -1; i < outputDeviceCount; ++i ){ + UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i); + PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; + PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + + deviceInfo->defaultLowInputLatency = defaultLowLatency; + deviceInfo->defaultLowOutputLatency = defaultLowLatency; + deviceInfo->defaultHighInputLatency = defaultHighLatency; + deviceInfo->defaultHighOutputLatency = defaultHighLatency; + + result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo, + winMmeDeviceId, &deviceInfoInitializationSucceeded ); + if( result != paNoError ) + goto error; + + if( deviceInfoInitializationSucceeded ){ + if( (*hostApi)->info.defaultOutputDevice == paNoDevice ) + (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; + + winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; + (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; + + winMmeHostApi->outputDeviceCount++; + (*hostApi)->info.deviceCount++; + } + } + } + } + + + InitializeDefaultDeviceIdsFromEnv( winMmeHostApi ); + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyRead, PaUtil_DummyWrite, + PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); + + PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + return result; + +error: + if( winMmeHostApi ) + { + if( winMmeHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); + } + + PaUtil_FreeMemory( winMmeHostApi ); + } + + return result; +} + + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; + + if( winMmeHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); + } + + PaUtil_FreeMemory( winMmeHostApi ); +} + + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; + PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo; + int inputChannelCount, outputChannelCount; + int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; + UINT winMmeInputDeviceId, winMmeOutputDeviceId; + unsigned int i; + PaError paerror; + + /* The calls to QueryFormatSupported below are intended to detect invalid + sample rates. If we assume that the channel count and format are OK, + then the only thing that could fail is the sample rate. This isn't + strictly true, but I can't think of a better way to test that the + sample rate is valid. + */ + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + inputStreamInfo = inputParameters->hostApiSpecificStreamInfo; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if( inputSampleFormat & paCustomFormat ) + return paSampleFormatNotSupported; + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification + && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) ) + { + inputMultipleDeviceChannelCount = 0; + for( i=0; i< inputStreamInfo->deviceCount; ++i ) + { + inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount; + + inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ]; + + /* check that input device can support inputChannelCount */ + if( inputStreamInfo->devices[i].channelCount <= 0 + || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels ) + return paInvalidChannelCount; + + /* test for valid sample rate, see comment above */ + winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device ); + paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate ); + if( paerror != paNoError ) + return paInvalidSampleRate; + } + + if( inputMultipleDeviceChannelCount != inputChannelCount ) + return paIncompatibleHostApiSpecificStreamInfo; + } + else + { + if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) ) + return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */ + + inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ]; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > inputDeviceInfo->maxInputChannels ) + return paInvalidChannelCount; + + /* test for valid sample rate, see comment above */ + winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device ); + paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate ); + if( paerror != paNoError ) + return paInvalidSampleRate; + } + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + outputStreamInfo = outputParameters->hostApiSpecificStreamInfo; + + /* all standard sample formats are supported by the buffer adapter, + this implementation doesn't support any custom sample formats */ + if( outputSampleFormat & paCustomFormat ) + return paSampleFormatNotSupported; + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification + && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) ) + { + outputMultipleDeviceChannelCount = 0; + for( i=0; i< outputStreamInfo->deviceCount; ++i ) + { + outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount; + + outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ]; + + /* check that output device can support outputChannelCount */ + if( outputStreamInfo->devices[i].channelCount <= 0 + || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels ) + return paInvalidChannelCount; + + /* test for valid sample rate, see comment above */ + winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device ); + paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate ); + if( paerror != paNoError ) + return paInvalidSampleRate; + } + + if( outputMultipleDeviceChannelCount != outputChannelCount ) + return paIncompatibleHostApiSpecificStreamInfo; + } + else + { + if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) ) + return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */ + + outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ]; + + /* check that output device can support outputChannelCount */ + if( outputChannelCount > outputDeviceInfo->maxOutputChannels ) + return paInvalidChannelCount; + + /* test for valid sample rate, see comment above */ + winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device ); + paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate ); + if( paerror != paNoError ) + return paInvalidSampleRate; + } + } + + /* + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + + for mme all we can do is test that the input and output devices + support the requested sample rate and number of channels. we + cannot test for full duplex compatibility. + */ + + return paFormatIsSupported; +} + + + +static void SelectBufferSizeAndCount( unsigned long baseBufferSize, + unsigned long requestedLatency, + unsigned long baseBufferCount, unsigned long minimumBufferCount, + unsigned long maximumBufferSize, unsigned long *hostBufferSize, + unsigned long *hostBufferCount ) +{ + unsigned long sizeMultiplier, bufferCount, latency; + unsigned long nextLatency, nextBufferSize; + int baseBufferSizeIsPowerOfTwo; + + sizeMultiplier = 1; + bufferCount = baseBufferCount; + + /* count-1 below because latency is always determined by one less + than the total number of buffers. + */ + latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); + + if( latency > requestedLatency ) + { + + /* reduce number of buffers without falling below suggested latency */ + + nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); + while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) + { + --bufferCount; + nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2); + } + + }else if( latency < requestedLatency ){ + + baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1))); + if( baseBufferSizeIsPowerOfTwo ){ + + /* double size of buffers without exceeding requestedLatency */ + + nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); + nextLatency = nextBufferSize * (bufferCount-1); + while( nextBufferSize <= maximumBufferSize + && nextLatency < requestedLatency ) + { + sizeMultiplier *= 2; + nextBufferSize = (baseBufferSize * (sizeMultiplier*2)); + nextLatency = nextBufferSize * (bufferCount-1); + } + + }else{ + + /* increase size of buffers upto first excess of requestedLatency */ + + nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); + nextLatency = nextBufferSize * (bufferCount-1); + while( nextBufferSize <= maximumBufferSize + && nextLatency < requestedLatency ) + { + ++sizeMultiplier; + nextBufferSize = (baseBufferSize * (sizeMultiplier+1)); + nextLatency = nextBufferSize * (bufferCount-1); + } + + if( nextLatency < requestedLatency ) + ++sizeMultiplier; + } + + /* increase number of buffers until requestedLatency is reached */ + + latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); + while( latency < requestedLatency ) + { + ++bufferCount; + latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1); + } + } + + *hostBufferSize = baseBufferSize * sizeMultiplier; + *hostBufferCount = bufferCount; +} + + +static void ReselectBufferCount( unsigned long bufferSize, + unsigned long requestedLatency, + unsigned long baseBufferCount, unsigned long minimumBufferCount, + unsigned long *hostBufferCount ) +{ + unsigned long bufferCount, latency; + unsigned long nextLatency; + + bufferCount = baseBufferCount; + + /* count-1 below because latency is always determined by one less + than the total number of buffers. + */ + latency = bufferSize * (bufferCount-1); + + if( latency > requestedLatency ) + { + /* reduce number of buffers without falling below suggested latency */ + + nextLatency = bufferSize * (bufferCount-2); + while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency ) + { + --bufferCount; + nextLatency = bufferSize * (bufferCount-2); + } + + }else if( latency < requestedLatency ){ + + /* increase number of buffers until requestedLatency is reached */ + + latency = bufferSize * (bufferCount-1); + while( latency < requestedLatency ) + { + ++bufferCount; + latency = bufferSize * (bufferCount-1); + } + } + + *hostBufferCount = bufferCount; +} + + +/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount, + framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values + of the other parameters. +*/ + +static PaError CalculateBufferSettings( + unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount, + unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount, + int inputChannelCount, PaSampleFormat hostInputSampleFormat, + PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo, + int outputChannelCount, PaSampleFormat hostOutputSampleFormat, + PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo, + double sampleRate, unsigned long framesPerBuffer ) +{ + PaError result = paNoError; + int effectiveInputChannelCount, effectiveOutputChannelCount; + int hostInputFrameSize = 0; + unsigned int i; + + if( inputChannelCount > 0 ) + { + int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat ); + if( hostInputSampleSize < 0 ) + { + result = hostInputSampleSize; + goto error; + } + + if( inputStreamInfo + && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) + { + /* set effectiveInputChannelCount to the largest number of + channels on any one device. + */ + effectiveInputChannelCount = 0; + for( i=0; i< inputStreamInfo->deviceCount; ++i ) + { + if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount ) + effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount; + } + } + else + { + effectiveInputChannelCount = inputChannelCount; + } + + hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount; + + if( inputStreamInfo + && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) + { + if( inputStreamInfo->bufferCount <= 0 + || inputStreamInfo->framesPerBuffer <= 0 ) + { + result = paIncompatibleHostApiSpecificStreamInfo; + goto error; + } + + *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer; + *hostInputBufferCount = inputStreamInfo->bufferCount; + } + else + { + unsigned long hostBufferSizeBytes, hostBufferCount; + unsigned long minimumBufferCount = (outputChannelCount > 0) + ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ + : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_; + + unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize); + if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) + maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; + + /* compute the following in bytes, then convert back to frames */ + + SelectBufferSizeAndCount( + ((framesPerBuffer == paFramesPerBufferUnspecified) + ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ + : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */ + ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ + 4, /* baseBufferCount */ + minimumBufferCount, maximumBufferSize, + &hostBufferSizeBytes, &hostBufferCount ); + + *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize; + *hostInputBufferCount = hostBufferCount; + } + } + else + { + *framesPerHostInputBuffer = 0; + *hostInputBufferCount = 0; + } + + if( outputChannelCount > 0 ) + { + if( outputStreamInfo + && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) + { + if( outputStreamInfo->bufferCount <= 0 + || outputStreamInfo->framesPerBuffer <= 0 ) + { + result = paIncompatibleHostApiSpecificStreamInfo; + goto error; + } + + *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer; + *hostOutputBufferCount = outputStreamInfo->bufferCount; + + + if( inputChannelCount > 0 ) /* full duplex */ + { + if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer ) + { + if( inputStreamInfo + && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) ) + { + /* a custom StreamInfo was used for specifying both input + and output buffer sizes, the larger buffer size + must be a multiple of the smaller buffer size */ + + if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer ) + { + if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 ) + { + result = paIncompatibleHostApiSpecificStreamInfo; + goto error; + } + } + else + { + assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer ); + if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 ) + { + result = paIncompatibleHostApiSpecificStreamInfo; + goto error; + } + } + } + else + { + /* a custom StreamInfo was not used for specifying the input buffer size, + so use the output buffer size, and approximately the same latency. */ + + *framesPerHostInputBuffer = *framesPerHostOutputBuffer; + *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1; + + if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ ) + *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; + } + } + } + } + else + { + unsigned long hostBufferSizeBytes, hostBufferCount; + unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; + unsigned long maximumBufferSize; + int hostOutputFrameSize; + int hostOutputSampleSize; + + hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat ); + if( hostOutputSampleSize < 0 ) + { + result = hostOutputSampleSize; + goto error; + } + + if( outputStreamInfo + && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) ) + { + /* set effectiveOutputChannelCount to the largest number of + channels on any one device. + */ + effectiveOutputChannelCount = 0; + for( i=0; i< outputStreamInfo->deviceCount; ++i ) + { + if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount ) + effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount; + } + } + else + { + effectiveOutputChannelCount = outputChannelCount; + } + + hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount; + + maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize); + if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ ) + maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_; + + + /* compute the following in bytes, then convert back to frames */ + + SelectBufferSizeAndCount( + ((framesPerBuffer == paFramesPerBufferUnspecified) + ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ + : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */ + ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ + 4, /* baseBufferCount */ + minimumBufferCount, + maximumBufferSize, + &hostBufferSizeBytes, &hostBufferCount ); + + *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize; + *hostOutputBufferCount = hostBufferCount; + + + if( inputChannelCount > 0 ) + { + /* ensure that both input and output buffer sizes are the same. + if they don't match at this stage, choose the smallest one + and use that for input and output + */ + + if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer ) + { + if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) + { + unsigned long framesPerHostBuffer = *framesPerHostInputBuffer; + + minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_; + ReselectBufferCount( + framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */ + ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */ + 4, /* baseBufferCount */ + minimumBufferCount, + &hostBufferCount ); + + *framesPerHostOutputBuffer = framesPerHostBuffer; + *hostOutputBufferCount = hostBufferCount; + } + else + { + unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer; + + minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_; + ReselectBufferCount( + framesPerHostBuffer * hostInputFrameSize, /* bufferSize */ + ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */ + 4, /* baseBufferCount */ + minimumBufferCount, + &hostBufferCount ); + + *framesPerHostInputBuffer = framesPerHostBuffer; + *hostInputBufferCount = hostBufferCount; + } + } + } + } + } + else + { + *framesPerHostOutputBuffer = 0; + *hostOutputBufferCount = 0; + } + +error: + return result; +} + + +typedef struct +{ + HANDLE bufferEvent; + void *waveHandles; + unsigned int deviceCount; + /* unsigned int channelCount; */ + WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */ + unsigned int bufferCount; + unsigned int currentBufferIndex; + unsigned int framesPerBuffer; + unsigned int framesUsedInCurrentBuffer; +}PaWinMmeSingleDirectionHandlesAndBuffers; + +/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */ + +static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ); +static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, + PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, + unsigned long bytesPerHostSample, + double sampleRate, PaWinMmeDeviceAndChannelCount *devices, + unsigned int deviceCount, int isInput ); +static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError ); +static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, + unsigned long hostBufferCount, + PaSampleFormat hostSampleFormat, + unsigned long framesPerHostBuffer, + PaWinMmeDeviceAndChannelCount *devices, + int isInput ); +static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput ); + + +static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) +{ + handlesAndBuffers->bufferEvent = 0; + handlesAndBuffers->waveHandles = 0; + handlesAndBuffers->deviceCount = 0; + handlesAndBuffers->waveHeaders = 0; + handlesAndBuffers->bufferCount = 0; +} + +static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, + PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, + unsigned long bytesPerHostSample, + double sampleRate, PaWinMmeDeviceAndChannelCount *devices, + unsigned int deviceCount, int isInput ) +{ + PaError result; + MMRESULT mmresult; + unsigned long bytesPerFrame; + WAVEFORMATEX wfx; + signed int i; + + /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers() + has already been called to zero some fields */ + + result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL ); + if( result != paNoError ) goto error; + + if( isInput ) + handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount ); + else + handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount ); + if( !handlesAndBuffers->waveHandles ) + { + result = paInsufficientMemory; + goto error; + } + + handlesAndBuffers->deviceCount = deviceCount; + + for( i = 0; i < (signed int)deviceCount; ++i ) + { + if( isInput ) + ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0; + else + ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0; + } + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nSamplesPerSec = (DWORD) sampleRate; + wfx.cbSize = 0; + + for( i = 0; i < (signed int)deviceCount; ++i ) + { + UINT winMmeDeviceId; + + winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device ); + wfx.nChannels = (WORD)devices[i].channelCount; + + bytesPerFrame = wfx.nChannels * bytesPerHostSample; + + wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate); + wfx.nBlockAlign = (WORD)bytesPerFrame; + wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8); + + /* REVIEW: consider not firing an event for input when a full duplex + stream is being used. this would probably depend on the + neverDropInput flag. */ + + if( isInput ) + mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, + (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT ); + else + mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, + (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT ); + + if( mmresult != MMSYSERR_NOERROR ) + { + switch( mmresult ) + { + case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_NODRIVER: /* No device driver is present. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ + result = paInsufficientMemory; + break; + + case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ + /* falls through */ + case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ + /* falls through */ + default: + result = paUnanticipatedHostError; + if( isInput ) + { + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + else + { + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + } + goto error; + } + } + + return result; + +error: + TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ ); + + return result; +} + + +static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError ) +{ + PaError result = paNoError; + MMRESULT mmresult; + signed int i; + + if( handlesAndBuffers->waveHandles ) + { + for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i ) + { + if( isInput ) + { + if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] ) + mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] ); + } + else + { + if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ) + mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] ); + } + + if( mmresult != MMSYSERR_NOERROR && + !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */ + { + result = paUnanticipatedHostError; + if( isInput ) + { + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + else + { + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + /* note that we don't break here, we try to continue closing devices */ + } + } + + PaUtil_FreeMemory( handlesAndBuffers->waveHandles ); + handlesAndBuffers->waveHandles = 0; + } + + if( handlesAndBuffers->bufferEvent ) + { + result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent ); + handlesAndBuffers->bufferEvent = 0; + } + + return result; +} + + +static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, + unsigned long hostBufferCount, + PaSampleFormat hostSampleFormat, + unsigned long framesPerHostBuffer, + PaWinMmeDeviceAndChannelCount *devices, + int isInput ) +{ + PaError result = paNoError; + MMRESULT mmresult; + WAVEHDR *deviceWaveHeaders; + signed int i, j; + + /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers() + has already been called to zero some fields */ + + + /* allocate an array of pointers to arrays of wave headers, one array of + wave headers per device */ + handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount ); + if( !handlesAndBuffers->waveHeaders ) + { + result = paInsufficientMemory; + goto error; + } + + for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i ) + handlesAndBuffers->waveHeaders[i] = 0; + + handlesAndBuffers->bufferCount = hostBufferCount; + + for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i ) + { + int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) * + framesPerHostBuffer * devices[i].channelCount; + if( bufferBytes < 0 ) + { + result = paInternalError; + goto error; + } + + /* Allocate an array of wave headers for device i */ + deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount ); + if( !deviceWaveHeaders ) + { + result = paInsufficientMemory; + goto error; + } + + for( j=0; j < (signed int)hostBufferCount; ++j ) + deviceWaveHeaders[j].lpData = 0; + + handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders; + + /* Allocate a buffer for each wave header */ + for( j=0; j < (signed int)hostBufferCount; ++j ) + { + deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes ); + if( !deviceWaveHeaders[j].lpData ) + { + result = paInsufficientMemory; + goto error; + } + deviceWaveHeaders[j].dwBufferLength = bufferBytes; + deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */ + + if( isInput ) + { + mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + goto error; + } + } + else /* output */ + { + mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + goto error; + } + } + deviceWaveHeaders[j].dwUser = devices[i].channelCount; + } + } + + return result; + +error: + TerminateWaveHeaders( handlesAndBuffers, isInput ); + + return result; +} + + +static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput ) +{ + signed int i, j; + WAVEHDR *deviceWaveHeaders; + + if( handlesAndBuffers->waveHeaders ) + { + for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i ) + { + deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */ + if( deviceWaveHeaders ) + { + for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j ) + { + if( deviceWaveHeaders[j].lpData ) + { + if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF ) + { + if( isInput ) + waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); + else + waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) ); + } + + PaUtil_FreeMemory( deviceWaveHeaders[j].lpData ); + } + } + + PaUtil_FreeMemory( deviceWaveHeaders ); + } + } + + PaUtil_FreeMemory( handlesAndBuffers->waveHeaders ); + handlesAndBuffers->waveHeaders = 0; + } +} + + + +/* PaWinMmeStream - a stream data structure specifically for this implementation */ +/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */ +struct PaWinMmeStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + int primeStreamUsingCallback; + + PaWinMmeSingleDirectionHandlesAndBuffers input; + PaWinMmeSingleDirectionHandlesAndBuffers output; + + /* Processing thread management -------------- */ + HANDLE abortEvent; + HANDLE processingThread; + DWORD processingThreadId; + + char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */ + int processingThreadPriority; + int highThreadPriority; + int throttledThreadPriority; + unsigned long throttledSleepMsecs; + + int isStopped; + volatile int isActive; + volatile int stopProcessing; /* stop thread once existing buffers have been returned */ + volatile int abortProcessing; /* stop thread immediately */ + + DWORD allBuffersDurationMs; /* used to calculate timeouts */ +}; + +/* updates deviceCount if PaWinMmeUseMultipleDevices is used */ + +static PaError ValidateWinMmeSpecificStreamInfo( + const PaStreamParameters *streamParameters, + const PaWinMmeStreamInfo *streamInfo, + char *throttleProcessingThreadOnOverload, + unsigned long *deviceCount ) +{ + if( streamInfo ) + { + if( streamInfo->size != sizeof( PaWinMmeStreamInfo ) + || streamInfo->version != 1 ) + { + return paIncompatibleHostApiSpecificStreamInfo; + } + + if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread ) + *throttleProcessingThreadOnOverload = 0; + + if( streamInfo->flags & paWinMmeUseMultipleDevices ) + { + if( streamParameters->device != paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + *deviceCount = streamInfo->deviceCount; + } + } + + return paNoError; +} + +static PaError RetrieveDevicesFromStreamParameters( + struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *streamParameters, + const PaWinMmeStreamInfo *streamInfo, + PaWinMmeDeviceAndChannelCount *devices, + unsigned long deviceCount ) +{ + PaError result = paNoError; + unsigned int i; + int totalChannelCount; + PaDeviceIndex hostApiDevice; + + if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices ) + { + totalChannelCount = 0; + for( i=0; i < deviceCount; ++i ) + { + /* validate that the device number is within range */ + result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, + streamInfo->devices[i].device, hostApi ); + if( result != paNoError ) + return result; + + devices[i].device = hostApiDevice; + devices[i].channelCount = streamInfo->devices[i].channelCount; + + totalChannelCount += devices[i].channelCount; + } + + if( totalChannelCount != streamParameters->channelCount ) + { + /* channelCount must match total channels specified by multiple devices */ + return paInvalidChannelCount; /* REVIEW use of this error code */ + } + } + else + { + devices[0].device = streamParameters->device; + devices[0].channelCount = streamParameters->channelCount; + } + + return result; +} + +static PaError ValidateInputChannelCounts( + struct PaUtilHostApiRepresentation *hostApi, + PaWinMmeDeviceAndChannelCount *devices, + unsigned long deviceCount ) +{ + unsigned int i; + + for( i=0; i < deviceCount; ++i ) + { + if( devices[i].channelCount < 1 || devices[i].channelCount + > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels ) + return paInvalidChannelCount; + } + + return paNoError; +} + +static PaError ValidateOutputChannelCounts( + struct PaUtilHostApiRepresentation *hostApi, + PaWinMmeDeviceAndChannelCount *devices, + unsigned long deviceCount ) +{ + unsigned int i; + + for( i=0; i < deviceCount; ++i ) + { + if( devices[i].channelCount < 1 || devices[i].channelCount + > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels ) + return paInvalidChannelCount; + } + + return paNoError; +} + + +/* the following macros are intended to improve the readability of the following code */ +#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles ) +#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles ) +#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles ) +#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) ) + +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result; + PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; + PaWinMmeStream *stream = 0; + int bufferProcessorIsInitialized = 0; + int streamRepresentationIsInitialized = 0; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + double suggestedInputLatency, suggestedOutputLatency; + PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; + unsigned long framesPerHostInputBuffer; + unsigned long hostInputBufferCount; + unsigned long framesPerHostOutputBuffer; + unsigned long hostOutputBufferCount; + unsigned long framesPerBufferProcessorCall; + PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ + unsigned long inputDeviceCount = 0; + PaWinMmeDeviceAndChannelCount *outputDevices = 0; + unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ + char throttleProcessingThreadOnOverload = 1; + + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + suggestedInputLatency = inputParameters->suggestedLatency; + + inputDeviceCount = 1; + + /* validate input hostApiSpecificStreamInfo */ + inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo; + result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo, + &throttleProcessingThreadOnOverload, + &inputDeviceCount ); + if( result != paNoError ) return result; + + inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount ); + if( !inputDevices ) return paInsufficientMemory; + + result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount ); + if( result != paNoError ) return result; + + result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount ); + if( result != paNoError ) return result; + + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); + } + else + { + inputChannelCount = 0; + inputSampleFormat = 0; + suggestedInputLatency = 0.; + inputStreamInfo = 0; + hostInputSampleFormat = 0; + } + + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + suggestedOutputLatency = outputParameters->suggestedLatency; + + outputDeviceCount = 1; + + /* validate output hostApiSpecificStreamInfo */ + outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo; + result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo, + &throttleProcessingThreadOnOverload, + &outputDeviceCount ); + if( result != paNoError ) return result; + + outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount ); + if( !outputDevices ) return paInsufficientMemory; + + result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount ); + if( result != paNoError ) return result; + + result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount ); + if( result != paNoError ) return result; + + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); + } + else + { + outputChannelCount = 0; + outputSampleFormat = 0; + outputStreamInfo = 0; + hostOutputSampleFormat = 0; + suggestedOutputLatency = 0.; + } + + + /* + IMPLEMENT ME: + - alter sampleRate to a close allowable rate if possible / necessary + */ + + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + + result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount, + &framesPerHostOutputBuffer, &hostOutputBufferCount, + inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo, + outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo, + sampleRate, framesPerBuffer ); + if( result != paNoError ) goto error; + + + stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + InitializeSingleDirectionHandlesAndBuffers( &stream->input ); + InitializeSingleDirectionHandlesAndBuffers( &stream->output ); + + stream->abortEvent = 0; + stream->processingThread = 0; + + stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload; + + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + ( (streamCallback) + ? &winMmeHostApi->callbackStreamInterface + : &winMmeHostApi->blockingStreamInterface ), + streamCallback, userData ); + streamRepresentationIsInitialized = 1; + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + + if( inputParameters && outputParameters ) /* full duplex */ + { + if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) + { + assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */ + + framesPerBufferProcessorCall = framesPerHostInputBuffer; + } + else + { + assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */ + + framesPerBufferProcessorCall = framesPerHostOutputBuffer; + } + } + else if( inputParameters ) + { + framesPerBufferProcessorCall = framesPerHostInputBuffer; + } + else if( outputParameters ) + { + framesPerBufferProcessorCall = framesPerHostOutputBuffer; + } + + stream->input.framesPerBuffer = framesPerHostInputBuffer; + stream->output.framesPerBuffer = framesPerHostOutputBuffer; + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerBufferProcessorCall, paUtilFixedHostBufferSize, + streamCallback, userData ); + if( result != paNoError ) goto error; + + bufferProcessorIsInitialized = 1; + + stream->streamRepresentation.streamInfo.inputLatency = + (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) + +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate; + stream->streamRepresentation.streamInfo.outputLatency = + (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) + +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate; + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0; + + /* time to sleep when throttling due to >100% cpu usage. + -a quater of a buffer's duration */ + stream->throttledSleepMsecs = + (unsigned long)(stream->bufferProcessor.framesPerHostBuffer * + stream->bufferProcessor.samplePeriod * .25 * 1000); + + stream->isStopped = 1; + stream->isActive = 0; + + + /* for maximum compatibility with multi-device multichannel drivers, + we first open all devices, then we prepare all buffers, finally + we start all devices ( in StartStream() ). teardown in reverse order. + */ + + if( inputParameters ) + { + result = InitializeWaveHandles( winMmeHostApi, &stream->input, + stream->bufferProcessor.bytesPerHostInputSample, sampleRate, + inputDevices, inputDeviceCount, 1 /* isInput */ ); + if( result != paNoError ) goto error; + } + + if( outputParameters ) + { + result = InitializeWaveHandles( winMmeHostApi, &stream->output, + stream->bufferProcessor.bytesPerHostOutputSample, sampleRate, + outputDevices, outputDeviceCount, 0 /* isInput */ ); + if( result != paNoError ) goto error; + } + + if( inputParameters ) + { + result = InitializeWaveHeaders( &stream->input, hostInputBufferCount, + hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ ); + if( result != paNoError ) goto error; + } + + if( outputParameters ) + { + result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount, + hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ ); + if( result != paNoError ) goto error; + + stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate); + } + else + { + stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate); + } + + + if( streamCallback ) + { + /* abort event is only needed for callback streams */ + result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL ); + if( result != paNoError ) goto error; + } + + *s = (PaStream*)stream; + + return result; + +error: + + if( stream ) + { + if( stream->abortEvent ) + CloseHandle( stream->abortEvent ); + + TerminateWaveHeaders( &stream->output, 0 /* not isInput */ ); + TerminateWaveHeaders( &stream->input, 1 /* isInput */ ); + + TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ ); + TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ ); + + if( bufferProcessorIsInitialized ) + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + + if( streamRepresentationIsInitialized ) + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + + PaUtil_FreeMemory( stream ); + } + + return result; +} + + +/* return non-zero if all current buffers are done */ +static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex ) +{ + unsigned int i; + + for( i=0; i < deviceCount; ++i ) + { + if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) ) + { + return 0; + } + } + + return 1; +} + +static int CurrentInputBuffersAreDone( PaWinMmeStream *stream ) +{ + return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex ); +} + +static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream ) +{ + return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex ); +} + + +/* return non-zero if any buffers are queued */ +static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) +{ + unsigned int i, j; + + if( handlesAndBuffers->waveHandles ) + { + for( i=0; i < handlesAndBuffers->bufferCount; ++i ) + { + for( j=0; j < handlesAndBuffers->deviceCount; ++j ) + { + if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) ) + { + return 0; + } + } + } + } + + return 1; +} + + +#define PA_CIRCULAR_INCREMENT_( current, max )\ + ( (((current) + 1) >= (max)) ? (0) : (current+1) ) + +#define PA_CIRCULAR_DECREMENT_( current, max )\ + ( ((current) == 0) ? ((max)-1) : (current-1) ) + + +static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ) +{ + signed long result = 0; + unsigned int i; + + if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) ) + { + /* we could calculate the following in O(1) if we kept track of the + last done buffer */ + result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer; + + i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount ); + while( i != handlesAndBuffers->currentBufferIndex ) + { + if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) ) + { + result += handlesAndBuffers->framesPerBuffer; + i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount ); + } + else + break; + } + } + + return result; +} + + +static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + unsigned int i; + + for( i=0; i < stream->input.deviceCount; ++i ) + { + mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i], + &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ], + sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + } + + stream->input.currentBufferIndex = + PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount ); + + stream->input.framesUsedInCurrentBuffer = 0; + + return result; +} + + +static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + unsigned int i; + + for( i=0; i < stream->output.deviceCount; ++i ) + { + mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i], + &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ], + sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + } + + stream->output.currentBufferIndex = + PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount ); + + stream->output.framesUsedInCurrentBuffer = 0; + + return result; +} + + +/* requeue all but the most recent input with the driver. Used for catching + up after a total input buffer underrun */ +static PaError CatchUpInputBuffers( PaWinMmeStream *stream ) +{ + PaError result = paNoError; + unsigned int i; + + for( i=0; i < stream->input.bufferCount - 1; ++i ) + { + result = AdvanceToNextInputBuffer( stream ); + if( result != paNoError ) + break; + } + + return result; +} + + +/* take the most recent output and duplicate it to all other output buffers + and requeue them. Used for catching up after a total output buffer underrun. +*/ +static PaError CatchUpOutputBuffers( PaWinMmeStream *stream ) +{ + PaError result = paNoError; + unsigned int i, j; + unsigned int previousBufferIndex = + PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount ); + + for( i=0; i < stream->output.bufferCount - 1; ++i ) + { + for( j=0; j < stream->output.deviceCount; ++j ) + { + if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData + != stream->output.waveHeaders[j][ previousBufferIndex ].lpData ) + { + CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData, + stream->output.waveHeaders[j][ previousBufferIndex ].lpData, + stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength ); + } + } + + result = AdvanceToNextOutputBuffer( stream ); + if( result != paNoError ) + break; + } + + return result; +} + + +static DWORD WINAPI ProcessingThreadProc( void *pArg ) +{ + PaWinMmeStream *stream = (PaWinMmeStream *)pArg; + HANDLE events[3]; + int eventCount = 0; + DWORD result = paNoError; + DWORD waitResult; + DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); + int hostBuffersAvailable; + signed int hostInputBufferIndex, hostOutputBufferIndex; + PaStreamCallbackFlags statusFlags; + int callbackResult; + int done = 0; + unsigned int channel, i; + unsigned long framesProcessed; + + /* prepare event array for call to WaitForMultipleObjects() */ + if( stream->input.bufferEvent ) + events[eventCount++] = stream->input.bufferEvent; + if( stream->output.bufferEvent ) + events[eventCount++] = stream->output.bufferEvent; + events[eventCount++] = stream->abortEvent; + + statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */ + + /* loop until something causes us to stop */ + do{ + /* wait for MME to signal that a buffer is available, or for + the PA abort event to be signaled. + + When this indicates that one or more buffers are available + NoBuffersAreQueued() and Current*BuffersAreDone are used below to + poll for additional done buffers. NoBuffersAreQueued() will fail + to identify an underrun/overflow if the driver doesn't mark all done + buffers prior to signalling the event. Some drivers do this + (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a + huge problem, it just means that we won't always be able to detect + underflow/overflow. + */ + waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout ); + if( waitResult == WAIT_FAILED ) + { + result = paUnanticipatedHostError; + /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */ + done = 1; + } + else if( waitResult == WAIT_TIMEOUT ) + { + /* if a timeout is encountered, continue */ + } + + if( stream->abortProcessing ) + { + /* Pa_AbortStream() has been called, stop processing immediately */ + done = 1; + } + else if( stream->stopProcessing ) + { + /* Pa_StopStream() has been called or the user callback returned + non-zero, processing will continue until all output buffers + are marked as done. The stream will stop immediately if it + is input-only. + */ + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + if( NoBuffersAreQueued( &stream->output ) ) + done = 1; /* Will cause thread to return. */ + } + else + { + /* input only stream */ + done = 1; /* Will cause thread to return. */ + } + } + else + { + hostBuffersAvailable = 1; + + /* process all available host buffers */ + do + { + hostInputBufferIndex = -1; + hostOutputBufferIndex = -1; + + if( PA_IS_INPUT_STREAM_(stream) ) + { + if( CurrentInputBuffersAreDone( stream ) ) + { + if( NoBuffersAreQueued( &stream->input ) ) + { + /** @todo + if all of the other buffers are also ready then + we discard all but the most recent. This is an + input buffer overflow. FIXME: these buffers should + be passed to the callback in a paNeverDropInput + stream. + + note that it is also possible for an input overflow + to happen while the callback is processing a buffer. + that is handled further down. + */ + result = CatchUpInputBuffers( stream ); + if( result != paNoError ) + done = 1; + + statusFlags |= paInputOverflow; + } + + hostInputBufferIndex = stream->input.currentBufferIndex; + } + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + if( CurrentOutputBuffersAreDone( stream ) ) + { + /* ok, we have an output buffer */ + + if( NoBuffersAreQueued( &stream->output ) ) + { + /* + if all of the other buffers are also ready, catch up by copying + the most recently generated buffer into all but one of the output + buffers. + + note that this catch up code only handles the case where all + buffers have been played out due to this thread not having + woken up at all. a more common case occurs when this thread + is woken up, processes one buffer, but takes too long, and as + a result all the other buffers have become un-queued. that + case is handled further down. + */ + + result = CatchUpOutputBuffers( stream ); + if( result != paNoError ) + done = 1; + + statusFlags |= paOutputUnderflow; + } + + hostOutputBufferIndex = stream->output.currentBufferIndex; + } + } + + + if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) || + (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) ) + { + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */ + + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime + from the current wave out position */ + MMTIME mmtime; + double timeBeforeGetPosition, timeAfterGetPosition; + double time; + long framesInBufferRing; + long writePosition; + long playbackPosition; + HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0]; + + mmtime.wType = TIME_SAMPLES; + timeBeforeGetPosition = PaUtil_GetTime(); + waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) ); + timeAfterGetPosition = PaUtil_GetTime(); + + timeInfo.currentTime = timeAfterGetPosition; + + /* approximate time at which wave out position was measured + as half way between timeBeforeGetPosition and timeAfterGetPosition */ + time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5; + + framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer; + playbackPosition = mmtime.u.sample % framesInBufferRing; + + writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer + + stream->output.framesUsedInCurrentBuffer; + + if( playbackPosition >= writePosition ){ + timeInfo.outputBufferDacTime = + time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod ); + }else{ + timeInfo.outputBufferDacTime = + time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod ); + } + } + + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags ); + + /* reset status flags once they have been passed to the buffer processor */ + statusFlags = 0; + + if( PA_IS_INPUT_STREAM_(stream) ) + { + PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + + channel = 0; + for( i=0; iinput.deviceCount; ++i ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; + + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, + stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + + stream->input.framesUsedInCurrentBuffer * channelCount * + stream->bufferProcessor.bytesPerHostInputSample, + channelCount ); + + + channel += channelCount; + } + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + + channel = 0; + for( i=0; ioutput.deviceCount; ++i ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; + + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, + stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + + stream->output.framesUsedInCurrentBuffer * channelCount * + stream->bufferProcessor.bytesPerHostOutputSample, + channelCount ); + + channel += channelCount; + } + } + + callbackResult = paContinue; + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + + stream->input.framesUsedInCurrentBuffer += framesProcessed; + stream->output.framesUsedInCurrentBuffer += framesProcessed; + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + if( callbackResult == paContinue ) + { + /* nothing special to do */ + } + else if( callbackResult == paAbort ) + { + stream->abortProcessing = 1; + done = 1; + /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */ + result = paNoError; + } + else + { + /* User callback has asked us to stop with paComplete or other non-zero value */ + stream->stopProcessing = 1; /* stop once currently queued audio has finished */ + result = paNoError; + } + + + if( PA_IS_INPUT_STREAM_(stream) + && stream->stopProcessing == 0 && stream->abortProcessing == 0 + && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer ) + { + if( NoBuffersAreQueued( &stream->input ) ) + { + /** @todo need to handle PaNeverDropInput here where necessary */ + result = CatchUpInputBuffers( stream ); + if( result != paNoError ) + done = 1; + + statusFlags |= paInputOverflow; + } + + result = AdvanceToNextInputBuffer( stream ); + if( result != paNoError ) + done = 1; + } + + + if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing ) + { + if( stream->stopProcessing && + stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer ) + { + /* zero remaining samples in output output buffer and flush */ + + stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor, + stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); + + /* we send the entire buffer to the output devices, but we could + just send a partial buffer, rather than zeroing the unused + samples. + */ + } + + if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer ) + { + /* check for underflow before enquing the just-generated buffer, + but recover from underflow after enquing it. This ensures + that the most recent audio segment is repeated */ + int outputUnderflow = NoBuffersAreQueued( &stream->output ); + + result = AdvanceToNextOutputBuffer( stream ); + if( result != paNoError ) + done = 1; + + if( outputUnderflow && !done && !stream->stopProcessing ) + { + /* Recover from underflow in the case where the + underflow occured while processing the buffer + we just finished */ + + result = CatchUpOutputBuffers( stream ); + if( result != paNoError ) + done = 1; + + statusFlags |= paOutputUnderflow; + } + } + } + + if( stream->throttleProcessingThreadOnOverload != 0 ) + { + if( stream->stopProcessing || stream->abortProcessing ) + { + if( stream->processingThreadPriority != stream->highThreadPriority ) + { + SetThreadPriority( stream->processingThread, stream->highThreadPriority ); + stream->processingThreadPriority = stream->highThreadPriority; + } + } + else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. ) + { + if( stream->processingThreadPriority != stream->throttledThreadPriority ) + { + SetThreadPriority( stream->processingThread, stream->throttledThreadPriority ); + stream->processingThreadPriority = stream->throttledThreadPriority; + } + + /* sleep to give other processes a go */ + Sleep( stream->throttledSleepMsecs ); + } + else + { + if( stream->processingThreadPriority != stream->highThreadPriority ) + { + SetThreadPriority( stream->processingThread, stream->highThreadPriority ); + stream->processingThreadPriority = stream->highThreadPriority; + } + } + } + } + else + { + hostBuffersAvailable = 0; + } + } + while( hostBuffersAvailable && + stream->stopProcessing == 0 && + stream->abortProcessing == 0 && + !done ); + } + } + while( !done ); + + stream->isActive = 0; + + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + + PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer ); + + return result; +} + + +/* + When CloseStream() is called, the multi-api layer ensures that + the stream has already been stopped or aborted. +*/ +static PaError CloseStream( PaStream* s ) +{ + PaError result; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + result = CloseHandleWithPaError( stream->abortEvent ); + if( result != paNoError ) goto error; + + TerminateWaveHeaders( &stream->output, 0 /* not isInput */ ); + TerminateWaveHeaders( &stream->input, 1 /* isInput */ ); + + TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ ); + TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ ); + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + +error: + /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */ + return result; +} + + +static PaError StartStream( PaStream *s ) +{ + PaError result; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + MMRESULT mmresult; + unsigned int i, j; + int callbackResult; + unsigned int channel; + unsigned long framesProcessed; + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */ + + PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; iinput.bufferCount; ++i ) + { + for( j=0; jinput.deviceCount; ++j ) + { + mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + goto error; + } + } + } + stream->input.currentBufferIndex = 0; + stream->input.framesUsedInCurrentBuffer = 0; + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i=0; ioutput.deviceCount; ++i ) + { + if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + goto error; + } + } + + for( i=0; ioutput.bufferCount; ++i ) + { + if( stream->primeStreamUsingCallback ) + { + + stream->output.framesUsedInCurrentBuffer = 0; + do{ + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, + &timeInfo, + paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0)); + + if( stream->input.bufferCount > 0 ) + PaUtil_SetNoInput( &stream->bufferProcessor ); + + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + + channel = 0; + for( j=0; joutput.deviceCount; ++j ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->output.waveHeaders[j][i].dwUser; + + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, + stream->output.waveHeaders[j][i].lpData + + stream->output.framesUsedInCurrentBuffer * channelCount * + stream->bufferProcessor.bytesPerHostOutputSample, + channelCount ); + + /* we have stored the number of channels in the buffer in dwUser */ + channel += channelCount; + } + + callbackResult = paContinue; + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + stream->output.framesUsedInCurrentBuffer += framesProcessed; + + if( callbackResult != paContinue ) + { + /** @todo fix this, what do we do if callback result is non-zero during stream + priming? + + for complete: play out primed waveHeaders as usual + for abort: clean up immediately. + */ + } + + }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer ); + + } + else + { + for( j=0; joutput.deviceCount; ++j ) + { + ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength ); + } + } + + /* we queue all channels of a single buffer frame (accross all + devices, because some multidevice multichannel drivers work + better this way */ + for( j=0; joutput.deviceCount; ++j ) + { + mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + goto error; + } + } + } + stream->output.currentBufferIndex = 0; + stream->output.framesUsedInCurrentBuffer = 0; + } + + + stream->isStopped = 0; + stream->isActive = 1; + stream->stopProcessing = 0; + stream->abortProcessing = 0; + + result = ResetEventWithPaError( stream->input.bufferEvent ); + if( result != paNoError ) goto error; + + result = ResetEventWithPaError( stream->output.bufferEvent ); + if( result != paNoError ) goto error; + + + if( stream->streamRepresentation.streamCallback ) + { + /* callback stream */ + + result = ResetEventWithPaError( stream->abortEvent ); + if( result != paNoError ) goto error; + + /* Create thread that waits for audio buffers to be ready for processing. */ + stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ); + if( !stream->processingThread ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + + /** @todo could have mme specific stream parameters to allow the user + to set the callback thread priorities */ + stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL; + stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL; + + if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + stream->processingThreadPriority = stream->highThreadPriority; + } + else + { + /* blocking read/write stream */ + + } + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; i < stream->input.deviceCount; ++i ) + { + mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] ); + PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult)); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + goto error; + } + } + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i=0; i < stream->output.deviceCount; ++i ) + { + if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + goto error; + } + } + } + + return result; + +error: + /** @todo FIXME: implement recovery as best we can + This should involve rolling back to a state as-if this function had never been called + */ + return result; +} + + +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + int timeout; + DWORD waitResult; + MMRESULT mmresult; + signed int hostOutputBufferIndex; + unsigned int channel, waitCount, i; + + /** @todo + REVIEW: the error checking in this function needs review. the basic + idea is to return from this function in a known state - for example + there is no point avoiding calling waveInReset just because + the thread times out. + */ + + if( stream->processingThread ) + { + /* callback stream */ + + /* Tell processing thread to stop generating more data and to let current data play out. */ + stream->stopProcessing = 1; + + /* Calculate timeOut longer than longest time it could take to return all buffers. */ + timeout = (int)(stream->allBuffersDurationMs * 1.5); + if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) + timeout = PA_MME_MIN_TIMEOUT_MSEC_; + + PA_DEBUG(("WinMME StopStream: waiting for background thread.\n")); + + waitResult = WaitForSingleObject( stream->processingThread, timeout ); + if( waitResult == WAIT_TIMEOUT ) + { + /* try to abort */ + stream->abortProcessing = 1; + SetEvent( stream->abortEvent ); + waitResult = WaitForSingleObject( stream->processingThread, timeout ); + if( waitResult == WAIT_TIMEOUT ) + { + PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n")); + result = paTimedOut; + } + } + + CloseHandle( stream->processingThread ); + stream->processingThread = NULL; + } + else + { + /* blocking read / write stream */ + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + if( stream->output.framesUsedInCurrentBuffer > 0 ) + { + /* there are still unqueued frames in the current buffer, so flush them */ + + hostOutputBufferIndex = stream->output.currentBufferIndex; + + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, + stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); + + channel = 0; + for( i=0; ioutput.deviceCount; ++i ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; + + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, + stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + + stream->output.framesUsedInCurrentBuffer * channelCount * + stream->bufferProcessor.bytesPerHostOutputSample, + channelCount ); + + channel += channelCount; + } + + PaUtil_ZeroOutput( &stream->bufferProcessor, + stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); + + /* we send the entire buffer to the output devices, but we could + just send a partial buffer, rather than zeroing the unused + samples. + */ + AdvanceToNextOutputBuffer( stream ); + } + + + timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1; + if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) + timeout = PA_MME_MIN_TIMEOUT_MSEC_; + + waitCount = 0; + while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount ) + { + /* wait for MME to signal that a buffer is available */ + waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout ); + if( waitResult == WAIT_FAILED ) + { + break; + } + else if( waitResult == WAIT_TIMEOUT ) + { + /* keep waiting */ + } + + ++waitCount; + } + } + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i =0; i < stream->output.deviceCount; ++i ) + { + mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + } + } + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; i < stream->input.deviceCount; ++i ) + { + mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + } + } + + stream->isStopped = 1; + stream->isActive = 0; + + return result; +} + + +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + int timeout; + DWORD waitResult; + MMRESULT mmresult; + unsigned int i; + + /** @todo + REVIEW: the error checking in this function needs review. the basic + idea is to return from this function in a known state - for example + there is no point avoiding calling waveInReset just because + the thread times out. + */ + + if( stream->processingThread ) + { + /* callback stream */ + + /* Tell processing thread to abort immediately */ + stream->abortProcessing = 1; + SetEvent( stream->abortEvent ); + } + + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i =0; i < stream->output.deviceCount; ++i ) + { + mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + return paUnanticipatedHostError; + } + } + } + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; i < stream->input.deviceCount; ++i ) + { + mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + return paUnanticipatedHostError; + } + } + } + + + if( stream->processingThread ) + { + /* callback stream */ + + PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n")); + + /* Calculate timeOut longer than longest time it could take to return all buffers. */ + timeout = (int)(stream->allBuffersDurationMs * 1.5); + if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ ) + timeout = PA_MME_MIN_TIMEOUT_MSEC_; + + waitResult = WaitForSingleObject( stream->processingThread, timeout ); + if( waitResult == WAIT_TIMEOUT ) + { + PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n")); + return paTimedOut; + } + + CloseHandle( stream->processingThread ); + stream->processingThread = NULL; + } + + stream->isStopped = 1; + stream->isActive = 0; + + return result; +} + + +static PaError IsStreamStopped( PaStream *s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + return stream->isStopped; +} + + +static PaError IsStreamActive( PaStream *s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + return stream->isActive; +} + + +static PaTime GetStreamTime( PaStream *s ) +{ + (void) s; /* unused parameter */ + + return PaUtil_GetTime(); +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + + +/* + As separate stream interfaces are used for blocking and callback + streams, the following functions can be guaranteed to only be called + for blocking streams. +*/ + +static PaError ReadStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaError result = paNoError; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + void *userBuffer; + unsigned long framesRead = 0; + unsigned long framesProcessed; + signed int hostInputBufferIndex; + DWORD waitResult; + DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); + unsigned int channel, i; + + if( PA_IS_INPUT_STREAM_(stream) ) + { + /* make a local copy of the user buffer pointer(s). this is necessary + because PaUtil_CopyInput() advances these pointers every time + it is called. + */ + if( stream->bufferProcessor.userInputIsInterleaved ) + { + userBuffer = buffer; + } + else + { + userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount ); + if( !userBuffer ) + return paInsufficientMemory; + for( i = 0; ibufferProcessor.inputChannelCount; ++i ) + ((void**)userBuffer)[i] = ((void**)buffer)[i]; + } + + do{ + if( CurrentInputBuffersAreDone( stream ) ) + { + if( NoBuffersAreQueued( &stream->input ) ) + { + /** @todo REVIEW: consider what to do if the input overflows. + do we requeue all of the buffers? should we be running + a thread to make sure they are always queued? */ + + result = paInputOverflowed; + } + + hostInputBufferIndex = stream->input.currentBufferIndex; + + PaUtil_SetInputFrameCount( &stream->bufferProcessor, + stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer ); + + channel = 0; + for( i=0; iinput.deviceCount; ++i ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; + + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, + stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + + stream->input.framesUsedInCurrentBuffer * channelCount * + stream->bufferProcessor.bytesPerHostInputSample, + channelCount ); + + channel += channelCount; + } + + framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead ); + + stream->input.framesUsedInCurrentBuffer += framesProcessed; + if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer ) + { + result = AdvanceToNextInputBuffer( stream ); + if( result != paNoError ) + break; + } + + framesRead += framesProcessed; + + }else{ + /* wait for MME to signal that a buffer is available */ + waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout ); + if( waitResult == WAIT_FAILED ) + { + result = paUnanticipatedHostError; + break; + } + else if( waitResult == WAIT_TIMEOUT ) + { + /* if a timeout is encountered, continue, + perhaps we should give up eventually + */ + } + } + }while( framesRead < frames ); + } + else + { + result = paCanNotReadFromAnOutputOnlyStream; + } + + return result; +} + + +static PaError WriteStream( PaStream* s, + const void *buffer, + unsigned long frames ) +{ + PaError result = paNoError; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + const void *userBuffer; + unsigned long framesWritten = 0; + unsigned long framesProcessed; + signed int hostOutputBufferIndex; + DWORD waitResult; + DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); + unsigned int channel, i; + + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + /* make a local copy of the user buffer pointer(s). this is necessary + because PaUtil_CopyOutput() advances these pointers every time + it is called. + */ + if( stream->bufferProcessor.userOutputIsInterleaved ) + { + userBuffer = buffer; + } + else + { + userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount ); + if( !userBuffer ) + return paInsufficientMemory; + for( i = 0; ibufferProcessor.outputChannelCount; ++i ) + ((const void**)userBuffer)[i] = ((const void**)buffer)[i]; + } + + do{ + if( CurrentOutputBuffersAreDone( stream ) ) + { + if( NoBuffersAreQueued( &stream->output ) ) + { + /** @todo REVIEW: consider what to do if the output + underflows. do we requeue all the existing buffers with + zeros? should we run a separate thread to keep the buffers + enqueued at all times? */ + + result = paOutputUnderflowed; + } + + hostOutputBufferIndex = stream->output.currentBufferIndex; + + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, + stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer ); + + channel = 0; + for( i=0; ioutput.deviceCount; ++i ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; + + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, + stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + + stream->output.framesUsedInCurrentBuffer * channelCount * + stream->bufferProcessor.bytesPerHostOutputSample, + channelCount ); + + channel += channelCount; + } + + framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten ); + + stream->output.framesUsedInCurrentBuffer += framesProcessed; + if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer ) + { + result = AdvanceToNextOutputBuffer( stream ); + if( result != paNoError ) + break; + } + + framesWritten += framesProcessed; + } + else + { + /* wait for MME to signal that a buffer is available */ + waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout ); + if( waitResult == WAIT_FAILED ) + { + result = paUnanticipatedHostError; + break; + } + else if( waitResult == WAIT_TIMEOUT ) + { + /* if a timeout is encountered, continue, + perhaps we should give up eventually + */ + } + } + }while( framesWritten < frames ); + } + else + { + result = paCanNotWriteToAnInputOnlyStream; + } + + return result; +} + + +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + if( PA_IS_INPUT_STREAM_(stream) ) + return GetAvailableFrames( &stream->input ); + else + return paCanNotReadFromAnOutputOnlyStream; +} + + +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + if( PA_IS_OUTPUT_STREAM_(stream) ) + return GetAvailableFrames( &stream->output ); + else + return paCanNotWriteToAnInputOnlyStream; +} + + +/* NOTE: the following functions are MME-stream specific, and are called directly + by client code. We need to check for many more error conditions here because + we don't have the benefit of pa_front.c's parameter checking. +*/ + +static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s ) +{ + PaError result; + PaUtilHostApiRepresentation *hostApi; + PaWinMmeHostApiRepresentation *winMmeHostApi; + + result = PaUtil_ValidateStreamPointer( s ); + if( result != paNoError ) + return result; + + result = PaUtil_GetHostApiRepresentation( &hostApi, paMME ); + if( result != paNoError ) + return result; + + winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; + + /* note, the following would be easier if there was a generic way of testing + that a stream belongs to a specific host API */ + + if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface + || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface ) + { + /* s is a WinMME stream */ + *stream = (PaWinMmeStream *)s; + return paNoError; + } + else + { + return paIncompatibleStreamHostApi; + } +} + + +int PaWinMME_GetStreamInputHandleCount( PaStream* s ) +{ + PaWinMmeStream *stream; + PaError result = GetWinMMEStreamPointer( &stream, s ); + + if( result == paNoError ) + return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0; + else + return result; +} + + +HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex ) +{ + PaWinMmeStream *stream; + PaError result = GetWinMMEStreamPointer( &stream, s ); + + if( result == paNoError + && PA_IS_INPUT_STREAM_(stream) + && handleIndex >= 0 + && (unsigned int)handleIndex < stream->input.deviceCount ) + return ((HWAVEIN*)stream->input.waveHandles)[handleIndex]; + else + return 0; +} + + +int PaWinMME_GetStreamOutputHandleCount( PaStream* s) +{ + PaWinMmeStream *stream; + PaError result = GetWinMMEStreamPointer( &stream, s ); + + if( result == paNoError ) + return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0; + else + return result; +} + + +HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex ) +{ + PaWinMmeStream *stream; + PaError result = GetWinMMEStreamPointer( &stream, s ); + + if( result == paNoError + && PA_IS_OUTPUT_STREAM_(stream) + && handleIndex >= 0 + && (unsigned int)handleIndex < stream->output.deviceCount ) + return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex]; + else + return 0; +} + + + + + diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h index 43469132b..1a71633ad 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h +++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h @@ -1,160 +1,160 @@ -#ifndef PA_WIN_WMME_H -#define PA_WIN_WMME_H -/* - * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $ - * PortAudio Portable Real-Time Audio Library - * MME specific extensions - * - * Copyright (c) 1999-2000 Ross Bencina and Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - * - */ - -/** @file - @brief WMME-specific PortAudio API extension header file. -*/ - - -#include "portaudio.h" - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -#define paWinMmeUseLowLevelLatencyParameters (0x01) -#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */ - - -/* By default, the mme implementation drops the processing thread's priority - to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100% - This flag disables any priority throttling. The processing thread will always - run at THREAD_PRIORITY_TIME_CRITICAL. -*/ -#define paWinMmeDontThrottleOverloadedProcessingThread (0x08) - - -typedef struct PaWinMmeDeviceAndChannelCount{ - PaDeviceIndex device; - int channelCount; -}PaWinMmeDeviceAndChannelCount; - - -typedef struct PaWinMmeStreamInfo{ - unsigned long size; /**< sizeof(PaWinMmeStreamInfo) */ - PaHostApiTypeId hostApiType; /**< paMME */ - unsigned long version; /**< 1 */ - - unsigned long flags; - - /* low-level latency setting support - These settings control the number and size of host buffers in order - to set latency. They will be used instead of the generic parameters - to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters - flag. - - If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters - are supplied for both input and output in a full duplex stream, then the - input and output framesPerBuffer must be the same, or the larger of the - two must be a multiple of the smaller, otherwise a - paIncompatibleHostApiSpecificStreamInfo error will be returned from - Pa_OpenStream(). - */ - unsigned long framesPerBuffer; - unsigned long bufferCount; /* formerly numBuffers */ - - /* multiple devices per direction support - If flags contains the PaWinMmeUseMultipleDevices flag, - this functionality will be used, otherwise the device parameter to - Pa_OpenStream() will be used instead. - If devices are specified here, the corresponding device parameter - to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification, - otherwise an paInvalidDevice error will result. - The total number of channels accross all specified devices - must agree with the corresponding channelCount parameter to - Pa_OpenStream() otherwise a paInvalidChannelCount error will result. - */ - PaWinMmeDeviceAndChannelCount *devices; - unsigned long deviceCount; - -}PaWinMmeStreamInfo; - - -/** Retrieve the number of wave in handles used by a PortAudio WinMME stream. - Returns zero if the stream is output only. - - @return A non-negative value indicating the number of wave in handles - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - @see PaWinMME_GetStreamInputHandle -*/ -int PaWinMME_GetStreamInputHandleCount( PaStream* stream ); - - -/** Retrieve a wave in handle used by a PortAudio WinMME stream. - - @param stream The stream to query. - @param handleIndex The zero based index of the wave in handle to retrieve. This - should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1]. - - @return A valid wave in handle, or NULL if an error occurred. - - @see PaWinMME_GetStreamInputHandle -*/ -HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex ); - - -/** Retrieve the number of wave out handles used by a PortAudio WinMME stream. - Returns zero if the stream is input only. - - @return A non-negative value indicating the number of wave out handles - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - @see PaWinMME_GetStreamOutputHandle -*/ -int PaWinMME_GetStreamOutputHandleCount( PaStream* stream ); - - -/** Retrieve a wave out handle used by a PortAudio WinMME stream. - - @param stream The stream to query. - @param handleIndex The zero based index of the wave out handle to retrieve. - This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1]. - - @return A valid wave out handle, or NULL if an error occurred. - - @see PaWinMME_GetStreamOutputHandleCount -*/ -HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* PA_WIN_WMME_H */ +#ifndef PA_WIN_WMME_H +#define PA_WIN_WMME_H +/* + * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $ + * PortAudio Portable Real-Time Audio Library + * MME specific extensions + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + * + */ + +/** @file + @brief WMME-specific PortAudio API extension header file. +*/ + + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +#define paWinMmeUseLowLevelLatencyParameters (0x01) +#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */ + + +/* By default, the mme implementation drops the processing thread's priority + to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100% + This flag disables any priority throttling. The processing thread will always + run at THREAD_PRIORITY_TIME_CRITICAL. +*/ +#define paWinMmeDontThrottleOverloadedProcessingThread (0x08) + + +typedef struct PaWinMmeDeviceAndChannelCount{ + PaDeviceIndex device; + int channelCount; +}PaWinMmeDeviceAndChannelCount; + + +typedef struct PaWinMmeStreamInfo{ + unsigned long size; /**< sizeof(PaWinMmeStreamInfo) */ + PaHostApiTypeId hostApiType; /**< paMME */ + unsigned long version; /**< 1 */ + + unsigned long flags; + + /* low-level latency setting support + These settings control the number and size of host buffers in order + to set latency. They will be used instead of the generic parameters + to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters + flag. + + If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters + are supplied for both input and output in a full duplex stream, then the + input and output framesPerBuffer must be the same, or the larger of the + two must be a multiple of the smaller, otherwise a + paIncompatibleHostApiSpecificStreamInfo error will be returned from + Pa_OpenStream(). + */ + unsigned long framesPerBuffer; + unsigned long bufferCount; /* formerly numBuffers */ + + /* multiple devices per direction support + If flags contains the PaWinMmeUseMultipleDevices flag, + this functionality will be used, otherwise the device parameter to + Pa_OpenStream() will be used instead. + If devices are specified here, the corresponding device parameter + to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification, + otherwise an paInvalidDevice error will result. + The total number of channels accross all specified devices + must agree with the corresponding channelCount parameter to + Pa_OpenStream() otherwise a paInvalidChannelCount error will result. + */ + PaWinMmeDeviceAndChannelCount *devices; + unsigned long deviceCount; + +}PaWinMmeStreamInfo; + + +/** Retrieve the number of wave in handles used by a PortAudio WinMME stream. + Returns zero if the stream is output only. + + @return A non-negative value indicating the number of wave in handles + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + @see PaWinMME_GetStreamInputHandle +*/ +int PaWinMME_GetStreamInputHandleCount( PaStream* stream ); + + +/** Retrieve a wave in handle used by a PortAudio WinMME stream. + + @param stream The stream to query. + @param handleIndex The zero based index of the wave in handle to retrieve. This + should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1]. + + @return A valid wave in handle, or NULL if an error occurred. + + @see PaWinMME_GetStreamInputHandle +*/ +HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex ); + + +/** Retrieve the number of wave out handles used by a PortAudio WinMME stream. + Returns zero if the stream is input only. + + @return A non-negative value indicating the number of wave out handles + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + @see PaWinMME_GetStreamOutputHandle +*/ +int PaWinMME_GetStreamOutputHandleCount( PaStream* stream ); + + +/** Retrieve a wave out handle used by a PortAudio WinMME stream. + + @param stream The stream to query. + @param handleIndex The zero based index of the wave out handle to retrieve. + This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1]. + + @return A valid wave out handle, or NULL if an error occurred. + + @see PaWinMME_GetStreamOutputHandleCount +*/ +HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_WIN_WMME_H */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c index 07b3c2ec7..3a33540bc 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c +++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c @@ -1,1167 +1,1167 @@ -#include "pa_x86_plain_converters.h" - -#include "pa_converters.h" -#include "pa_dither.h" - -/* - plain intel assemby versions of standard pa converter functions. - - the main reason these versions are faster than the equivalent C versions - is that float -> int casting is expensive in C on x86 because the rounding - mode needs to be changed for every cast. these versions only set - the rounding mode once outside the loop. - - small additional speed gains are made by the way that clamping is - implemented. - -TODO: - o- inline dither code - o- implement Dither only (no-clip) versions - o- implement int8 and uint8 versions - o- test thouroughly - - o- the packed 24 bit functions could benefit from unrolling and avoiding - byte and word sized register access. -*/ - -/* -------------------------------------------------------------------------- */ - -/* -#define PA_CLIP_( val, min, max )\ - { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } -*/ - -/* - the following notes were used to determine whether a floating point - value should be saturated (ie >1 or <-1) by loading it into an integer - register. these should be rewritten so that they make sense. - - an ieee floating point value - - 1.xxxxxxxxxxxxxxxxxxxx? - - - is less than or equal to 1 and greater than or equal to -1 either: - - if the mantissa is 0 and the unbiased exponent is 0 - - OR - - if the unbiased exponent < 0 - - this translates to: - - if the mantissa is 0 and the biased exponent is 7F - - or - - if the biased exponent is less than 7F - - - therefore the value is greater than 1 or less than -1 if - - the mantissa is not 0 and the biased exponent is 7F - - or - - if the biased exponent is greater than 7F - - - in other words, if we mask out the sign bit, the value is - greater than 1 or less than -1 if its integer representation is greater than: - - 0 01111111 0000 0000 0000 0000 0000 000 - - 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000 -*/ - -/* -------------------------------------------------------------------------- */ - -static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/ -static const double int32Scaler_ = 0x7FFFFFFF; -static const double ditheredInt32Scaler_ = 0x7FFFFFFE; -static const double int24Scaler_ = 0x7FFFFF; -static const double ditheredInt24Scaler_ = 0x7FFFFE; -static const double int16Scaler_ = 0x7FFF; -static const double ditheredInt16Scaler_ = 0x7FFE; - -#define PA_DITHER_BITS_ (15) -/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ -#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1< source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 and int32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int32Scaler_ // stack: (int)0x7FFFFFFF - - Float32_To_Int32_loop: - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF - /* - note: we could store to a temporary qword here which would cause - wraparound distortion instead of int indefinite 0x10. that would - be more work, and given that not enabling clipping is only advisable - when you know that your signal isn't going to clip it isn't worth it. - */ - fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int32_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - (void) ditherGenerator; // unused parameter - - while( count-- ) - { - // REVIEW - double scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648., 2147483647. ); - *dest = (signed long) scaled; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 and int32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int32Scaler_ // stack: (int)0x7FFFFFFF - - Float32_To_Int32_Clip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int32_Clip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF - fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF - jmp Float32_To_Int32_Clip_stored - - Float32_To_Int32_Clip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFFFF // convert to maximum range integers - mov dword ptr [edi], edx - - Float32_To_Int32_Clip_stored: - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int32_Clip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int32_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ - /* - float *src = (float*)sourceBuffer; - signed long *dest = (signed long*)destinationBuffer; - - while( count-- ) - { - // REVIEW - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - // use smaller scaler to prevent overflow when we add the dither - double dithered = ((double)*src * (2147483646.0)) + dither; - PA_CLIP_( dithered, -2147483648., 2147483647. ); - *dest = (signed long) dithered; - - - src += sourceStride; - dest += destinationStride; - } - */ - - short savedFpuControlWord; - - // spill storage: - signed long sourceByteStride; - signed long highpassedDither; - - // dither state: - unsigned long ditherPrevious = ditherGenerator->previous; - unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; - unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 and int32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld ditheredInt32Scaler_ // stack: int scaler - - Float32_To_Int32_DitherClip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int32_DitherClip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, int scaler - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler - - /* - // call PaUtil_GenerateFloatTriangularDither with C calling convention - mov sourceByteStride, eax // save eax - mov sourceEnd, ecx // save ecx - push ditherGenerator // pass ditherGenerator parameter on stack - call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler - pop edx // clear parameter off stack - mov ecx, sourceEnd // restore ecx - mov eax, sourceByteStride // restore eax - */ - - // generate dither - mov sourceByteStride, eax // save eax - mov edx, 196314165 - mov eax, ditherRandSeed1 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov ditherRandSeed1, eax - mov edx, 196314165 - mov eax, ditherRandSeed2 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov edx, ditherRandSeed1 - shr edx, PA_DITHER_SHIFT_ - mov ditherRandSeed2, eax - shr eax, PA_DITHER_SHIFT_ - //add eax, edx // eax -> current - lea eax, [eax+edx] - mov edx, ditherPrevious - neg edx - lea edx, [eax+edx] // highpass = current - previous - mov highpassedDither, edx - mov ditherPrevious, eax // previous = current - mov eax, sourceByteStride // restore eax - fild highpassedDither - fmul const_float_dither_scale_ - // end generate dither, dither signal in st(0) - - faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler - fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler - jmp Float32_To_Int32_DitherClip_stored - - Float32_To_Int32_DitherClip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFFFF // convert to maximum range integers - mov dword ptr [edi], edx - - Float32_To_Int32_DitherClip_stored: - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int32_DitherClip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } - - ditherGenerator->previous = ditherPrevious; - ditherGenerator->randSeed1 = ditherRandSeed1; - ditherGenerator->randSeed2 = ditherRandSeed2; -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - (void) ditherGenerator; // unused parameter - - while( count-- ) - { - // convert to 32 bit and drop the low 8 bits - double scaled = *src * 0x7FFFFFFF; - temp = (signed long) scaled; - - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); - - src += sourceStride; - dest += destinationStride * 3; - } -*/ - - short savedFpuControlWord; - - signed long tempInt32; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov edx, 3 // sizeof int24 - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int24Scaler_ // stack: (int)0x7FFFFF - - Float32_To_Int24_loop: - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF - fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF - mov edx, tempInt32 - - mov byte ptr [edi], DL - shr edx, 8 - //mov byte ptr [edi+1], DL - //mov byte ptr [edi+2], DH - mov word ptr [edi+1], DX - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int24_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - (void) ditherGenerator; // unused parameter - - while( count-- ) - { - // convert to 32 bit and drop the low 8 bits - double scaled = *src * 0x7FFFFFFF; - PA_CLIP_( scaled, -2147483648., 2147483647. ); - temp = (signed long) scaled; - - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); - - src += sourceStride; - dest += destinationStride * 3; - } -*/ - - short savedFpuControlWord; - - signed long tempInt32; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov edx, 3 // sizeof int24 - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int24Scaler_ // stack: (int)0x7FFFFF - - Float32_To_Int24_Clip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int24_Clip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFFFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF - fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF - mov edx, tempInt32 - jmp Float32_To_Int24_Clip_store - - Float32_To_Int24_Clip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFF // convert to maximum range integers - - Float32_To_Int24_Clip_store: - - mov byte ptr [edi], DL - shr edx, 8 - //mov byte ptr [edi+1], DL - //mov byte ptr [edi+2], DH - mov word ptr [edi+1], DX - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int24_Clip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int24_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - unsigned char *dest = (unsigned char*)destinationBuffer; - signed long temp; - - while( count-- ) - { - // convert to 32 bit and drop the low 8 bits - - // FIXME: the dither amplitude here appears to be too small by 8 bits - double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - // use smaller scaler to prevent overflow when we add the dither - double dithered = ((double)*src * (2147483646.0)) + dither; - PA_CLIP_( dithered, -2147483648., 2147483647. ); - - temp = (signed long) dithered; - - dest[0] = (unsigned char)(temp >> 8); - dest[1] = (unsigned char)(temp >> 16); - dest[2] = (unsigned char)(temp >> 24); - - src += sourceStride; - dest += destinationStride * 3; - } -*/ - - short savedFpuControlWord; - - // spill storage: - signed long sourceByteStride; - signed long highpassedDither; - - // dither state: - unsigned long ditherPrevious = ditherGenerator->previous; - unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; - unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; - - signed long tempInt32; - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx - - mov ecx, count - imul ecx, eax - add ecx, esi - - mov edi, destinationBuffer - - mov edx, 3 // sizeof int24 - mov ebx, destinationStride - imul ebx, edx - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld ditheredInt24Scaler_ // stack: int scaler - - Float32_To_Int24_DitherClip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int24_DitherClip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, int scaler - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler - - /* - // call PaUtil_GenerateFloatTriangularDither with C calling convention - mov sourceByteStride, eax // save eax - mov sourceEnd, ecx // save ecx - push ditherGenerator // pass ditherGenerator parameter on stack - call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler - pop edx // clear parameter off stack - mov ecx, sourceEnd // restore ecx - mov eax, sourceByteStride // restore eax - */ - - // generate dither - mov sourceByteStride, eax // save eax - mov edx, 196314165 - mov eax, ditherRandSeed1 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov ditherRandSeed1, eax - mov edx, 196314165 - mov eax, ditherRandSeed2 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov edx, ditherRandSeed1 - shr edx, PA_DITHER_SHIFT_ - mov ditherRandSeed2, eax - shr eax, PA_DITHER_SHIFT_ - //add eax, edx // eax -> current - lea eax, [eax+edx] - mov edx, ditherPrevious - neg edx - lea edx, [eax+edx] // highpass = current - previous - mov highpassedDither, edx - mov ditherPrevious, eax // previous = current - mov eax, sourceByteStride // restore eax - fild highpassedDither - fmul const_float_dither_scale_ - // end generate dither, dither signal in st(0) - - faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler - fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler - mov edx, tempInt32 - jmp Float32_To_Int24_DitherClip_store - - Float32_To_Int24_DitherClip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add edx, 0x7FFFFF // convert to maximum range integers - - Float32_To_Int24_DitherClip_store: - - mov byte ptr [edi], DL - shr edx, 8 - //mov byte ptr [edi+1], DL - //mov byte ptr [edi+2], DH - mov word ptr [edi+1], DX - - //add edi, ebx // increment destination ptr - lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int24_DitherClip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } - - ditherGenerator->previous = ditherPrevious; - ditherGenerator->randSeed1 = ditherRandSeed1; - ditherGenerator->randSeed2 = ditherRandSeed2; -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; // unused parameter - - while( count-- ) - { - - short samp = (short) (*src * (32767.0f)); - *dest = samp; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx // source byte stride - - mov ecx, count - imul ecx, eax - add ecx, esi // source end ptr = count * source byte stride + source ptr - - mov edi, destinationBuffer - - mov edx, 2 // sizeof int16 - mov ebx, destinationStride - imul ebx, edx // destination byte stride - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int16Scaler_ // stack: (int)0x7FFF - - Float32_To_Int16_loop: - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF - fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int16_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_Clip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; // unused parameter - - while( count-- ) - { - long samp = (signed long) (*src * (32767.0f)); - PA_CLIP_( samp, -0x8000, 0x7FFF ); - *dest = (signed short) samp; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - (void) ditherGenerator; /* unused parameter */ - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx // source byte stride - - mov ecx, count - imul ecx, eax - add ecx, esi // source end ptr = count * source byte stride + source ptr - - mov edi, destinationBuffer - - mov edx, 2 // sizeof int16 - mov ebx, destinationStride - imul ebx, edx // destination byte stride - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld int16Scaler_ // stack: (int)0x7FFF - - Float32_To_Int16_Clip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int16_Clip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, (int)0x7FFF - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF - fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF - jmp Float32_To_Int16_Clip_stored - - Float32_To_Int16_Clip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add dx, 0x7FFF // convert to maximum range integers - mov word ptr [edi], dx // store clamped into into dest - - Float32_To_Int16_Clip_stored: - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int16_Clip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } -} - -/* -------------------------------------------------------------------------- */ - -static void Float32_To_Int16_DitherClip( - void *destinationBuffer, signed int destinationStride, - void *sourceBuffer, signed int sourceStride, - unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) -{ -/* - float *src = (float*)sourceBuffer; - signed short *dest = (signed short*)destinationBuffer; - (void)ditherGenerator; // unused parameter - - while( count-- ) - { - - float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); - // use smaller scaler to prevent overflow when we add the dither - float dithered = (*src * (32766.0f)) + dither; - signed long samp = (signed long) dithered; - PA_CLIP_( samp, -0x8000, 0x7FFF ); - *dest = (signed short) samp; - - src += sourceStride; - dest += destinationStride; - } -*/ - - short savedFpuControlWord; - - // spill storage: - signed long sourceByteStride; - signed long highpassedDither; - - // dither state: - unsigned long ditherPrevious = ditherGenerator->previous; - unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; - unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; - - __asm{ - // esi -> source ptr - // eax -> source byte stride - // edi -> destination ptr - // ebx -> destination byte stride - // ecx -> source end ptr - // edx -> temp - - mov esi, sourceBuffer - - mov edx, 4 // sizeof float32 - mov eax, sourceStride - imul eax, edx // source byte stride - - mov ecx, count - imul ecx, eax - add ecx, esi // source end ptr = count * source byte stride + source ptr - - mov edi, destinationBuffer - - mov edx, 2 // sizeof int16 - mov ebx, destinationStride - imul ebx, edx // destination byte stride - - fwait - fstcw savedFpuControlWord - fldcw fpuControlWord_ - - fld ditheredInt16Scaler_ // stack: int scaler - - Float32_To_Int16_DitherClip_loop: - - mov edx, dword ptr [esi] // load floating point value into integer register - - and edx, 0x7FFFFFFF // mask off sign - cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 - - jg Float32_To_Int16_DitherClip_clamp - - // load unscaled value into st(0) - fld dword ptr [esi] // stack: value, int scaler - add esi, eax // increment source ptr - //lea esi, [esi+eax] - fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler - - /* - // call PaUtil_GenerateFloatTriangularDither with C calling convention - mov sourceByteStride, eax // save eax - mov sourceEnd, ecx // save ecx - push ditherGenerator // pass ditherGenerator parameter on stack - call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler - pop edx // clear parameter off stack - mov ecx, sourceEnd // restore ecx - mov eax, sourceByteStride // restore eax - */ - - // generate dither - mov sourceByteStride, eax // save eax - mov edx, 196314165 - mov eax, ditherRandSeed1 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov ditherRandSeed1, eax - mov edx, 196314165 - mov eax, ditherRandSeed2 - mul edx // eax:edx = eax * 196314165 - //add eax, 907633515 - lea eax, [eax+907633515] - mov edx, ditherRandSeed1 - shr edx, PA_DITHER_SHIFT_ - mov ditherRandSeed2, eax - shr eax, PA_DITHER_SHIFT_ - //add eax, edx // eax -> current - lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x - mov edx, ditherPrevious - neg edx - lea edx, [eax+edx] // highpass = current - previous - mov highpassedDither, edx - mov ditherPrevious, eax // previous = current - mov eax, sourceByteStride // restore eax - fild highpassedDither - fmul const_float_dither_scale_ - // end generate dither, dither signal in st(0) - - faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler - fistp word ptr [edi] // store scaled int into dest, stack: int scaler - jmp Float32_To_Int16_DitherClip_stored - - Float32_To_Int16_DitherClip_clamp: - mov edx, dword ptr [esi] // load floating point value into integer register - shr edx, 31 // move sign bit into bit 0 - add esi, eax // increment source ptr - //lea esi, [esi+eax] - add dx, 0x7FFF // convert to maximum range integers - mov word ptr [edi], dx // store clamped into into dest - - Float32_To_Int16_DitherClip_stored: - - add edi, ebx // increment destination ptr - //lea edi, [edi+ebx] - - cmp esi, ecx // has src ptr reached end? - jne Float32_To_Int16_DitherClip_loop - - ffree st(0) - fincstp - - fwait - fnclex - fldcw savedFpuControlWord - } - - ditherGenerator->previous = ditherPrevious; - ditherGenerator->randSeed1 = ditherRandSeed1; - ditherGenerator->randSeed2 = ditherRandSeed2; -} - -/* -------------------------------------------------------------------------- */ - -void PaUtil_InitializeX86PlainConverters( void ) -{ - paConverters.Float32_To_Int32 = Float32_To_Int32; - paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip; - paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip; - - paConverters.Float32_To_Int24 = Float32_To_Int24; - paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip; - paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip; - - paConverters.Float32_To_Int16 = Float32_To_Int16; - paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip; - paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip; -} - -/* -------------------------------------------------------------------------- */ +#include "pa_x86_plain_converters.h" + +#include "pa_converters.h" +#include "pa_dither.h" + +/* + plain intel assemby versions of standard pa converter functions. + + the main reason these versions are faster than the equivalent C versions + is that float -> int casting is expensive in C on x86 because the rounding + mode needs to be changed for every cast. these versions only set + the rounding mode once outside the loop. + + small additional speed gains are made by the way that clamping is + implemented. + +TODO: + o- inline dither code + o- implement Dither only (no-clip) versions + o- implement int8 and uint8 versions + o- test thouroughly + + o- the packed 24 bit functions could benefit from unrolling and avoiding + byte and word sized register access. +*/ + +/* -------------------------------------------------------------------------- */ + +/* +#define PA_CLIP_( val, min, max )\ + { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } +*/ + +/* + the following notes were used to determine whether a floating point + value should be saturated (ie >1 or <-1) by loading it into an integer + register. these should be rewritten so that they make sense. + + an ieee floating point value + + 1.xxxxxxxxxxxxxxxxxxxx? + + + is less than or equal to 1 and greater than or equal to -1 either: + + if the mantissa is 0 and the unbiased exponent is 0 + + OR + + if the unbiased exponent < 0 + + this translates to: + + if the mantissa is 0 and the biased exponent is 7F + + or + + if the biased exponent is less than 7F + + + therefore the value is greater than 1 or less than -1 if + + the mantissa is not 0 and the biased exponent is 7F + + or + + if the biased exponent is greater than 7F + + + in other words, if we mask out the sign bit, the value is + greater than 1 or less than -1 if its integer representation is greater than: + + 0 01111111 0000 0000 0000 0000 0000 000 + + 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000 +*/ + +/* -------------------------------------------------------------------------- */ + +static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/ +static const double int32Scaler_ = 0x7FFFFFFF; +static const double ditheredInt32Scaler_ = 0x7FFFFFFE; +static const double int24Scaler_ = 0x7FFFFF; +static const double ditheredInt24Scaler_ = 0x7FFFFE; +static const double int16Scaler_ = 0x7FFF; +static const double ditheredInt16Scaler_ = 0x7FFE; + +#define PA_DITHER_BITS_ (15) +/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ +#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1< source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int32Scaler_ // stack: (int)0x7FFFFFFF + + Float32_To_Int32_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF + /* + note: we could store to a temporary qword here which would cause + wraparound distortion instead of int indefinite 0x10. that would + be more work, and given that not enabling clipping is only advisable + when you know that your signal isn't going to clip it isn't worth it. + */ + fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void) ditherGenerator; // unused parameter + + while( count-- ) + { + // REVIEW + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + *dest = (signed long) scaled; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int32Scaler_ // stack: (int)0x7FFFFFFF + + Float32_To_Int32_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int32_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF + fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF + jmp Float32_To_Int32_Clip_stored + + Float32_To_Int32_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFFFF // convert to maximum range integers + mov dword ptr [edi], edx + + Float32_To_Int32_Clip_stored: + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + /* + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + + while( count-- ) + { + // REVIEW + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + // use smaller scaler to prevent overflow when we add the dither + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + *dest = (signed long) dithered; + + + src += sourceStride; + dest += destinationStride; + } + */ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt32Scaler_ // stack: int scaler + + Float32_To_Int32_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int32_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler + fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler + jmp Float32_To_Int32_DitherClip_stored + + Float32_To_Int32_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFFFF // convert to maximum range integers + mov dword ptr [edi], edx + + Float32_To_Int32_DitherClip_stored: + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + (void) ditherGenerator; // unused parameter + + while( count-- ) + { + // convert to 32 bit and drop the low 8 bits + double scaled = *src * 0x7FFFFFFF; + temp = (signed long) scaled; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + signed long tempInt32; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int24Scaler_ // stack: (int)0x7FFFFF + + Float32_To_Int24_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF + fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF + mov edx, tempInt32 + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + (void) ditherGenerator; // unused parameter + + while( count-- ) + { + // convert to 32 bit and drop the low 8 bits + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + temp = (signed long) scaled; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + signed long tempInt32; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int24Scaler_ // stack: (int)0x7FFFFF + + Float32_To_Int24_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int24_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF + fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF + mov edx, tempInt32 + jmp Float32_To_Int24_Clip_store + + Float32_To_Int24_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFF // convert to maximum range integers + + Float32_To_Int24_Clip_store: + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + signed long temp; + + while( count-- ) + { + // convert to 32 bit and drop the low 8 bits + + // FIXME: the dither amplitude here appears to be too small by 8 bits + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + // use smaller scaler to prevent overflow when we add the dither + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + + temp = (signed long) dithered; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + signed long tempInt32; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt24Scaler_ // stack: int scaler + + Float32_To_Int24_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int24_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler + fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler + mov edx, tempInt32 + jmp Float32_To_Int24_DitherClip_store + + Float32_To_Int24_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFF // convert to maximum range integers + + Float32_To_Int24_DitherClip_store: + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + + short samp = (short) (*src * (32767.0f)); + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int16Scaler_ // stack: (int)0x7FFF + + Float32_To_Int16_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF + fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + long samp = (signed long) (*src * (32767.0f)); + PA_CLIP_( samp, -0x8000, 0x7FFF ); + *dest = (signed short) samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int16Scaler_ // stack: (int)0x7FFF + + Float32_To_Int16_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int16_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF + fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF + jmp Float32_To_Int16_Clip_stored + + Float32_To_Int16_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add dx, 0x7FFF // convert to maximum range integers + mov word ptr [edi], dx // store clamped into into dest + + Float32_To_Int16_Clip_stored: + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + + float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + // use smaller scaler to prevent overflow when we add the dither + float dithered = (*src * (32766.0f)) + dither; + signed long samp = (signed long) dithered; + PA_CLIP_( samp, -0x8000, 0x7FFF ); + *dest = (signed short) samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt16Scaler_ // stack: int scaler + + Float32_To_Int16_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int16_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler + fistp word ptr [edi] // store scaled int into dest, stack: int scaler + jmp Float32_To_Int16_DitherClip_stored + + Float32_To_Int16_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add dx, 0x7FFF // convert to maximum range integers + mov word ptr [edi], dx // store clamped into into dest + + Float32_To_Int16_DitherClip_stored: + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +void PaUtil_InitializeX86PlainConverters( void ) +{ + paConverters.Float32_To_Int32 = Float32_To_Int32; + paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip; + paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip; + + paConverters.Float32_To_Int24 = Float32_To_Int24; + paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip; + paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip; + + paConverters.Float32_To_Int16 = Float32_To_Int16; + paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip; + paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip; +} + +/* -------------------------------------------------------------------------- */ diff --git a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h index 36959a230..f56c710fd 100644 --- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h +++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h @@ -1,19 +1,19 @@ -#ifndef PA_X86_PLAIN_CONVERTERS_H -#define PA_X86_PLAIN_CONVERTERS_H - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** - @brief Install optimised converter functions suitable for all IA32 processors -*/ -void PaUtil_InitializeX86PlainConverters( void ); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PA_X86_PLAIN_CONVERTERS_H */ +#ifndef PA_X86_PLAIN_CONVERTERS_H +#define PA_X86_PLAIN_CONVERTERS_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** + @brief Install optimised converter functions suitable for all IA32 processors +*/ +void PaUtil_InitializeX86PlainConverters( void ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_X86_PLAIN_CONVERTERS_H */ diff --git a/pjmedia/src/pjmedia/portaudio/portaudio.h b/pjmedia/src/pjmedia/portaudio/portaudio.h index 5fa48b52f..341d92c9c 100644 --- a/pjmedia/src/pjmedia/portaudio/portaudio.h +++ b/pjmedia/src/pjmedia/portaudio/portaudio.h @@ -1,1123 +1,1123 @@ - -#ifndef PORTAUDIO_H -#define PORTAUDIO_H -/* - * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $ - * PortAudio Portable Real-Time Audio Library - * PortAudio API Header File - * Latest version available at: http://www.portaudio.com/ - * - * Copyright (c) 1999-2002 Ross Bencina and Phil Burk - * - * 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. - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. - * - * 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. - */ - -/** @file - @brief The PortAudio API. -*/ - - -#ifdef __cplusplus -extern "C" -{ -#endif /* __cplusplus */ - - -/** Retrieve the release number of the currently running PortAudio build, - eg 1900. -*/ -int Pa_GetVersion( void ); - - -/** Retrieve a textual description of the current PortAudio build, - eg "PortAudio V19-devel 13 October 2002". -*/ -const char* Pa_GetVersionText( void ); - - -/** Error codes returned by PortAudio functions. - Note that with the exception of paNoError, all PaErrorCodes are negative. -*/ - -typedef int PaError; -typedef enum PaErrorCode -{ - paNoError = 0, - - paNotInitialized = -10000, - paUnanticipatedHostError, - paInvalidChannelCount, - paInvalidSampleRate, - paInvalidDevice, - paInvalidFlag, - paSampleFormatNotSupported, - paBadIODeviceCombination, - paInsufficientMemory, - paBufferTooBig, - paBufferTooSmall, - paNullCallback, - paBadStreamPtr, - paTimedOut, - paInternalError, - paDeviceUnavailable, - paIncompatibleHostApiSpecificStreamInfo, - paStreamIsStopped, - paStreamIsNotStopped, - paInputOverflowed, - paOutputUnderflowed, - paHostApiNotFound, - paInvalidHostApi, - paCanNotReadFromACallbackStream, /**< @todo review error code name */ - paCanNotWriteToACallbackStream, /**< @todo review error code name */ - paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */ - paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */ - paIncompatibleStreamHostApi -} PaErrorCode; - - -/** Translate the supplied PortAudio error code into a human readable - message. -*/ -const char *Pa_GetErrorText( PaError errorCode ); - - -/** Library initialization function - call this before using PortAudio. - This function initialises internal data structures and prepares underlying - host APIs for use. This function MUST be called before using any other - PortAudio API functions. - - If Pa_Initialize() is called multiple times, each successful - call must be matched with a corresponding call to Pa_Terminate(). - Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not - required to be fully nested. - - Note that if Pa_Initialize() returns an error code, Pa_Terminate() should - NOT be called. - - @return paNoError if successful, otherwise an error code indicating the cause - of failure. - - @see Pa_Terminate -*/ -PaError Pa_Initialize( void ); - - -/** Library termination function - call this when finished using PortAudio. - This function deallocates all resources allocated by PortAudio since it was - initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has - been called multiple times, each call must be matched with a corresponding call - to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically - close any PortAudio streams that are still open. - - Pa_Terminate() MUST be called before exiting a program which uses PortAudio. - Failure to do so may result in serious resource leaks, such as audio devices - not being available until the next reboot. - - @return paNoError if successful, otherwise an error code indicating the cause - of failure. - - @see Pa_Initialize -*/ -PaError Pa_Terminate( void ); - - - -/** The type used to refer to audio devices. Values of this type usually - range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice - and paUseHostApiSpecificDeviceSpecification values. - - @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification -*/ -typedef int PaDeviceIndex; - - -/** A special PaDeviceIndex value indicating that no device is available, - or should be used. - - @see PaDeviceIndex -*/ -#define paNoDevice ((PaDeviceIndex)-1) - - -/** A special PaDeviceIndex value indicating that the device(s) to be used - are specified in the host api specific stream info structure. - - @see PaDeviceIndex -*/ -#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2) - - -/* Host API enumeration mechanism */ - -/** The type used to enumerate to host APIs at runtime. Values of this type - range from 0 to (Pa_GetHostApiCount()-1). - - @see Pa_GetHostApiCount -*/ -typedef int PaHostApiIndex; - - -/** Retrieve the number of available host APIs. Even if a host API is - available it may have no devices available. - - @return A non-negative value indicating the number of available host APIs - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - @see PaHostApiIndex -*/ -PaHostApiIndex Pa_GetHostApiCount( void ); - - -/** Retrieve the index of the default host API. The default host API will be - the lowest common denominator host API on the current platform and is - unlikely to provide the best performance. - - @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1) - indicating the default host API index or, a PaErrorCode (which are always - negative) if PortAudio is not initialized or an error is encountered. -*/ -PaHostApiIndex Pa_GetDefaultHostApi( void ); - - -/** Unchanging unique identifiers for each supported host API. This type - is used in the PaHostApiInfo structure. The values are guaranteed to be - unique and to never change, thus allowing code to be written that - conditionally uses host API specific extensions. - - New type ids will be allocated when support for a host API reaches - "public alpha" status, prior to that developers should use the - paInDevelopment type id. - - @see PaHostApiInfo -*/ -typedef enum PaHostApiTypeId -{ - paInDevelopment=0, /* use while developing support for a new host API */ - paDirectSound=1, - paMME=2, - paASIO=3, - paSoundManager=4, - paCoreAudio=5, - paOSS=7, - paALSA=8, - paAL=9, - paBeOS=10, - paWDMKS=11, - paJACK=12 -} PaHostApiTypeId; - - -/** A structure containing information about a particular host API. */ - -typedef struct PaHostApiInfo -{ - /** this is struct version 1 */ - int structVersion; - /** The well known unique identifier of this host API @see PaHostApiTypeId */ - PaHostApiTypeId type; - /** A textual description of the host API for display on user interfaces. */ - const char *name; - - /** The number of devices belonging to this host API. This field may be - used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate - all devices for this host API. - @see Pa_HostApiDeviceIndexToDeviceIndex - */ - int deviceCount; - - /** The the default input device for this host API. The value will be a - device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice - if no default input device is available. - */ - PaDeviceIndex defaultInputDevice; - - /** The the default output device for this host API. The value will be a - device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice - if no default output device is available. - */ - PaDeviceIndex defaultOutputDevice; - -} PaHostApiInfo; - - -/** Retrieve a pointer to a structure containing information about a specific - host Api. - - @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) - - @return A pointer to an immutable PaHostApiInfo structure describing - a specific host API. If the hostApi parameter is out of range or an error - is encountered, the function returns NULL. - - The returned structure is owned by the PortAudio implementation and must not - be manipulated or freed. The pointer is only guaranteed to be valid between - calls to Pa_Initialize() and Pa_Terminate(). -*/ -const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi ); - - -/** Convert a static host API unique identifier, into a runtime - host API index. - - @param type A unique host API identifier belonging to the PaHostApiTypeId - enumeration. - - @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or, - a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - The paHostApiNotFound error code indicates that the host API specified by the - type parameter is not available. - - @see PaHostApiTypeId -*/ -PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ); - - -/** Convert a host-API-specific device index to standard PortAudio device index. - This function may be used in conjunction with the deviceCount field of - PaHostApiInfo to enumerate all devices for the specified host API. - - @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) - - @param hostApiDeviceIndex A valid per-host device index in the range - 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1) - - @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1) - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. - - A paInvalidHostApi error code indicates that the host API index specified by - the hostApi parameter is out of range. - - A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter - is out of range. - - @see PaHostApiInfo -*/ -PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, - int hostApiDeviceIndex ); - - - -/** Structure used to return information about a host error condition. -*/ -typedef struct PaHostErrorInfo{ - PaHostApiTypeId hostApiType; /**< the host API which returned the error code */ - long errorCode; /**< the error code returned */ - const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */ -}PaHostErrorInfo; - - -/** Return information about the last host error encountered. The error - information returned by Pa_GetLastHostErrorInfo() will never be modified - asyncronously by errors occurring in other PortAudio owned threads - (such as the thread that manages the stream callback.) - - This function is provided as a last resort, primarily to enhance debugging - by providing clients with access to all available error information. - - @return A pointer to an immutable structure constaining information about - the host error. The values in this structure will only be valid if a - PortAudio function has previously returned the paUnanticipatedHostError - error code. -*/ -const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ); - - - -/* Device enumeration and capabilities */ - -/** Retrieve the number of available devices. The number of available devices - may be zero. - - @return A non-negative value indicating the number of available devices or, - a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. -*/ -PaDeviceIndex Pa_GetDeviceCount( void ); - - -/** Retrieve the index of the default input device. The result can be - used in the inputDevice parameter to Pa_OpenStream(). - - @return The default input device index for the default host API, or paNoDevice - if no default input device is available or an error was encountered. -*/ -PaDeviceIndex Pa_GetDefaultInputDevice( void ); - - -/** Retrieve the index of the default output device. The result can be - used in the outputDevice parameter to Pa_OpenStream(). - - @return The default output device index for the defualt host API, or paNoDevice - if no default output device is available or an error was encountered. - - @note - On the PC, the user can specify a default device by - setting an environment variable. For example, to use device #1. -
- set PA_RECOMMENDED_OUTPUT_DEVICE=1
-
- The user should first determine the available device ids by using - the supplied application "pa_devs". -*/ -PaDeviceIndex Pa_GetDefaultOutputDevice( void ); - - -/** The type used to represent monotonic time in seconds that can be used - for syncronisation. The type is used for the outTime argument to the - PaStreamCallback and as the result of Pa_GetStreamTime(). - - @see PaStreamCallback, Pa_GetStreamTime -*/ -typedef double PaTime; - - -/** A type used to specify one or more sample formats. Each value indicates - a possible format for sound data passed to and from the stream callback, - Pa_ReadStream and Pa_WriteStream. - - The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8 - and aUInt8 are usually implemented by all implementations. - - The floating point representation (paFloat32) uses +1.0 and -1.0 as the - maximum and minimum respectively. - - paUInt8 is an unsigned 8 bit format where 128 is considered "ground" - - The paNonInterleaved flag indicates that a multichannel buffer is passed - as a set of non-interleaved pointers. - - @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo - @see paFloat32, paInt16, paInt32, paInt24, paInt8 - @see paUInt8, paCustomFormat, paNonInterleaved -*/ -typedef unsigned long PaSampleFormat; - - -#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */ -#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */ -#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */ -#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */ -#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */ -#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */ -#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */ - -#define paNonInterleaved ((PaSampleFormat) 0x80000000) - -/** A structure providing information and capabilities of PortAudio devices. - Devices may support input, output or both input and output. -*/ -typedef struct PaDeviceInfo -{ - int structVersion; /* this is struct version 2 */ - const char *name; - PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/ - - int maxInputChannels; - int maxOutputChannels; - - /* Default latency values for interactive performance. */ - PaTime defaultLowInputLatency; - PaTime defaultLowOutputLatency; - /* Default latency values for robust non-interactive applications (eg. playing sound files). */ - PaTime defaultHighInputLatency; - PaTime defaultHighOutputLatency; - - double defaultSampleRate; -} PaDeviceInfo; - - -/** Retrieve a pointer to a PaDeviceInfo structure containing information - about the specified device. - @return A pointer to an immutable PaDeviceInfo structure. If the device - parameter is out of range the function returns NULL. - - @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1) - - @note PortAudio manages the memory referenced by the returned pointer, - the client must not manipulate or free the memory. The pointer is only - guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate(). - - @see PaDeviceInfo, PaDeviceIndex -*/ -const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ); - - -/** Parameters for one direction (input or output) of a stream. -*/ -typedef struct PaStreamParameters -{ - /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1) - specifying the device to be used or the special constant - paUseHostApiSpecificDeviceSpecification which indicates that the actual - device(s) to use are specified in hostApiSpecificStreamInfo. - This field must not be set to paNoDevice. - */ - PaDeviceIndex device; - - /** The number of channels of sound to be delivered to the - stream callback or accessed by Pa_ReadStream() or Pa_WriteStream(). - It can range from 1 to the value of maxInputChannels in the - PaDeviceInfo record for the device specified by the device parameter. - */ - int channelCount; - - /** The sample format of the buffer provided to the stream callback, - a_ReadStream() or Pa_WriteStream(). It may be any of the formats described - by the PaSampleFormat enumeration. - */ - PaSampleFormat sampleFormat; - - /** The desired latency in seconds. Where practical, implementations should - configure their latency based on these parameters, otherwise they may - choose the closest viable latency instead. Unless the suggested latency - is greater than the absolute upper limit for the device implementations - shouldround the suggestedLatency up to the next practial value - ie to - provide an equal or higher latency than suggestedLatency whereever possibe. - Actual latency values for an open stream may be retrieved using the - inputLatency and outputLatency fields of the PaStreamInfo structure - returned by Pa_GetStreamInfo(). - @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo - */ - PaTime suggestedLatency; - - /** An optional pointer to a host api specific data structure - containing additional information for device setup and/or stream processing. - hostApiSpecificStreamInfo is never required for correct operation, - if not used it should be set to NULL. - */ - void *hostApiSpecificStreamInfo; - -} PaStreamParameters; - - -/** Return code for Pa_IsFormatSupported indicating success. */ -#define paFormatIsSupported (0) - -/** Determine whether it would be possible to open a stream with the specified - parameters. - - @param inputParameters A structure that describes the input parameters used to - open a stream. The suggestedLatency field is ignored. See PaStreamParameters - for a description of these parameters. inputParameters must be NULL for - output-only streams. - - @param outputParameters A structure that describes the output parameters used - to open a stream. The suggestedLatency field is ignored. See PaStreamParameters - for a description of these parameters. outputParameters must be NULL for - input-only streams. - - @param sampleRate The required sampleRate. For full-duplex streams it is the - sample rate for both input and output - - @return Returns 0 if the format is supported, and an error code indicating why - the format is not supported otherwise. The constant paFormatIsSupported is - provided to compare with the return value for success. - - @see paFormatIsSupported, PaStreamParameters -*/ -PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); - - - -/* Streaming types and functions */ - - -/** - A single PaStream can provide multiple channels of real-time - streaming audio input and output to a client application. A stream - provides access to audio hardware represented by one or more - PaDevices. Depending on the underlying Host API, it may be possible - to open multiple streams using the same device, however this behavior - is implementation defined. Portable applications should assume that - a PaDevice may be simultaneously used by at most one PaStream. - - Pointers to PaStream objects are passed between PortAudio functions that - operate on streams. - - @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream, - Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive, - Pa_GetStreamTime, Pa_GetStreamCpuLoad - -*/ -typedef void PaStream; - - -/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream() - or Pa_OpenDefaultStream() to indicate that the stream callback will - accept buffers of any size. -*/ -#define paFramesPerBufferUnspecified (0) - - -/** Flags used to control the behavior of a stream. They are passed as - parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be - ORed together. - - @see Pa_OpenStream, Pa_OpenDefaultStream - @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput, - paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags -*/ -typedef unsigned long PaStreamFlags; - -/** @see PaStreamFlags */ -#define paNoFlag ((PaStreamFlags) 0) - -/** Disable default clipping of out of range samples. - @see PaStreamFlags -*/ -#define paClipOff ((PaStreamFlags) 0x00000001) - -/** Disable default dithering. - @see PaStreamFlags -*/ -#define paDitherOff ((PaStreamFlags) 0x00000002) - -/** Flag requests that where possible a full duplex stream will not discard - overflowed input samples without calling the stream callback. This flag is - only valid for full duplex callback streams and only when used in combination - with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using - this flag incorrectly results in a paInvalidFlag error being returned from - Pa_OpenStream and Pa_OpenDefaultStream. - - @see PaStreamFlags, paFramesPerBufferUnspecified -*/ -#define paNeverDropInput ((PaStreamFlags) 0x00000004) - -/** Call the stream callback to fill initial output buffers, rather than the - default behavior of priming the buffers with zeros (silence). This flag has - no effect for input-only and blocking read/write streams. - - @see PaStreamFlags -*/ -#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008) - -/** A mask specifying the platform specific bits. - @see PaStreamFlags -*/ -#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000) - -/** - Timing information for the buffers passed to the stream callback. -*/ -typedef struct PaStreamCallbackTimeInfo{ - PaTime inputBufferAdcTime; - PaTime currentTime; - PaTime outputBufferDacTime; -} PaStreamCallbackTimeInfo; - - -/** - Flag bit constants for the statusFlags to PaStreamCallback. - - @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow, - paPrimingOutput -*/ -typedef unsigned long PaStreamCallbackFlags; - -/** In a stream opened with paFramesPerBufferUnspecified, indicates that - input data is all silence (zeros) because no real data is available. In a - stream opened without paFramesPerBufferUnspecified, it indicates that one or - more zero samples have been inserted into the input buffer to compensate - for an input underflow. - @see PaStreamCallbackFlags -*/ -#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001) - -/** In a stream opened with paFramesPerBufferUnspecified, indicates that data - prior to the first sample of the input buffer was discarded due to an - overflow, possibly because the stream callback is using too much CPU time. - Otherwise indicates that data prior to one or more samples in the - input buffer was discarded. - @see PaStreamCallbackFlags -*/ -#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002) - -/** Indicates that output data (or a gap) was inserted, possibly because the - stream callback is using too much CPU time. - @see PaStreamCallbackFlags -*/ -#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004) - -/** Indicates that output data will be discarded because no room is available. - @see PaStreamCallbackFlags -*/ -#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008) - -/** Some of all of the output data will be used to prime the stream, input - data may be zero. - @see PaStreamCallbackFlags -*/ -#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010) - -/** - Allowable return values for the PaStreamCallback. - @see PaStreamCallback -*/ -typedef enum PaStreamCallbackResult -{ - paContinue=0, - paComplete=1, - paAbort=2 -} PaStreamCallbackResult; - - -/** - Functions of type PaStreamCallback are implemented by PortAudio clients. - They consume, process or generate audio in response to requests from an - active PortAudio stream. - - @param input and @param output are arrays of interleaved samples, - the format, packing and number of channels used by the buffers are - determined by parameters to Pa_OpenStream(). - - @param frameCount The number of sample frames to be processed by - the stream callback. - - @param timeInfo The time in seconds when the first sample of the input - buffer was received at the audio input, the time in seconds when the first - sample of the output buffer will begin being played at the audio output, and - the time in seconds when the stream callback was called. - See also Pa_GetStreamTime() - - @param statusFlags Flags indicating whether input and/or output buffers - have been inserted or will be dropped to overcome underflow or overflow - conditions. - - @param userData The value of a user supplied pointer passed to - Pa_OpenStream() intended for storing synthesis data etc. - - @return - The stream callback should return one of the values in the - PaStreamCallbackResult enumeration. To ensure that the callback continues - to be called, it should return paContinue (0). Either paComplete or paAbort - can be returned to finish stream processing, after either of these values is - returned the callback will not be called again. If paAbort is returned the - stream will finish as soon as possible. If paComplete is returned, the stream - will continue until all buffers generated by the callback have been played. - This may be useful in applications such as soundfile players where a specific - duration of output is required. However, it is not necessary to utilise this - mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also - be used to stop the stream. The callback must always fill the entire output - buffer irrespective of its return value. - - @see Pa_OpenStream, Pa_OpenDefaultStream - - @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call - PortAudio API functions from within the stream callback. -*/ -typedef int PaStreamCallback( - const void *input, void *output, - unsigned long frameCount, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ); - - -/** Opens a stream for either input, output or both. - - @param stream The address of a PaStream pointer which will receive - a pointer to the newly opened stream. - - @param inputParameters A structure that describes the input parameters used by - the opened stream. See PaStreamParameters for a description of these parameters. - inputParameters must be NULL for output-only streams. - - @param outputParameters A structure that describes the output parameters used by - the opened stream. See PaStreamParameters for a description of these parameters. - outputParameters must be NULL for input-only streams. - - @param sampleRate The desired sampleRate. For full-duplex streams it is the - sample rate for both input and output - - @param framesPerBuffer The number of frames passed to the stream callback - function, or the preferred block granularity for a blocking read/write stream. - The special value paFramesPerBufferUnspecified (0) may be used to request that - the stream callback will recieve an optimal (and possibly varying) number of - frames based on host requirements and the requested latency settings. - Note: With some host APIs, the use of non-zero framesPerBuffer for a callback - stream may introduce an additional layer of buffering which could introduce - additional latency. PortAudio guarantees that the additional latency - will be kept to the theoretical minimum however, it is strongly recommended - that a non-zero framesPerBuffer value only be used when your algorithm - requires a fixed number of frames per stream callback. - - @param streamFlags Flags which modify the behaviour of the streaming process. - This parameter may contain a combination of flags ORed together. Some flags may - only be relevant to certain buffer formats. - - @param streamCallback A pointer to a client supplied function that is responsible - for processing and filling input and output buffers. If this parameter is NULL - the stream will be opened in 'blocking read/write' mode. In blocking mode, - the client can receive sample data using Pa_ReadStream and write sample data - using Pa_WriteStream, the number of samples that may be read or written - without blocking is returned by Pa_GetStreamReadAvailable and - Pa_GetStreamWriteAvailable respectively. - - @param userData A client supplied pointer which is passed to the stream callback - function. It could for example, contain a pointer to instance data necessary - for processing the audio buffers. This parameter is ignored if streamCallback - is NULL. - - @return - Upon success Pa_OpenStream() returns paNoError and places a pointer to a - valid PaStream in the stream argument. The stream is inactive (stopped). - If a call to Pa_OpenStream() fails, a non-zero error code is returned (see - PaError for possible error codes) and the value of stream is invalid. - - @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream, - Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable -*/ -PaError Pa_OpenStream( PaStream** stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); - - -/** A simplified version of Pa_OpenStream() that opens the default input - and/or output devices. - - @param stream The address of a PaStream pointer which will receive - a pointer to the newly opened stream. - - @param numInputChannels The number of channels of sound that will be supplied - to the stream callback or returned by Pa_ReadStream. It can range from 1 to - the value of maxInputChannels in the PaDeviceInfo record for the default input - device. If 0 the stream is opened as an output-only stream. - - @param numOutputChannels The number of channels of sound to be delivered to the - stream callback or passed to Pa_WriteStream. It can range from 1 to the value - of maxOutputChannels in the PaDeviceInfo record for the default output dvice. - If 0 the stream is opened as an output-only stream. - - @param sampleFormat The sample format of both the input and output buffers - provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream. - sampleFormat may be any of the formats described by the PaSampleFormat - enumeration. - - @param sampleRate Same as Pa_OpenStream parameter of the same name. - @param framesPerBuffer Same as Pa_OpenStream parameter of the same name. - @param streamCallback Same as Pa_OpenStream parameter of the same name. - @param userData Same as Pa_OpenStream parameter of the same name. - - @return As for Pa_OpenStream - - @see Pa_OpenStream, PaStreamCallback -*/ -PaError Pa_OpenDefaultStream( PaStream** stream, - int numInputChannels, - int numOutputChannels, - PaSampleFormat sampleFormat, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamCallback *streamCallback, - void *userData ); - - -/** Closes an audio stream. If the audio stream is active it - discards any pending buffers as if Pa_AbortStream() had been called. -*/ -PaError Pa_CloseStream( PaStream *stream ); - - -/** Functions of type PaStreamFinishedCallback are implemented by PortAudio - clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback - function. Once registered they are called when the stream becomes inactive - (ie once a call to Pa_StopStream() will not block). - A stream will become inactive after the stream callback returns non-zero, - or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio - output, if the stream callback returns paComplete, or Pa_StopStream is called, - the stream finished callback will not be called until all generated sample data - has been played. - - @param userData The userData parameter supplied to Pa_OpenStream() - - @see Pa_SetStreamFinishedCallback -*/ -typedef void PaStreamFinishedCallback( void *userData ); - - -/** Register a stream finished callback function which will be called when the - stream becomes inactive. See the description of PaStreamFinishedCallback for - further details about when the callback will be called. - - @param stream a pointer to a PaStream that is in the stopped state - if the - stream is not stopped, the stream's finished callback will remain unchanged - and an error code will be returned. - - @param streamFinishedCallback a pointer to a function with the same signature - as PaStreamFinishedCallback, that will be called when the stream becomes - inactive. Passing NULL for this parameter will un-register a previously - registered stream finished callback function. - - @return on success returns paNoError, otherwise an error code indicating the cause - of the error. - - @see PaStreamFinishedCallback -*/ -PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); - - -/** Commences audio processing. -*/ -PaError Pa_StartStream( PaStream *stream ); - - -/** Terminates audio processing. It waits until all pending - audio buffers have been played before it returns. -*/ -PaError Pa_StopStream( PaStream *stream ); - - -/** Terminates audio processing immediately without waiting for pending - buffers to complete. -*/ -PaError Pa_AbortStream( PaStream *stream ); - - -/** Determine whether the stream is stopped. - A stream is considered to be stopped prior to a successful call to - Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream. - If a stream callback returns a value other than paContinue the stream is NOT - considered to be stopped. - - @return Returns one (1) when the stream is stopped, zero (0) when - the stream is running or, a PaErrorCode (which are always negative) if - PortAudio is not initialized or an error is encountered. - - @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive -*/ -PaError Pa_IsStreamStopped( PaStream *stream ); - - -/** Determine whether the stream is active. - A stream is active after a successful call to Pa_StartStream(), until it - becomes inactive either as a result of a call to Pa_StopStream() or - Pa_AbortStream(), or as a result of a return value other than paContinue from - the stream callback. In the latter case, the stream is considered inactive - after the last buffer has finished playing. - - @return Returns one (1) when the stream is active (ie playing or recording - audio), zero (0) when not playing or, a PaErrorCode (which are always negative) - if PortAudio is not initialized or an error is encountered. - - @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped -*/ -PaError Pa_IsStreamActive( PaStream *stream ); - - - -/** A structure containing unchanging information about an open stream. - @see Pa_GetStreamInfo -*/ - -typedef struct PaStreamInfo -{ - /** this is struct version 1 */ - int structVersion; - - /** The input latency of the stream in seconds. This value provides the most - accurate estimate of input latency available to the implementation. It may - differ significantly from the suggestedLatency value passed to Pa_OpenStream(). - The value of this field will be zero (0.) for output-only streams. - @see PaTime - */ - PaTime inputLatency; - - /** The output latency of the stream in seconds. This value provides the most - accurate estimate of output latency available to the implementation. It may - differ significantly from the suggestedLatency value passed to Pa_OpenStream(). - The value of this field will be zero (0.) for input-only streams. - @see PaTime - */ - PaTime outputLatency; - - /** The sample rate of the stream in Hertz (samples per second). In cases - where the hardware sample rate is inaccurate and PortAudio is aware of it, - the value of this field may be different from the sampleRate parameter - passed to Pa_OpenStream(). If information about the actual hardware sample - rate is not available, this field will have the same value as the sampleRate - parameter passed to Pa_OpenStream(). - */ - double sampleRate; - -} PaStreamInfo; - - -/** Retrieve a pointer to a PaStreamInfo structure containing information - about the specified stream. - @return A pointer to an immutable PaStreamInfo structure. If the stream - parameter invalid, or an error is encountered, the function returns NULL. - - @param stream A pointer to an open stream previously created with Pa_OpenStream. - - @note PortAudio manages the memory referenced by the returned pointer, - the client must not manipulate or free the memory. The pointer is only - guaranteed to be valid until the specified stream is closed. - - @see PaStreamInfo -*/ -const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ); - - -/** Determine the current time for the stream according to the same clock used - to generate buffer timestamps. This time may be used for syncronising other - events to the audio stream, for example synchronizing audio to MIDI. - - @return The stream's current time in seconds, or 0 if an error occurred. - - @see PaTime, PaStreamCallback -*/ -PaTime Pa_GetStreamTime( PaStream *stream ); - - -/** Retrieve CPU usage information for the specified stream. - The "CPU Load" is a fraction of total CPU time consumed by a callback stream's - audio processing routines including, but not limited to the client supplied - stream callback. This function does not work with blocking read/write streams. - - This function may be called from the stream callback function or the - application. - - @return - A floating point value, typically between 0.0 and 1.0, where 1.0 indicates - that the stream callback is consuming the maximum number of CPU cycles possible - to maintain real-time operation. A value of 0.5 would imply that PortAudio and - the stream callback was consuming roughly 50% of the available CPU time. The - return value may exceed 1.0. A value of 0.0 will always be returned for a - blocking read/write stream, or if an error occurrs. -*/ -double Pa_GetStreamCpuLoad( PaStream* stream ); - - -/** Read samples from an input stream. The function doesn't return until - the entire buffer has been filled - this may involve waiting for the operating - system to supply the data. - - @param stream A pointer to an open stream previously created with Pa_OpenStream. - - @param buffer A pointer to a buffer of sample frames. The buffer contains - samples in the format specified by the inputParameters->sampleFormat field - used to open the stream, and the number of channels specified by - inputParameters->numChannels. If non-interleaved samples were requested, - buffer is a pointer to the first element of an array of non-interleaved - buffer pointers, one for each channel. - - @param frames The number of frames to be read into buffer. This parameter - is not constrained to a specific range, however high performance applications - will want to match this parameter to the framesPerBuffer parameter used - when opening the stream. - - @return On success PaNoError will be returned, or PaInputOverflowed if input - data was discarded by PortAudio after the previous call and before this call. -*/ -PaError Pa_ReadStream( PaStream* stream, - void *buffer, - unsigned long frames ); - - -/** Write samples to an output stream. This function doesn't return until the - entire buffer has been consumed - this may involve waiting for the operating - system to consume the data. - - @param stream A pointer to an open stream previously created with Pa_OpenStream. - - @param buffer A pointer to a buffer of sample frames. The buffer contains - samples in the format specified by the outputParameters->sampleFormat field - used to open the stream, and the number of channels specified by - outputParameters->numChannels. If non-interleaved samples were requested, - buffer is a pointer to the first element of an array of non-interleaved - buffer pointers, one for each channel. - - @param frames The number of frames to be written from buffer. This parameter - is not constrained to a specific range, however high performance applications - will want to match this parameter to the framesPerBuffer parameter used - when opening the stream. - - @return On success PaNoError will be returned, or paOutputUnderflowed if - additional output data was inserted after the previous call and before this - call. -*/ -PaError Pa_WriteStream( PaStream* stream, - const void *buffer, - unsigned long frames ); - - -/** Retrieve the number of frames that can be read from the stream without - waiting. - - @return Returns a non-negative value representing the maximum number of frames - that can be read from the stream without blocking or busy waiting or, a - PaErrorCode (which are always negative) if PortAudio is not initialized or an - error is encountered. -*/ -signed long Pa_GetStreamReadAvailable( PaStream* stream ); - - -/** Retrieve the number of frames that can be written to the stream without - waiting. - - @return Returns a non-negative value representing the maximum number of frames - that can be written to the stream without blocking or busy waiting or, a - PaErrorCode (which are always negative) if PortAudio is not initialized or an - error is encountered. -*/ -signed long Pa_GetStreamWriteAvailable( PaStream* stream ); - - -/* Miscellaneous utilities */ - - -/** Retrieve the size of a given sample format in bytes. - - @return The size in bytes of a single sample in the specified format, - or paSampleFormatNotSupported if the format is not supported. -*/ -PaError Pa_GetSampleSize( PaSampleFormat format ); - - -/** Put the caller to sleep for at least 'msec' milliseconds. This function is - provided only as a convenience for authors of portable code (such as the tests - and examples in the PortAudio distribution.) - - The function may sleep longer than requested so don't rely on this for accurate - musical timing. -*/ -void Pa_Sleep( long msec ); - - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* PORTAUDIO_H */ + +#ifndef PORTAUDIO_H +#define PORTAUDIO_H +/* + * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $ + * PortAudio Portable Real-Time Audio Library + * PortAudio API Header File + * Latest version available at: http://www.portaudio.com/ + * + * Copyright (c) 1999-2002 Ross Bencina and Phil Burk + * + * 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. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * 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. + */ + +/** @file + @brief The PortAudio API. +*/ + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** Retrieve the release number of the currently running PortAudio build, + eg 1900. +*/ +int Pa_GetVersion( void ); + + +/** Retrieve a textual description of the current PortAudio build, + eg "PortAudio V19-devel 13 October 2002". +*/ +const char* Pa_GetVersionText( void ); + + +/** Error codes returned by PortAudio functions. + Note that with the exception of paNoError, all PaErrorCodes are negative. +*/ + +typedef int PaError; +typedef enum PaErrorCode +{ + paNoError = 0, + + paNotInitialized = -10000, + paUnanticipatedHostError, + paInvalidChannelCount, + paInvalidSampleRate, + paInvalidDevice, + paInvalidFlag, + paSampleFormatNotSupported, + paBadIODeviceCombination, + paInsufficientMemory, + paBufferTooBig, + paBufferTooSmall, + paNullCallback, + paBadStreamPtr, + paTimedOut, + paInternalError, + paDeviceUnavailable, + paIncompatibleHostApiSpecificStreamInfo, + paStreamIsStopped, + paStreamIsNotStopped, + paInputOverflowed, + paOutputUnderflowed, + paHostApiNotFound, + paInvalidHostApi, + paCanNotReadFromACallbackStream, /**< @todo review error code name */ + paCanNotWriteToACallbackStream, /**< @todo review error code name */ + paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */ + paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */ + paIncompatibleStreamHostApi +} PaErrorCode; + + +/** Translate the supplied PortAudio error code into a human readable + message. +*/ +const char *Pa_GetErrorText( PaError errorCode ); + + +/** Library initialization function - call this before using PortAudio. + This function initialises internal data structures and prepares underlying + host APIs for use. This function MUST be called before using any other + PortAudio API functions. + + If Pa_Initialize() is called multiple times, each successful + call must be matched with a corresponding call to Pa_Terminate(). + Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not + required to be fully nested. + + Note that if Pa_Initialize() returns an error code, Pa_Terminate() should + NOT be called. + + @return paNoError if successful, otherwise an error code indicating the cause + of failure. + + @see Pa_Terminate +*/ +PaError Pa_Initialize( void ); + + +/** Library termination function - call this when finished using PortAudio. + This function deallocates all resources allocated by PortAudio since it was + initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has + been called multiple times, each call must be matched with a corresponding call + to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically + close any PortAudio streams that are still open. + + Pa_Terminate() MUST be called before exiting a program which uses PortAudio. + Failure to do so may result in serious resource leaks, such as audio devices + not being available until the next reboot. + + @return paNoError if successful, otherwise an error code indicating the cause + of failure. + + @see Pa_Initialize +*/ +PaError Pa_Terminate( void ); + + + +/** The type used to refer to audio devices. Values of this type usually + range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice + and paUseHostApiSpecificDeviceSpecification values. + + @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification +*/ +typedef int PaDeviceIndex; + + +/** A special PaDeviceIndex value indicating that no device is available, + or should be used. + + @see PaDeviceIndex +*/ +#define paNoDevice ((PaDeviceIndex)-1) + + +/** A special PaDeviceIndex value indicating that the device(s) to be used + are specified in the host api specific stream info structure. + + @see PaDeviceIndex +*/ +#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2) + + +/* Host API enumeration mechanism */ + +/** The type used to enumerate to host APIs at runtime. Values of this type + range from 0 to (Pa_GetHostApiCount()-1). + + @see Pa_GetHostApiCount +*/ +typedef int PaHostApiIndex; + + +/** Retrieve the number of available host APIs. Even if a host API is + available it may have no devices available. + + @return A non-negative value indicating the number of available host APIs + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + @see PaHostApiIndex +*/ +PaHostApiIndex Pa_GetHostApiCount( void ); + + +/** Retrieve the index of the default host API. The default host API will be + the lowest common denominator host API on the current platform and is + unlikely to provide the best performance. + + @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1) + indicating the default host API index or, a PaErrorCode (which are always + negative) if PortAudio is not initialized or an error is encountered. +*/ +PaHostApiIndex Pa_GetDefaultHostApi( void ); + + +/** Unchanging unique identifiers for each supported host API. This type + is used in the PaHostApiInfo structure. The values are guaranteed to be + unique and to never change, thus allowing code to be written that + conditionally uses host API specific extensions. + + New type ids will be allocated when support for a host API reaches + "public alpha" status, prior to that developers should use the + paInDevelopment type id. + + @see PaHostApiInfo +*/ +typedef enum PaHostApiTypeId +{ + paInDevelopment=0, /* use while developing support for a new host API */ + paDirectSound=1, + paMME=2, + paASIO=3, + paSoundManager=4, + paCoreAudio=5, + paOSS=7, + paALSA=8, + paAL=9, + paBeOS=10, + paWDMKS=11, + paJACK=12 +} PaHostApiTypeId; + + +/** A structure containing information about a particular host API. */ + +typedef struct PaHostApiInfo +{ + /** this is struct version 1 */ + int structVersion; + /** The well known unique identifier of this host API @see PaHostApiTypeId */ + PaHostApiTypeId type; + /** A textual description of the host API for display on user interfaces. */ + const char *name; + + /** The number of devices belonging to this host API. This field may be + used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate + all devices for this host API. + @see Pa_HostApiDeviceIndexToDeviceIndex + */ + int deviceCount; + + /** The the default input device for this host API. The value will be a + device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice + if no default input device is available. + */ + PaDeviceIndex defaultInputDevice; + + /** The the default output device for this host API. The value will be a + device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice + if no default output device is available. + */ + PaDeviceIndex defaultOutputDevice; + +} PaHostApiInfo; + + +/** Retrieve a pointer to a structure containing information about a specific + host Api. + + @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) + + @return A pointer to an immutable PaHostApiInfo structure describing + a specific host API. If the hostApi parameter is out of range or an error + is encountered, the function returns NULL. + + The returned structure is owned by the PortAudio implementation and must not + be manipulated or freed. The pointer is only guaranteed to be valid between + calls to Pa_Initialize() and Pa_Terminate(). +*/ +const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi ); + + +/** Convert a static host API unique identifier, into a runtime + host API index. + + @param type A unique host API identifier belonging to the PaHostApiTypeId + enumeration. + + @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or, + a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + The paHostApiNotFound error code indicates that the host API specified by the + type parameter is not available. + + @see PaHostApiTypeId +*/ +PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ); + + +/** Convert a host-API-specific device index to standard PortAudio device index. + This function may be used in conjunction with the deviceCount field of + PaHostApiInfo to enumerate all devices for the specified host API. + + @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1) + + @param hostApiDeviceIndex A valid per-host device index in the range + 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1) + + @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1) + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. + + A paInvalidHostApi error code indicates that the host API index specified by + the hostApi parameter is out of range. + + A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter + is out of range. + + @see PaHostApiInfo +*/ +PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, + int hostApiDeviceIndex ); + + + +/** Structure used to return information about a host error condition. +*/ +typedef struct PaHostErrorInfo{ + PaHostApiTypeId hostApiType; /**< the host API which returned the error code */ + long errorCode; /**< the error code returned */ + const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */ +}PaHostErrorInfo; + + +/** Return information about the last host error encountered. The error + information returned by Pa_GetLastHostErrorInfo() will never be modified + asyncronously by errors occurring in other PortAudio owned threads + (such as the thread that manages the stream callback.) + + This function is provided as a last resort, primarily to enhance debugging + by providing clients with access to all available error information. + + @return A pointer to an immutable structure constaining information about + the host error. The values in this structure will only be valid if a + PortAudio function has previously returned the paUnanticipatedHostError + error code. +*/ +const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ); + + + +/* Device enumeration and capabilities */ + +/** Retrieve the number of available devices. The number of available devices + may be zero. + + @return A non-negative value indicating the number of available devices or, + a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. +*/ +PaDeviceIndex Pa_GetDeviceCount( void ); + + +/** Retrieve the index of the default input device. The result can be + used in the inputDevice parameter to Pa_OpenStream(). + + @return The default input device index for the default host API, or paNoDevice + if no default input device is available or an error was encountered. +*/ +PaDeviceIndex Pa_GetDefaultInputDevice( void ); + + +/** Retrieve the index of the default output device. The result can be + used in the outputDevice parameter to Pa_OpenStream(). + + @return The default output device index for the defualt host API, or paNoDevice + if no default output device is available or an error was encountered. + + @note + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. +
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ids by using + the supplied application "pa_devs". +*/ +PaDeviceIndex Pa_GetDefaultOutputDevice( void ); + + +/** The type used to represent monotonic time in seconds that can be used + for syncronisation. The type is used for the outTime argument to the + PaStreamCallback and as the result of Pa_GetStreamTime(). + + @see PaStreamCallback, Pa_GetStreamTime +*/ +typedef double PaTime; + + +/** A type used to specify one or more sample formats. Each value indicates + a possible format for sound data passed to and from the stream callback, + Pa_ReadStream and Pa_WriteStream. + + The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8 + and aUInt8 are usually implemented by all implementations. + + The floating point representation (paFloat32) uses +1.0 and -1.0 as the + maximum and minimum respectively. + + paUInt8 is an unsigned 8 bit format where 128 is considered "ground" + + The paNonInterleaved flag indicates that a multichannel buffer is passed + as a set of non-interleaved pointers. + + @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo + @see paFloat32, paInt16, paInt32, paInt24, paInt8 + @see paUInt8, paCustomFormat, paNonInterleaved +*/ +typedef unsigned long PaSampleFormat; + + +#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */ +#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */ +#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */ +#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */ +#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */ +#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */ +#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */ + +#define paNonInterleaved ((PaSampleFormat) 0x80000000) + +/** A structure providing information and capabilities of PortAudio devices. + Devices may support input, output or both input and output. +*/ +typedef struct PaDeviceInfo +{ + int structVersion; /* this is struct version 2 */ + const char *name; + PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/ + + int maxInputChannels; + int maxOutputChannels; + + /* Default latency values for interactive performance. */ + PaTime defaultLowInputLatency; + PaTime defaultLowOutputLatency; + /* Default latency values for robust non-interactive applications (eg. playing sound files). */ + PaTime defaultHighInputLatency; + PaTime defaultHighOutputLatency; + + double defaultSampleRate; +} PaDeviceInfo; + + +/** Retrieve a pointer to a PaDeviceInfo structure containing information + about the specified device. + @return A pointer to an immutable PaDeviceInfo structure. If the device + parameter is out of range the function returns NULL. + + @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1) + + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate(). + + @see PaDeviceInfo, PaDeviceIndex +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ); + + +/** Parameters for one direction (input or output) of a stream. +*/ +typedef struct PaStreamParameters +{ + /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1) + specifying the device to be used or the special constant + paUseHostApiSpecificDeviceSpecification which indicates that the actual + device(s) to use are specified in hostApiSpecificStreamInfo. + This field must not be set to paNoDevice. + */ + PaDeviceIndex device; + + /** The number of channels of sound to be delivered to the + stream callback or accessed by Pa_ReadStream() or Pa_WriteStream(). + It can range from 1 to the value of maxInputChannels in the + PaDeviceInfo record for the device specified by the device parameter. + */ + int channelCount; + + /** The sample format of the buffer provided to the stream callback, + a_ReadStream() or Pa_WriteStream(). It may be any of the formats described + by the PaSampleFormat enumeration. + */ + PaSampleFormat sampleFormat; + + /** The desired latency in seconds. Where practical, implementations should + configure their latency based on these parameters, otherwise they may + choose the closest viable latency instead. Unless the suggested latency + is greater than the absolute upper limit for the device implementations + shouldround the suggestedLatency up to the next practial value - ie to + provide an equal or higher latency than suggestedLatency whereever possibe. + Actual latency values for an open stream may be retrieved using the + inputLatency and outputLatency fields of the PaStreamInfo structure + returned by Pa_GetStreamInfo(). + @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo + */ + PaTime suggestedLatency; + + /** An optional pointer to a host api specific data structure + containing additional information for device setup and/or stream processing. + hostApiSpecificStreamInfo is never required for correct operation, + if not used it should be set to NULL. + */ + void *hostApiSpecificStreamInfo; + +} PaStreamParameters; + + +/** Return code for Pa_IsFormatSupported indicating success. */ +#define paFormatIsSupported (0) + +/** Determine whether it would be possible to open a stream with the specified + parameters. + + @param inputParameters A structure that describes the input parameters used to + open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. inputParameters must be NULL for + output-only streams. + + @param outputParameters A structure that describes the output parameters used + to open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. outputParameters must be NULL for + input-only streams. + + @param sampleRate The required sampleRate. For full-duplex streams it is the + sample rate for both input and output + + @return Returns 0 if the format is supported, and an error code indicating why + the format is not supported otherwise. The constant paFormatIsSupported is + provided to compare with the return value for success. + + @see paFormatIsSupported, PaStreamParameters +*/ +PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); + + + +/* Streaming types and functions */ + + +/** + A single PaStream can provide multiple channels of real-time + streaming audio input and output to a client application. A stream + provides access to audio hardware represented by one or more + PaDevices. Depending on the underlying Host API, it may be possible + to open multiple streams using the same device, however this behavior + is implementation defined. Portable applications should assume that + a PaDevice may be simultaneously used by at most one PaStream. + + Pointers to PaStream objects are passed between PortAudio functions that + operate on streams. + + @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream, + Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive, + Pa_GetStreamTime, Pa_GetStreamCpuLoad + +*/ +typedef void PaStream; + + +/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream() + or Pa_OpenDefaultStream() to indicate that the stream callback will + accept buffers of any size. +*/ +#define paFramesPerBufferUnspecified (0) + + +/** Flags used to control the behavior of a stream. They are passed as + parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be + ORed together. + + @see Pa_OpenStream, Pa_OpenDefaultStream + @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput, + paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags +*/ +typedef unsigned long PaStreamFlags; + +/** @see PaStreamFlags */ +#define paNoFlag ((PaStreamFlags) 0) + +/** Disable default clipping of out of range samples. + @see PaStreamFlags +*/ +#define paClipOff ((PaStreamFlags) 0x00000001) + +/** Disable default dithering. + @see PaStreamFlags +*/ +#define paDitherOff ((PaStreamFlags) 0x00000002) + +/** Flag requests that where possible a full duplex stream will not discard + overflowed input samples without calling the stream callback. This flag is + only valid for full duplex callback streams and only when used in combination + with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using + this flag incorrectly results in a paInvalidFlag error being returned from + Pa_OpenStream and Pa_OpenDefaultStream. + + @see PaStreamFlags, paFramesPerBufferUnspecified +*/ +#define paNeverDropInput ((PaStreamFlags) 0x00000004) + +/** Call the stream callback to fill initial output buffers, rather than the + default behavior of priming the buffers with zeros (silence). This flag has + no effect for input-only and blocking read/write streams. + + @see PaStreamFlags +*/ +#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008) + +/** A mask specifying the platform specific bits. + @see PaStreamFlags +*/ +#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000) + +/** + Timing information for the buffers passed to the stream callback. +*/ +typedef struct PaStreamCallbackTimeInfo{ + PaTime inputBufferAdcTime; + PaTime currentTime; + PaTime outputBufferDacTime; +} PaStreamCallbackTimeInfo; + + +/** + Flag bit constants for the statusFlags to PaStreamCallback. + + @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow, + paPrimingOutput +*/ +typedef unsigned long PaStreamCallbackFlags; + +/** In a stream opened with paFramesPerBufferUnspecified, indicates that + input data is all silence (zeros) because no real data is available. In a + stream opened without paFramesPerBufferUnspecified, it indicates that one or + more zero samples have been inserted into the input buffer to compensate + for an input underflow. + @see PaStreamCallbackFlags +*/ +#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001) + +/** In a stream opened with paFramesPerBufferUnspecified, indicates that data + prior to the first sample of the input buffer was discarded due to an + overflow, possibly because the stream callback is using too much CPU time. + Otherwise indicates that data prior to one or more samples in the + input buffer was discarded. + @see PaStreamCallbackFlags +*/ +#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002) + +/** Indicates that output data (or a gap) was inserted, possibly because the + stream callback is using too much CPU time. + @see PaStreamCallbackFlags +*/ +#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004) + +/** Indicates that output data will be discarded because no room is available. + @see PaStreamCallbackFlags +*/ +#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008) + +/** Some of all of the output data will be used to prime the stream, input + data may be zero. + @see PaStreamCallbackFlags +*/ +#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010) + +/** + Allowable return values for the PaStreamCallback. + @see PaStreamCallback +*/ +typedef enum PaStreamCallbackResult +{ + paContinue=0, + paComplete=1, + paAbort=2 +} PaStreamCallbackResult; + + +/** + Functions of type PaStreamCallback are implemented by PortAudio clients. + They consume, process or generate audio in response to requests from an + active PortAudio stream. + + @param input and @param output are arrays of interleaved samples, + the format, packing and number of channels used by the buffers are + determined by parameters to Pa_OpenStream(). + + @param frameCount The number of sample frames to be processed by + the stream callback. + + @param timeInfo The time in seconds when the first sample of the input + buffer was received at the audio input, the time in seconds when the first + sample of the output buffer will begin being played at the audio output, and + the time in seconds when the stream callback was called. + See also Pa_GetStreamTime() + + @param statusFlags Flags indicating whether input and/or output buffers + have been inserted or will be dropped to overcome underflow or overflow + conditions. + + @param userData The value of a user supplied pointer passed to + Pa_OpenStream() intended for storing synthesis data etc. + + @return + The stream callback should return one of the values in the + PaStreamCallbackResult enumeration. To ensure that the callback continues + to be called, it should return paContinue (0). Either paComplete or paAbort + can be returned to finish stream processing, after either of these values is + returned the callback will not be called again. If paAbort is returned the + stream will finish as soon as possible. If paComplete is returned, the stream + will continue until all buffers generated by the callback have been played. + This may be useful in applications such as soundfile players where a specific + duration of output is required. However, it is not necessary to utilise this + mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also + be used to stop the stream. The callback must always fill the entire output + buffer irrespective of its return value. + + @see Pa_OpenStream, Pa_OpenDefaultStream + + @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call + PortAudio API functions from within the stream callback. +*/ +typedef int PaStreamCallback( + const void *input, void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); + + +/** Opens a stream for either input, output or both. + + @param stream The address of a PaStream pointer which will receive + a pointer to the newly opened stream. + + @param inputParameters A structure that describes the input parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + inputParameters must be NULL for output-only streams. + + @param outputParameters A structure that describes the output parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + outputParameters must be NULL for input-only streams. + + @param sampleRate The desired sampleRate. For full-duplex streams it is the + sample rate for both input and output + + @param framesPerBuffer The number of frames passed to the stream callback + function, or the preferred block granularity for a blocking read/write stream. + The special value paFramesPerBufferUnspecified (0) may be used to request that + the stream callback will recieve an optimal (and possibly varying) number of + frames based on host requirements and the requested latency settings. + Note: With some host APIs, the use of non-zero framesPerBuffer for a callback + stream may introduce an additional layer of buffering which could introduce + additional latency. PortAudio guarantees that the additional latency + will be kept to the theoretical minimum however, it is strongly recommended + that a non-zero framesPerBuffer value only be used when your algorithm + requires a fixed number of frames per stream callback. + + @param streamFlags Flags which modify the behaviour of the streaming process. + This parameter may contain a combination of flags ORed together. Some flags may + only be relevant to certain buffer formats. + + @param streamCallback A pointer to a client supplied function that is responsible + for processing and filling input and output buffers. If this parameter is NULL + the stream will be opened in 'blocking read/write' mode. In blocking mode, + the client can receive sample data using Pa_ReadStream and write sample data + using Pa_WriteStream, the number of samples that may be read or written + without blocking is returned by Pa_GetStreamReadAvailable and + Pa_GetStreamWriteAvailable respectively. + + @param userData A client supplied pointer which is passed to the stream callback + function. It could for example, contain a pointer to instance data necessary + for processing the audio buffers. This parameter is ignored if streamCallback + is NULL. + + @return + Upon success Pa_OpenStream() returns paNoError and places a pointer to a + valid PaStream in the stream argument. The stream is inactive (stopped). + If a call to Pa_OpenStream() fails, a non-zero error code is returned (see + PaError for possible error codes) and the value of stream is invalid. + + @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream, + Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable +*/ +PaError Pa_OpenStream( PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); + + +/** A simplified version of Pa_OpenStream() that opens the default input + and/or output devices. + + @param stream The address of a PaStream pointer which will receive + a pointer to the newly opened stream. + + @param numInputChannels The number of channels of sound that will be supplied + to the stream callback or returned by Pa_ReadStream. It can range from 1 to + the value of maxInputChannels in the PaDeviceInfo record for the default input + device. If 0 the stream is opened as an output-only stream. + + @param numOutputChannels The number of channels of sound to be delivered to the + stream callback or passed to Pa_WriteStream. It can range from 1 to the value + of maxOutputChannels in the PaDeviceInfo record for the default output dvice. + If 0 the stream is opened as an output-only stream. + + @param sampleFormat The sample format of both the input and output buffers + provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream. + sampleFormat may be any of the formats described by the PaSampleFormat + enumeration. + + @param sampleRate Same as Pa_OpenStream parameter of the same name. + @param framesPerBuffer Same as Pa_OpenStream parameter of the same name. + @param streamCallback Same as Pa_OpenStream parameter of the same name. + @param userData Same as Pa_OpenStream parameter of the same name. + + @return As for Pa_OpenStream + + @see Pa_OpenStream, PaStreamCallback +*/ +PaError Pa_OpenDefaultStream( PaStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamCallback *streamCallback, + void *userData ); + + +/** Closes an audio stream. If the audio stream is active it + discards any pending buffers as if Pa_AbortStream() had been called. +*/ +PaError Pa_CloseStream( PaStream *stream ); + + +/** Functions of type PaStreamFinishedCallback are implemented by PortAudio + clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback + function. Once registered they are called when the stream becomes inactive + (ie once a call to Pa_StopStream() will not block). + A stream will become inactive after the stream callback returns non-zero, + or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio + output, if the stream callback returns paComplete, or Pa_StopStream is called, + the stream finished callback will not be called until all generated sample data + has been played. + + @param userData The userData parameter supplied to Pa_OpenStream() + + @see Pa_SetStreamFinishedCallback +*/ +typedef void PaStreamFinishedCallback( void *userData ); + + +/** Register a stream finished callback function which will be called when the + stream becomes inactive. See the description of PaStreamFinishedCallback for + further details about when the callback will be called. + + @param stream a pointer to a PaStream that is in the stopped state - if the + stream is not stopped, the stream's finished callback will remain unchanged + and an error code will be returned. + + @param streamFinishedCallback a pointer to a function with the same signature + as PaStreamFinishedCallback, that will be called when the stream becomes + inactive. Passing NULL for this parameter will un-register a previously + registered stream finished callback function. + + @return on success returns paNoError, otherwise an error code indicating the cause + of the error. + + @see PaStreamFinishedCallback +*/ +PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); + + +/** Commences audio processing. +*/ +PaError Pa_StartStream( PaStream *stream ); + + +/** Terminates audio processing. It waits until all pending + audio buffers have been played before it returns. +*/ +PaError Pa_StopStream( PaStream *stream ); + + +/** Terminates audio processing immediately without waiting for pending + buffers to complete. +*/ +PaError Pa_AbortStream( PaStream *stream ); + + +/** Determine whether the stream is stopped. + A stream is considered to be stopped prior to a successful call to + Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream. + If a stream callback returns a value other than paContinue the stream is NOT + considered to be stopped. + + @return Returns one (1) when the stream is stopped, zero (0) when + the stream is running or, a PaErrorCode (which are always negative) if + PortAudio is not initialized or an error is encountered. + + @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive +*/ +PaError Pa_IsStreamStopped( PaStream *stream ); + + +/** Determine whether the stream is active. + A stream is active after a successful call to Pa_StartStream(), until it + becomes inactive either as a result of a call to Pa_StopStream() or + Pa_AbortStream(), or as a result of a return value other than paContinue from + the stream callback. In the latter case, the stream is considered inactive + after the last buffer has finished playing. + + @return Returns one (1) when the stream is active (ie playing or recording + audio), zero (0) when not playing or, a PaErrorCode (which are always negative) + if PortAudio is not initialized or an error is encountered. + + @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped +*/ +PaError Pa_IsStreamActive( PaStream *stream ); + + + +/** A structure containing unchanging information about an open stream. + @see Pa_GetStreamInfo +*/ + +typedef struct PaStreamInfo +{ + /** this is struct version 1 */ + int structVersion; + + /** The input latency of the stream in seconds. This value provides the most + accurate estimate of input latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for output-only streams. + @see PaTime + */ + PaTime inputLatency; + + /** The output latency of the stream in seconds. This value provides the most + accurate estimate of output latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for input-only streams. + @see PaTime + */ + PaTime outputLatency; + + /** The sample rate of the stream in Hertz (samples per second). In cases + where the hardware sample rate is inaccurate and PortAudio is aware of it, + the value of this field may be different from the sampleRate parameter + passed to Pa_OpenStream(). If information about the actual hardware sample + rate is not available, this field will have the same value as the sampleRate + parameter passed to Pa_OpenStream(). + */ + double sampleRate; + +} PaStreamInfo; + + +/** Retrieve a pointer to a PaStreamInfo structure containing information + about the specified stream. + @return A pointer to an immutable PaStreamInfo structure. If the stream + parameter invalid, or an error is encountered, the function returns NULL. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid until the specified stream is closed. + + @see PaStreamInfo +*/ +const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ); + + +/** Determine the current time for the stream according to the same clock used + to generate buffer timestamps. This time may be used for syncronising other + events to the audio stream, for example synchronizing audio to MIDI. + + @return The stream's current time in seconds, or 0 if an error occurred. + + @see PaTime, PaStreamCallback +*/ +PaTime Pa_GetStreamTime( PaStream *stream ); + + +/** Retrieve CPU usage information for the specified stream. + The "CPU Load" is a fraction of total CPU time consumed by a callback stream's + audio processing routines including, but not limited to the client supplied + stream callback. This function does not work with blocking read/write streams. + + This function may be called from the stream callback function or the + application. + + @return + A floating point value, typically between 0.0 and 1.0, where 1.0 indicates + that the stream callback is consuming the maximum number of CPU cycles possible + to maintain real-time operation. A value of 0.5 would imply that PortAudio and + the stream callback was consuming roughly 50% of the available CPU time. The + return value may exceed 1.0. A value of 0.0 will always be returned for a + blocking read/write stream, or if an error occurrs. +*/ +double Pa_GetStreamCpuLoad( PaStream* stream ); + + +/** Read samples from an input stream. The function doesn't return until + the entire buffer has been filled - this may involve waiting for the operating + system to supply the data. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the inputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + inputParameters->numChannels. If non-interleaved samples were requested, + buffer is a pointer to the first element of an array of non-interleaved + buffer pointers, one for each channel. + + @param frames The number of frames to be read into buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or PaInputOverflowed if input + data was discarded by PortAudio after the previous call and before this call. +*/ +PaError Pa_ReadStream( PaStream* stream, + void *buffer, + unsigned long frames ); + + +/** Write samples to an output stream. This function doesn't return until the + entire buffer has been consumed - this may involve waiting for the operating + system to consume the data. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the outputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + outputParameters->numChannels. If non-interleaved samples were requested, + buffer is a pointer to the first element of an array of non-interleaved + buffer pointers, one for each channel. + + @param frames The number of frames to be written from buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or paOutputUnderflowed if + additional output data was inserted after the previous call and before this + call. +*/ +PaError Pa_WriteStream( PaStream* stream, + const void *buffer, + unsigned long frames ); + + +/** Retrieve the number of frames that can be read from the stream without + waiting. + + @return Returns a non-negative value representing the maximum number of frames + that can be read from the stream without blocking or busy waiting or, a + PaErrorCode (which are always negative) if PortAudio is not initialized or an + error is encountered. +*/ +signed long Pa_GetStreamReadAvailable( PaStream* stream ); + + +/** Retrieve the number of frames that can be written to the stream without + waiting. + + @return Returns a non-negative value representing the maximum number of frames + that can be written to the stream without blocking or busy waiting or, a + PaErrorCode (which are always negative) if PortAudio is not initialized or an + error is encountered. +*/ +signed long Pa_GetStreamWriteAvailable( PaStream* stream ); + + +/* Miscellaneous utilities */ + + +/** Retrieve the size of a given sample format in bytes. + + @return The size in bytes of a single sample in the specified format, + or paSampleFormatNotSupported if the format is not supported. +*/ +PaError Pa_GetSampleSize( PaSampleFormat format ); + + +/** Put the caller to sleep for at least 'msec' milliseconds. This function is + provided only as a convenience for authors of portable code (such as the tests + and examples in the PortAudio distribution.) + + The function may sleep longer than requested so don't rely on this for accurate + musical timing. +*/ +void Pa_Sleep( long msec ); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORTAUDIO_H */ diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c index c22bcd4c4..f509b117c 100644 --- a/pjmedia/src/pjmedia/rtcp.c +++ b/pjmedia/src/pjmedia/rtcp.c @@ -1,200 +1,200 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/rtcp.c 4 8/24/05 10:30a Bennylp $ */ - -#include -#include /* pj_gettimeofday */ -#include /* pj_htonx, pj_ntohx */ -#include /* memset */ - -#define RTCP_SR 200 -#define RTCP_RR 201 - - - -/* - * Get NTP time. - */ -static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp) -{ - pj_time_val tv; - - pj_gettimeofday(&tv); - - ntp->hi = tv.sec; - tv.msec = tv.msec % 1000; - ntp->lo = tv.msec * 0xFFFF / 1000; - ntp->lo <<= 16; -} - - -PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc) -{ - pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; - - memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt)); - - /* Init time */ - s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0; - s->rtcp_lsr_time = 0; - - /* Init common RTCP header */ - rtcp_pkt->common.version = 2; - rtcp_pkt->common.count = 1; - rtcp_pkt->common.pt = RTCP_SR; - rtcp_pkt->common.length = pj_htons(12); - - /* Init SR */ - rtcp_pkt->sr.ssrc = pj_htonl(ssrc); - - /* RR will be initialized on receipt of the first RTP packet. */ -} - -PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session) -{ - /* Nothing to do. */ - PJ_UNUSED_ARG(session) -} - -static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq) -{ - s->received = 0; - s->expected_prior = 0; - s->received_prior = 0; - s->transit = 0; - s->jitter = 0; - - pj_rtp_seq_restart(&s->seq_ctrl, seq); -} - -PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts) -{ - pj_uint32_t arrival; - pj_int32_t transit; - unsigned long timer_tick; - pj_time_val tv; - int status; - - /* Update sequence numbers (received, lost, etc). */ - status = pj_rtp_seq_update(&s->seq_ctrl, seq); - if (status == PJ_RTP_ERR_SESSION_RESTARTED) { - rtcp_init_seq(s, seq); - status = 0; - } - - if (status != 0) - return; - - ++s->received; - - pj_gettimeofday(&tv); - timer_tick = tv.sec * 1000 + tv.msec; - - /* - * Calculate jitter (s->jitter is in timer tick unit) - */ - PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE) - - arrival = timer_tick << 3; // 8 samples per ms. - transit = arrival - rtp_ts; - - if (s->transit == 0) { - s->transit = transit; - } else { - pj_int32_t d, jitter = s->jitter; - - d = transit - s->transit; - s->transit = transit; - if (d < 0) - d = -d; - - jitter += d - ((jitter + 8) >> 4); - s->jitter = jitter; - } -} - -PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size) -{ - pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; - rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1); - rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size ); -} - -static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc) -{ - pj_uint32_t expected; - pj_uint32_t u32; - pj_uint32_t expected_interval, received_interval, lost_interval; - pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; - - /* SSRC and last_seq */ - rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc); - rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L); - rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq; - rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq); - - /* Jitter */ - rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4); - - /* Total lost. */ - expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1; - u32 = expected - s->received; - rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF; - rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF; - rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF; - - /* Fraction lost calculation */ - expected_interval = expected - s->expected_prior; - s->expected_prior = expected; - - received_interval = s->received - s->received_prior; - s->received_prior = s->received; - - lost_interval = expected_interval - received_interval; - - if (expected_interval==0 || lost_interval == 0) { - rtcp_pkt->rr.fract_lost = 0; - } else { - rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval; - } -} - -PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len) -{ - pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt; - pj_rtcp_ntp_rec ntp; - pj_time_val now; - - rtcp_build_rtcp(session, session->peer_ssrc); - - /* Get current NTP time. */ - rtcp_get_ntp_time(&ntp); - - /* Fill in NTP timestamp in SR. */ - rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi); - rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo); - - if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) { - rtcp_pkt->rr.lsr = 0; - rtcp_pkt->rr.dlsr = 0; - } else { - unsigned msec_elapsed; - - /* Fill in LSR. - LSR is the middle 32bit of the last SR NTP time received. - */ - rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) | - ((session->rtcp_lsr.lo >> 16) & 0xFFFF); - rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr); - - /* Fill in DLSR. - DLSR is Delay since Last SR, in 1/65536 seconds. - */ - pj_gettimeofday(&now); - msec_elapsed = (now.msec - session->rtcp_lsr_time); - rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000); - } - - /* Return pointer. */ - *ret_p_pkt = rtcp_pkt; - *len = sizeof(pj_rtcp_pkt); -} - +/* $Header: /pjproject/pjmedia/src/pjmedia/rtcp.c 4 8/24/05 10:30a Bennylp $ */ + +#include +#include /* pj_gettimeofday */ +#include /* pj_htonx, pj_ntohx */ +#include /* memset */ + +#define RTCP_SR 200 +#define RTCP_RR 201 + + + +/* + * Get NTP time. + */ +static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp) +{ + pj_time_val tv; + + pj_gettimeofday(&tv); + + ntp->hi = tv.sec; + tv.msec = tv.msec % 1000; + ntp->lo = tv.msec * 0xFFFF / 1000; + ntp->lo <<= 16; +} + + +PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc) +{ + pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; + + memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt)); + + /* Init time */ + s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0; + s->rtcp_lsr_time = 0; + + /* Init common RTCP header */ + rtcp_pkt->common.version = 2; + rtcp_pkt->common.count = 1; + rtcp_pkt->common.pt = RTCP_SR; + rtcp_pkt->common.length = pj_htons(12); + + /* Init SR */ + rtcp_pkt->sr.ssrc = pj_htonl(ssrc); + + /* RR will be initialized on receipt of the first RTP packet. */ +} + +PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session) +{ + /* Nothing to do. */ + PJ_UNUSED_ARG(session) +} + +static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq) +{ + s->received = 0; + s->expected_prior = 0; + s->received_prior = 0; + s->transit = 0; + s->jitter = 0; + + pj_rtp_seq_restart(&s->seq_ctrl, seq); +} + +PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts) +{ + pj_uint32_t arrival; + pj_int32_t transit; + unsigned long timer_tick; + pj_time_val tv; + int status; + + /* Update sequence numbers (received, lost, etc). */ + status = pj_rtp_seq_update(&s->seq_ctrl, seq); + if (status == PJ_RTP_ERR_SESSION_RESTARTED) { + rtcp_init_seq(s, seq); + status = 0; + } + + if (status != 0) + return; + + ++s->received; + + pj_gettimeofday(&tv); + timer_tick = tv.sec * 1000 + tv.msec; + + /* + * Calculate jitter (s->jitter is in timer tick unit) + */ + PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE) + + arrival = timer_tick << 3; // 8 samples per ms. + transit = arrival - rtp_ts; + + if (s->transit == 0) { + s->transit = transit; + } else { + pj_int32_t d, jitter = s->jitter; + + d = transit - s->transit; + s->transit = transit; + if (d < 0) + d = -d; + + jitter += d - ((jitter + 8) >> 4); + s->jitter = jitter; + } +} + +PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size) +{ + pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; + rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1); + rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size ); +} + +static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc) +{ + pj_uint32_t expected; + pj_uint32_t u32; + pj_uint32_t expected_interval, received_interval, lost_interval; + pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt; + + /* SSRC and last_seq */ + rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc); + rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L); + rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq; + rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq); + + /* Jitter */ + rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4); + + /* Total lost. */ + expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1; + u32 = expected - s->received; + rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF; + rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF; + rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF; + + /* Fraction lost calculation */ + expected_interval = expected - s->expected_prior; + s->expected_prior = expected; + + received_interval = s->received - s->received_prior; + s->received_prior = s->received; + + lost_interval = expected_interval - received_interval; + + if (expected_interval==0 || lost_interval == 0) { + rtcp_pkt->rr.fract_lost = 0; + } else { + rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval; + } +} + +PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len) +{ + pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt; + pj_rtcp_ntp_rec ntp; + pj_time_val now; + + rtcp_build_rtcp(session, session->peer_ssrc); + + /* Get current NTP time. */ + rtcp_get_ntp_time(&ntp); + + /* Fill in NTP timestamp in SR. */ + rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi); + rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo); + + if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) { + rtcp_pkt->rr.lsr = 0; + rtcp_pkt->rr.dlsr = 0; + } else { + unsigned msec_elapsed; + + /* Fill in LSR. + LSR is the middle 32bit of the last SR NTP time received. + */ + rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) | + ((session->rtcp_lsr.lo >> 16) & 0xFFFF); + rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr); + + /* Fill in DLSR. + DLSR is Delay since Last SR, in 1/65536 seconds. + */ + pj_gettimeofday(&now); + msec_elapsed = (now.msec - session->rtcp_lsr_time); + rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000); + } + + /* Return pointer. */ + *ret_p_pkt = rtcp_pkt; + *len = sizeof(pj_rtcp_pkt); +} + diff --git a/pjmedia/src/pjmedia/rtcp.h b/pjmedia/src/pjmedia/rtcp.h index 750dcb3bb..1f5fa7389 100644 --- a/pjmedia/src/pjmedia/rtcp.h +++ b/pjmedia/src/pjmedia/rtcp.h @@ -1,176 +1,176 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/rtcp.h 5 8/24/05 10:30a Bennylp $ */ - -#ifndef __PJMEDIA_RTCP_H__ -#define __PJMEDIA_RTCP_H__ - -/** - * @file rtcp.h - * @brief RTCP implementation. - */ - -#include -#include - -PJ_BEGIN_DECL - - -/** - * @defgroup PJMED_RTCP RTCP - * @ingroup PJMEDIA - * @{ - */ - -/** - * RTCP sender report. - */ -struct pj_rtcp_sr -{ - pj_uint32_t ssrc; - pj_uint32_t ntp_sec; - pj_uint32_t ntp_frac; - pj_uint32_t rtp_ts; - pj_uint32_t sender_pcount; - pj_uint32_t sender_bcount; -}; - -typedef struct pj_rtcp_sr pj_rtcp_sr; - -/** - * RTCP receiver report. - */ -struct pj_rtcp_rr -{ - pj_uint32_t ssrc; -#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 - pj_uint32_t fract_lost:8; - pj_uint32_t total_lost_2:8; - pj_uint32_t total_lost_1:8; - pj_uint32_t total_lost_0:8; -#else - pj_uint32_t fract_lost:8; - pj_uint32_t total_lost_0:8; - pj_uint32_t total_lost_1:8; - pj_uint32_t total_lost_2:8; -#endif - pj_uint32_t last_seq; - pj_uint32_t jitter; - pj_uint32_t lsr; - pj_uint32_t dlsr; -}; - -typedef struct pj_rtcp_rr pj_rtcp_rr; - -/** - * RTCP common header. - */ -struct pj_rtcp_common -{ -#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 - unsigned version:2; /* packet type */ - unsigned p:1; /* padding flag */ - unsigned count:5; /* varies by payload type */ - unsigned pt:8; /* payload type */ -#else - unsigned count:5; /* varies by payload type */ - unsigned p:1; /* padding flag */ - unsigned version:2; /* packet type */ - unsigned pt:8; /* payload type */ -#endif - pj_uint16_t length; /* packet length */ -}; - -typedef struct pj_rtcp_common pj_rtcp_common; - -/** - * RTCP packet. - */ -struct pj_rtcp_pkt -{ - pj_rtcp_common common; - pj_rtcp_sr sr; - pj_rtcp_rr rr; /* variable-length list */ -}; - -typedef struct pj_rtcp_pkt pj_rtcp_pkt; - -/** - * NTP time representation. - */ -struct pj_rtcp_ntp_rec -{ - pj_uint32_t hi; - pj_uint32_t lo; -}; - -typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec; - -/** - * RTCP session. - */ -struct pj_rtcp_session -{ - pj_rtcp_pkt rtcp_pkt; - - pj_rtp_seq_session seq_ctrl; - - pj_uint32_t received; /* packets received */ - pj_uint32_t expected_prior; /* packet expected at last interval */ - pj_uint32_t received_prior; /* packet received at last interval */ - pj_int32_t transit; /* relative trans time for prev pkt */ - pj_uint32_t jitter; /* estimated jitter */ - - pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */ - unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */ - unsigned peer_ssrc; /* Peer SSRC */ - -}; - -typedef struct pj_rtcp_session pj_rtcp_session; - -/** - * Init RTCP session. - * @param session The session - * @param ssrc The SSRC used in to identify the session. - */ -PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc ); - -/** - * Deinit RTCP session. - * @param session The session. - */ -PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session); - -/** - * Call this function everytime an RTP packet is received to let the RTCP - * session do its internal calculations. - * @param session The session. - * @param seq The RTP packet sequence number, in host byte order. - * @param ts The RTP packet timestamp, in host byte order. - */ -PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts ); - -/** - * Call this function everytime an RTP packet is sent to let the RTCP session - * do its internal calculations. - * @param session The session. - * @param bytes_payload_size The payload size of the RTP packet (ie packet minus - * RTP header). - */ -PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size ); - -/** - * Build a RTCP SR/RR packet to be transmitted to remote RTP peer. - * @param session The session. - * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet. - * @param len [output] Upon return, it will indicate the size of the RTCP packet. - */ -PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len ); - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJMEDIA_RTCP_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/rtcp.h 5 8/24/05 10:30a Bennylp $ */ + +#ifndef __PJMEDIA_RTCP_H__ +#define __PJMEDIA_RTCP_H__ + +/** + * @file rtcp.h + * @brief RTCP implementation. + */ + +#include +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_RTCP RTCP + * @ingroup PJMEDIA + * @{ + */ + +/** + * RTCP sender report. + */ +struct pj_rtcp_sr +{ + pj_uint32_t ssrc; + pj_uint32_t ntp_sec; + pj_uint32_t ntp_frac; + pj_uint32_t rtp_ts; + pj_uint32_t sender_pcount; + pj_uint32_t sender_bcount; +}; + +typedef struct pj_rtcp_sr pj_rtcp_sr; + +/** + * RTCP receiver report. + */ +struct pj_rtcp_rr +{ + pj_uint32_t ssrc; +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + pj_uint32_t fract_lost:8; + pj_uint32_t total_lost_2:8; + pj_uint32_t total_lost_1:8; + pj_uint32_t total_lost_0:8; +#else + pj_uint32_t fract_lost:8; + pj_uint32_t total_lost_0:8; + pj_uint32_t total_lost_1:8; + pj_uint32_t total_lost_2:8; +#endif + pj_uint32_t last_seq; + pj_uint32_t jitter; + pj_uint32_t lsr; + pj_uint32_t dlsr; +}; + +typedef struct pj_rtcp_rr pj_rtcp_rr; + +/** + * RTCP common header. + */ +struct pj_rtcp_common +{ +#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0 + unsigned version:2; /* packet type */ + unsigned p:1; /* padding flag */ + unsigned count:5; /* varies by payload type */ + unsigned pt:8; /* payload type */ +#else + unsigned count:5; /* varies by payload type */ + unsigned p:1; /* padding flag */ + unsigned version:2; /* packet type */ + unsigned pt:8; /* payload type */ +#endif + pj_uint16_t length; /* packet length */ +}; + +typedef struct pj_rtcp_common pj_rtcp_common; + +/** + * RTCP packet. + */ +struct pj_rtcp_pkt +{ + pj_rtcp_common common; + pj_rtcp_sr sr; + pj_rtcp_rr rr; /* variable-length list */ +}; + +typedef struct pj_rtcp_pkt pj_rtcp_pkt; + +/** + * NTP time representation. + */ +struct pj_rtcp_ntp_rec +{ + pj_uint32_t hi; + pj_uint32_t lo; +}; + +typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec; + +/** + * RTCP session. + */ +struct pj_rtcp_session +{ + pj_rtcp_pkt rtcp_pkt; + + pj_rtp_seq_session seq_ctrl; + + pj_uint32_t received; /* packets received */ + pj_uint32_t expected_prior; /* packet expected at last interval */ + pj_uint32_t received_prior; /* packet received at last interval */ + pj_int32_t transit; /* relative trans time for prev pkt */ + pj_uint32_t jitter; /* estimated jitter */ + + pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */ + unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */ + unsigned peer_ssrc; /* Peer SSRC */ + +}; + +typedef struct pj_rtcp_session pj_rtcp_session; + +/** + * Init RTCP session. + * @param session The session + * @param ssrc The SSRC used in to identify the session. + */ +PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc ); + +/** + * Deinit RTCP session. + * @param session The session. + */ +PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session); + +/** + * Call this function everytime an RTP packet is received to let the RTCP + * session do its internal calculations. + * @param session The session. + * @param seq The RTP packet sequence number, in host byte order. + * @param ts The RTP packet timestamp, in host byte order. + */ +PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts ); + +/** + * Call this function everytime an RTP packet is sent to let the RTCP session + * do its internal calculations. + * @param session The session. + * @param bytes_payload_size The payload size of the RTP packet (ie packet minus + * RTP header). + */ +PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size ); + +/** + * Build a RTCP SR/RR packet to be transmitted to remote RTP peer. + * @param session The session. + * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet. + * @param len [output] Upon return, it will indicate the size of the RTCP packet. + */ +PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len ); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_RTCP_H__ */ diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c index 601c7d618..83d0e5e74 100644 --- a/pjmedia/src/pjmedia/rtp.c +++ b/pjmedia/src/pjmedia/rtp.c @@ -1,245 +1,245 @@ -/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/rtp.c 5 10/14/05 12:23a Bennylp $ */ - -#include -#include -#include /* pj_gettimeofday() */ -#include /* pj_htonx, pj_htonx */ -#include /* memset() */ - -#define THIS_FILE "rtp.c" - -#define RTP_VERSION 2 - -#define RTP_SEQ_MOD (1 << 16) -#define MAX_DROPOUT ((pj_int16_t)3000) -#define MAX_MISORDER ((pj_int16_t)100) -#define MIN_SEQUENTIAL ((pj_int16_t)2) - - -PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses, - int default_pt, pj_uint32_t sender_ssrc ) -{ - PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x", - ses, default_pt, sender_ssrc)); - - /* Check RTP header packing. */ - if (sizeof(struct pj_rtp_hdr) != 12) { - pj_assert(!"Wrong RTP header packing!"); - return PJ_RTP_ERR_RTP_PACKING; - } - - /* If sender_ssrc is not specified, create from time value. */ - if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) { - pj_time_val tv; - - pj_gettimeofday(&tv); - sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec); - } else { - sender_ssrc = pj_htonl(sender_ssrc); - } - - /* Initialize session. */ - ses->out_extseq = 0; - ses->peer_ssrc = 0; - - /* Sequence number will be initialized when the first RTP packet is receieved. */ - - /* Build default header for outgoing RTP packet. */ - memset(ses, 0, sizeof(*ses)); - ses->out_hdr.v = RTP_VERSION; - ses->out_hdr.p = 0; - ses->out_hdr.x = 0; - ses->out_hdr.cc = 0; - ses->out_hdr.m = 0; - ses->out_hdr.pt = (pj_uint8_t) default_pt; - ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq ); - ses->out_hdr.ts = 0; - ses->out_hdr.ssrc = sender_ssrc; - - /* Keep some arguments as session defaults. */ - ses->out_pt = (pj_uint16_t) default_pt; - - return PJ_SUCCESS; -} - - -PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m, - int payload_len, int ts_len, - const void **rtphdr, int *hdrlen ) -{ - PJ_UNUSED_ARG(payload_len) - - PJ_LOG(6, (THIS_FILE, - "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d", - ses, pt, m, payload_len, ts_len)); - - /* Update session. */ - ses->out_extseq++; - ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len); - - /* Create outgoing header. */ - ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt); - ses->out_hdr.m = (pj_uint16_t) m; - ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq); - - /* Return values */ - *rtphdr = &ses->out_hdr; - *hdrlen = sizeof(pj_rtp_hdr); - - return PJ_SUCCESS; -} - - -PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, - const void *pkt, int pkt_len, - const pj_rtp_hdr **hdr, - const void **payload, - unsigned *payloadlen) -{ - int offset; - - PJ_UNUSED_ARG(ses) - - PJ_LOG(6, (THIS_FILE, - "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d", - ses, pkt, pkt_len)); - - /* Assume RTP header at the start of packet. We'll verify this later. */ - *hdr = (pj_rtp_hdr*)pkt; - - /* Check RTP header sanity. */ - if ((*hdr)->v != RTP_VERSION) { - PJ_LOG(4, (THIS_FILE, " invalid RTP version!")); - return PJ_RTP_ERR_INVALID_VERSION; - } - - /* Payload is located right after header plus CSRC */ - offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t)); - - /* Adjust offset if RTP extension is used. */ - if ((*hdr)->x) { - pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset); - offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t)); - } - - /* Check that offset is less than packet size */ - if (offset >= pkt_len) - return PJ_RTP_ERR_INVALID_PACKET; - - /* Find and set payload. */ - *payload = ((pj_uint8_t*)pkt) + offset; - *payloadlen = pkt_len - offset; - - return PJ_SUCCESS; -} - - -PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr) -{ - int status; - - /* Check SSRC. */ - if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc); - /* - if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) { - PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)", - ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc)); - return PJ_RTP_ERR_INVALID_SSRC; - } - */ - - /* Check payload type. */ - if (hdr->pt != ses->out_pt) { - PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)", - ses, hdr->pt, ses->out_pt)); - return PJ_RTP_ERR_INVALID_PT; - } - - /* Initialize sequence number on first packet received. */ - if (ses->received == 0) - pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) ); - - /* Check sequence number to see if remote session has been restarted. */ - status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq)); - if (status == PJ_RTP_ERR_SESSION_RESTARTED) { - pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq)); - ++ses->received; - } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) { - ++ses->received; - } - - - return status; -} - - -void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq) -{ - sctrl->base_seq = seq; - sctrl->max_seq = seq; - sctrl->bad_seq = RTP_SEQ_MOD + 1; - sctrl->cycles = 0; -} - - -void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq) -{ - pj_rtp_seq_restart(sctrl, seq); - - sctrl->max_seq = (pj_uint16_t) (seq - 1); - sctrl->probation = MIN_SEQUENTIAL; -} - - -int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq) -{ - pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq); - - /* - * Source is not valid until MIN_SEQUENTIAL packets with - * sequential sequence numbers have been received. - */ - if (sctrl->probation) { - /* packet is in sequence */ - if (seq == sctrl->max_seq+ 1) { - sctrl->probation--; - sctrl->max_seq = seq; - if (sctrl->probation == 0) { - return PJ_RTP_ERR_SESSION_RESTARTED; - } - } else { - sctrl->probation = MIN_SEQUENTIAL - 1; - sctrl->max_seq = seq; - } - return PJ_RTP_ERR_SESSION_PROBATION; - - } else if (udelta < MAX_DROPOUT) { - /* in order, with permissible gap */ - if (seq < sctrl->max_seq) { - /* Sequence number wrapped - count another 64K cycle. */ - sctrl->cycles += RTP_SEQ_MOD; - } - sctrl->max_seq = seq; - - } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) { - /* the sequence number made a very large jump */ - if (seq == sctrl->bad_seq) { - /* - * Two sequential packets -- assume that the other side - * restarted without telling us so just re-sync - * (i.e., pretend this was the first packet). - */ - return PJ_RTP_ERR_SESSION_RESTARTED; - } - else { - sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); - return PJ_RTP_ERR_BAD_SEQUENCE; - } - } else { - /* duplicate or reordered packet */ - } - - return 0; -} - - +/* $Header: /pjproject-0.3/pjmedia/src/pjmedia/rtp.c 5 10/14/05 12:23a Bennylp $ */ + +#include +#include +#include /* pj_gettimeofday() */ +#include /* pj_htonx, pj_htonx */ +#include /* memset() */ + +#define THIS_FILE "rtp.c" + +#define RTP_VERSION 2 + +#define RTP_SEQ_MOD (1 << 16) +#define MAX_DROPOUT ((pj_int16_t)3000) +#define MAX_MISORDER ((pj_int16_t)100) +#define MIN_SEQUENTIAL ((pj_int16_t)2) + + +PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses, + int default_pt, pj_uint32_t sender_ssrc ) +{ + PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x", + ses, default_pt, sender_ssrc)); + + /* Check RTP header packing. */ + if (sizeof(struct pj_rtp_hdr) != 12) { + pj_assert(!"Wrong RTP header packing!"); + return PJ_RTP_ERR_RTP_PACKING; + } + + /* If sender_ssrc is not specified, create from time value. */ + if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) { + pj_time_val tv; + + pj_gettimeofday(&tv); + sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec); + } else { + sender_ssrc = pj_htonl(sender_ssrc); + } + + /* Initialize session. */ + ses->out_extseq = 0; + ses->peer_ssrc = 0; + + /* Sequence number will be initialized when the first RTP packet is receieved. */ + + /* Build default header for outgoing RTP packet. */ + memset(ses, 0, sizeof(*ses)); + ses->out_hdr.v = RTP_VERSION; + ses->out_hdr.p = 0; + ses->out_hdr.x = 0; + ses->out_hdr.cc = 0; + ses->out_hdr.m = 0; + ses->out_hdr.pt = (pj_uint8_t) default_pt; + ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq ); + ses->out_hdr.ts = 0; + ses->out_hdr.ssrc = sender_ssrc; + + /* Keep some arguments as session defaults. */ + ses->out_pt = (pj_uint16_t) default_pt; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m, + int payload_len, int ts_len, + const void **rtphdr, int *hdrlen ) +{ + PJ_UNUSED_ARG(payload_len) + + PJ_LOG(6, (THIS_FILE, + "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d", + ses, pt, m, payload_len, ts_len)); + + /* Update session. */ + ses->out_extseq++; + ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len); + + /* Create outgoing header. */ + ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt); + ses->out_hdr.m = (pj_uint16_t) m; + ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq); + + /* Return values */ + *rtphdr = &ses->out_hdr; + *hdrlen = sizeof(pj_rtp_hdr); + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, + const void *pkt, int pkt_len, + const pj_rtp_hdr **hdr, + const void **payload, + unsigned *payloadlen) +{ + int offset; + + PJ_UNUSED_ARG(ses) + + PJ_LOG(6, (THIS_FILE, + "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d", + ses, pkt, pkt_len)); + + /* Assume RTP header at the start of packet. We'll verify this later. */ + *hdr = (pj_rtp_hdr*)pkt; + + /* Check RTP header sanity. */ + if ((*hdr)->v != RTP_VERSION) { + PJ_LOG(4, (THIS_FILE, " invalid RTP version!")); + return PJ_RTP_ERR_INVALID_VERSION; + } + + /* Payload is located right after header plus CSRC */ + offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t)); + + /* Adjust offset if RTP extension is used. */ + if ((*hdr)->x) { + pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset); + offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t)); + } + + /* Check that offset is less than packet size */ + if (offset >= pkt_len) + return PJ_RTP_ERR_INVALID_PACKET; + + /* Find and set payload. */ + *payload = ((pj_uint8_t*)pkt) + offset; + *payloadlen = pkt_len - offset; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr) +{ + int status; + + /* Check SSRC. */ + if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc); + /* + if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) { + PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)", + ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc)); + return PJ_RTP_ERR_INVALID_SSRC; + } + */ + + /* Check payload type. */ + if (hdr->pt != ses->out_pt) { + PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)", + ses, hdr->pt, ses->out_pt)); + return PJ_RTP_ERR_INVALID_PT; + } + + /* Initialize sequence number on first packet received. */ + if (ses->received == 0) + pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) ); + + /* Check sequence number to see if remote session has been restarted. */ + status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq)); + if (status == PJ_RTP_ERR_SESSION_RESTARTED) { + pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq)); + ++ses->received; + } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) { + ++ses->received; + } + + + return status; +} + + +void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq) +{ + sctrl->base_seq = seq; + sctrl->max_seq = seq; + sctrl->bad_seq = RTP_SEQ_MOD + 1; + sctrl->cycles = 0; +} + + +void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq) +{ + pj_rtp_seq_restart(sctrl, seq); + + sctrl->max_seq = (pj_uint16_t) (seq - 1); + sctrl->probation = MIN_SEQUENTIAL; +} + + +int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq) +{ + pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq); + + /* + * Source is not valid until MIN_SEQUENTIAL packets with + * sequential sequence numbers have been received. + */ + if (sctrl->probation) { + /* packet is in sequence */ + if (seq == sctrl->max_seq+ 1) { + sctrl->probation--; + sctrl->max_seq = seq; + if (sctrl->probation == 0) { + return PJ_RTP_ERR_SESSION_RESTARTED; + } + } else { + sctrl->probation = MIN_SEQUENTIAL - 1; + sctrl->max_seq = seq; + } + return PJ_RTP_ERR_SESSION_PROBATION; + + } else if (udelta < MAX_DROPOUT) { + /* in order, with permissible gap */ + if (seq < sctrl->max_seq) { + /* Sequence number wrapped - count another 64K cycle. */ + sctrl->cycles += RTP_SEQ_MOD; + } + sctrl->max_seq = seq; + + } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) { + /* the sequence number made a very large jump */ + if (seq == sctrl->bad_seq) { + /* + * Two sequential packets -- assume that the other side + * restarted without telling us so just re-sync + * (i.e., pretend this was the first packet). + */ + return PJ_RTP_ERR_SESSION_RESTARTED; + } + else { + sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); + return PJ_RTP_ERR_BAD_SEQUENCE; + } + } else { + /* duplicate or reordered packet */ + } + + return 0; +} + + diff --git a/pjmedia/src/pjmedia/rtp.h b/pjmedia/src/pjmedia/rtp.h index e10b561c0..b38953afc 100644 --- a/pjmedia/src/pjmedia/rtp.h +++ b/pjmedia/src/pjmedia/rtp.h @@ -1,240 +1,240 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/rtp.h 6 8/24/05 10:30a Bennylp $ */ - -#ifndef __PJMEDIA_RTP_H__ -#define __PJMEDIA_RTP_H__ - -#include - -/** - * @file rtp.h - * @brief RTP implementation. - */ - -PJ_BEGIN_DECL - - -/** - * @defgroup PJMED_RTP RTP - * @ingroup PJMEDIA - * @{ - * - * The RTP module is designed to be dependent only to PJLIB, it does not depend - * on any other parts of PJMEDIA library. The RTP module does not even depend - * on any transports (sockets), to promote even more use. - * - * An RTCP implementation is also separated from this module. - * - * The functions that are provided by this module: - * - creating RTP header for each outgoing packet. - * - decoding RTP packet into RTP header and payload. - * - provide simple RTP session management (sequence number, etc.) - * - * The RTP module does not use any dynamic memory at all. - * - * \section P1 How to Use the RTP Module - * - * First application must call #pj_rtp_session_init to initialize the RTP - * session. - * - * When application wants to send RTP packet, it needs to call - * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build - * the complete RTP packet, but instead only the header. Application can - * then either concatenate the header with the payload, or send the two - * fragments (the header and the payload) using scatter-gather transport API - * (e.g. \a sendv()). - * - * When application receives an RTP packet, first it should call - * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call - * #pj_rtp_session_update to check whether we can process the RTP payload, - * and to let the RTP session updates its internal status. The decode function - * is guaranteed to point the payload to the correct position regardless of - * any options present in the RTP packet. - * - */ - - -#ifdef _MSC_VER -# pragma warning ( disable : 4214 ) -#endif - - -/** - * Error codes. - */ -enum pj_rtp_error_t -{ - PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */ - PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */ - PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */ - PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */ - PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */ - PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */ - PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */ - PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */ -}; - -#pragma pack(1) -/** - * RTP packet header. - */ -struct pj_rtp_hdr -{ -#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0) - pj_uint16_t v:2; /**< packet type/version */ - pj_uint16_t p:1; /**< padding flag */ - pj_uint16_t x:1; /**< extension flag */ - pj_uint16_t cc:4; /**< CSRC count */ - pj_uint16_t m:1; /**< marker bit */ - pj_uint16_t pt:7; /**< payload type */ -#else - pj_uint16_t cc:4; /**< CSRC count */ - pj_uint16_t x:1; /**< header extension flag */ - pj_uint16_t p:1; /**< padding flag */ - pj_uint16_t v:2; /**< packet type/version */ - pj_uint16_t pt:7; /**< payload type */ - pj_uint16_t m:1; /**< marker bit */ -#endif - pj_uint16_t seq; /**< sequence number */ - pj_uint32_t ts; /**< timestamp */ - pj_uint32_t ssrc; /**< synchronization source */ -}; -#pragma pack() - -typedef struct pj_rtp_hdr pj_rtp_hdr; - -/** - * RTP extendsion header. - */ -struct pj_rtp_ext_hdr -{ - pj_uint16_t profile_data; - pj_uint16_t length; -}; - -typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr; - -/** - * A generic sequence number management, used by both RTP and RTCP. - */ -struct pj_rtp_seq_session -{ - pj_uint16_t max_seq; /**< highest sequence number heard */ - pj_uint32_t cycles; /**< shifted count of seq. number cycles */ - pj_uint32_t base_seq; /**< base seq number */ - pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */ - pj_uint32_t probation; /**< sequ. packets till source is valid */ -}; - -typedef struct pj_rtp_seq_session pj_rtp_seq_session; - -/** - * RTP session descriptor. - */ -struct pj_rtp_session -{ - pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */ - pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */ - pj_uint16_t out_pt; /**< Default outgoing payload type. */ - pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */ - pj_uint32_t peer_ssrc; /**< Peer SSRC. */ - pj_uint32_t received; /**< Number of received packets. */ -}; - -typedef struct pj_rtp_session pj_rtp_session; - -/** - * \brief Initialize RTP session. - * This function will initialize the RTP session according to given parameters. - * - * @param ses The session. - * @param default_pt Default payload type. - * @param sender_ssrc SSRC used for outgoing packets. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses, - int default_pt, pj_uint32_t sender_ssrc ); - -/** - * \brief Encode outgoing RTP packet header. - * Create the RTP header based on arguments and current state of the RTP - * session. - * - * @param ses The session. - * @param pt Payload type. - * @param m Marker flag. - * @param payload_len Payload length in bytes. - * @param ts_len Timestamp length. - * @param rtphdr Upon return will point to RTP packet header. - * @param hdrlen Upon return will indicate the size of RTP packet header - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m, - int payload_len, int ts_len, - const void **rtphdr, int *hdrlen ); - -/** - * \brief Decode an incoming RTP packet. - * This function will decode incoming packet into RTP header and payload. - * The decode function is guaranteed to point the payload to the correct - * position regardless of any options present in the RTP packet. - * - * @param ses The session. - * @param pkt The received RTP packet. - * @param pkt_len The length of the packet. - * @param hdr Upon return will point to the location of the RTP header - * inside the packet. - * @param payload Upon return will point to the location of the - * payload inside the packet. - * @param payloadlen Upon return will indicate the size of the payload. - * - * @return zero if successfull. - */ -PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, - const void *pkt, int pkt_len, - const pj_rtp_hdr **hdr, - const void **payload, - unsigned *payloadlen); - -/** - * \brief Update RTP session with an incoming RTP packet. - * Call this function everytime - * an RTP packet is received to check whether the packet can be received and to - * let the RTP session performs its internal calculations. - * - * @param ses The session. - * @param hdr The RTP header of the incoming packet. - * - * @return zero if the packet is valid and can be processed, otherwise will - * return one of the error in #pj_rtp_error_t. - */ -PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, - const pj_rtp_hdr *hdr); - -/** -* \brief Internal. - * Internal function for sequence control, shared by RTCP implementation. - */ -void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); - -/** -* \brief Internal. - * Internal function for sequence control, shared by RTCP implementation. - */ -void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); - -/** -* \brief Internal. - * Internal function for sequence control, shared by RTCP implementation. - */ -int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJMEDIA_RTP_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/rtp.h 6 8/24/05 10:30a Bennylp $ */ + +#ifndef __PJMEDIA_RTP_H__ +#define __PJMEDIA_RTP_H__ + +#include + +/** + * @file rtp.h + * @brief RTP implementation. + */ + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_RTP RTP + * @ingroup PJMEDIA + * @{ + * + * The RTP module is designed to be dependent only to PJLIB, it does not depend + * on any other parts of PJMEDIA library. The RTP module does not even depend + * on any transports (sockets), to promote even more use. + * + * An RTCP implementation is also separated from this module. + * + * The functions that are provided by this module: + * - creating RTP header for each outgoing packet. + * - decoding RTP packet into RTP header and payload. + * - provide simple RTP session management (sequence number, etc.) + * + * The RTP module does not use any dynamic memory at all. + * + * \section P1 How to Use the RTP Module + * + * First application must call #pj_rtp_session_init to initialize the RTP + * session. + * + * When application wants to send RTP packet, it needs to call + * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build + * the complete RTP packet, but instead only the header. Application can + * then either concatenate the header with the payload, or send the two + * fragments (the header and the payload) using scatter-gather transport API + * (e.g. \a sendv()). + * + * When application receives an RTP packet, first it should call + * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call + * #pj_rtp_session_update to check whether we can process the RTP payload, + * and to let the RTP session updates its internal status. The decode function + * is guaranteed to point the payload to the correct position regardless of + * any options present in the RTP packet. + * + */ + + +#ifdef _MSC_VER +# pragma warning ( disable : 4214 ) +#endif + + +/** + * Error codes. + */ +enum pj_rtp_error_t +{ + PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */ + PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */ + PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */ + PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */ + PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */ + PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */ + PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */ + PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */ +}; + +#pragma pack(1) +/** + * RTP packet header. + */ +struct pj_rtp_hdr +{ +#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0) + pj_uint16_t v:2; /**< packet type/version */ + pj_uint16_t p:1; /**< padding flag */ + pj_uint16_t x:1; /**< extension flag */ + pj_uint16_t cc:4; /**< CSRC count */ + pj_uint16_t m:1; /**< marker bit */ + pj_uint16_t pt:7; /**< payload type */ +#else + pj_uint16_t cc:4; /**< CSRC count */ + pj_uint16_t x:1; /**< header extension flag */ + pj_uint16_t p:1; /**< padding flag */ + pj_uint16_t v:2; /**< packet type/version */ + pj_uint16_t pt:7; /**< payload type */ + pj_uint16_t m:1; /**< marker bit */ +#endif + pj_uint16_t seq; /**< sequence number */ + pj_uint32_t ts; /**< timestamp */ + pj_uint32_t ssrc; /**< synchronization source */ +}; +#pragma pack() + +typedef struct pj_rtp_hdr pj_rtp_hdr; + +/** + * RTP extendsion header. + */ +struct pj_rtp_ext_hdr +{ + pj_uint16_t profile_data; + pj_uint16_t length; +}; + +typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr; + +/** + * A generic sequence number management, used by both RTP and RTCP. + */ +struct pj_rtp_seq_session +{ + pj_uint16_t max_seq; /**< highest sequence number heard */ + pj_uint32_t cycles; /**< shifted count of seq. number cycles */ + pj_uint32_t base_seq; /**< base seq number */ + pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */ + pj_uint32_t probation; /**< sequ. packets till source is valid */ +}; + +typedef struct pj_rtp_seq_session pj_rtp_seq_session; + +/** + * RTP session descriptor. + */ +struct pj_rtp_session +{ + pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */ + pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */ + pj_uint16_t out_pt; /**< Default outgoing payload type. */ + pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */ + pj_uint32_t peer_ssrc; /**< Peer SSRC. */ + pj_uint32_t received; /**< Number of received packets. */ +}; + +typedef struct pj_rtp_session pj_rtp_session; + +/** + * \brief Initialize RTP session. + * This function will initialize the RTP session according to given parameters. + * + * @param ses The session. + * @param default_pt Default payload type. + * @param sender_ssrc SSRC used for outgoing packets. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses, + int default_pt, pj_uint32_t sender_ssrc ); + +/** + * \brief Encode outgoing RTP packet header. + * Create the RTP header based on arguments and current state of the RTP + * session. + * + * @param ses The session. + * @param pt Payload type. + * @param m Marker flag. + * @param payload_len Payload length in bytes. + * @param ts_len Timestamp length. + * @param rtphdr Upon return will point to RTP packet header. + * @param hdrlen Upon return will indicate the size of RTP packet header + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m, + int payload_len, int ts_len, + const void **rtphdr, int *hdrlen ); + +/** + * \brief Decode an incoming RTP packet. + * This function will decode incoming packet into RTP header and payload. + * The decode function is guaranteed to point the payload to the correct + * position regardless of any options present in the RTP packet. + * + * @param ses The session. + * @param pkt The received RTP packet. + * @param pkt_len The length of the packet. + * @param hdr Upon return will point to the location of the RTP header + * inside the packet. + * @param payload Upon return will point to the location of the + * payload inside the packet. + * @param payloadlen Upon return will indicate the size of the payload. + * + * @return zero if successfull. + */ +PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses, + const void *pkt, int pkt_len, + const pj_rtp_hdr **hdr, + const void **payload, + unsigned *payloadlen); + +/** + * \brief Update RTP session with an incoming RTP packet. + * Call this function everytime + * an RTP packet is received to check whether the packet can be received and to + * let the RTP session performs its internal calculations. + * + * @param ses The session. + * @param hdr The RTP header of the incoming packet. + * + * @return zero if the packet is valid and can be processed, otherwise will + * return one of the error in #pj_rtp_error_t. + */ +PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, + const pj_rtp_hdr *hdr); + +/** +* \brief Internal. + * Internal function for sequence control, shared by RTCP implementation. + */ +void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); + +/** +* \brief Internal. + * Internal function for sequence control, shared by RTCP implementation. + */ +void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); + +/** +* \brief Internal. + * Internal function for sequence control, shared by RTCP implementation. + */ +int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_RTP_H__ */ diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c index 43b47d1da..ad1546f33 100644 --- a/pjmedia/src/pjmedia/sdp.c +++ b/pjmedia/src/pjmedia/sdp.c @@ -1,934 +1,934 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/sdp.c 9 6/12/05 11:36a Bennylp $ */ - - -#include -#include -#include -#include -#include -#include -#include - -enum { - SKIP_WS = 0, - SYNTAX_ERROR = 1, -}; -#define TOKEN "-.!%*_=`'~" -#define NTP_OFFSET ((pj_uint32_t)2208988800) -#define LOG_THIS "sdp" - -/* - * Prototypes for line parser. - */ -static void parse_version(pj_scanner *scanner); -static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses); -static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses); -static void parse_generic_line(pj_scanner *scanner, pj_str_t *str); -static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn); -static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner); -static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med); - -/* - * Prototypes for attribute parsers. - */ -static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner ); -static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner ); -static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner ); -static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner ); -static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner ); - - -/* - * Prototypes for functions to print attribute. - * All of them returns integer for the length printed, or -1 on error. - */ -static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr, - char *buf, int length); -static int print_generic_string_attr(const pjsdp_attr_string *attr, - char *buf, int length); -static int print_generic_num_attr(const pjsdp_attr_num *attr, - char *buf, int length); -static int print_name_only_attr(const pjsdp_attr *attr, - char *buf, int length); -static int print_fmtp_attr(const pjsdp_fmtp_attr *attr, - char *buf, int length); - -/* - * Prototypes for cloning attributes. - */ -static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs); -static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs); -static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs); -static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs); -static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs); - - -/* - * Prototypes - */ -static void init_sdp_parser(void); - - -typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner); -typedef int (*FPRINT)(const void *attr, char *buf, int length); -typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs); - -/* - * Array of functions to print attribute. - */ -static struct attr_map_rec -{ - pj_str_t name; - FPARSE parse_attr; - FPRINT print_attr; - FCLONE clone; -} attr_map[] = -{ - {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr}, - {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr}, - {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, - {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, - {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, - {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, - {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr}, - {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr}, - {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, - {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr} -}; - -/* - * Scanner character specification. - */ -static int is_initialized; -static pj_char_spec cs_token; - -static void init_sdp_parser(void) -{ - if (is_initialized == 0) { - is_initialized = 1; - if (is_initialized != 1) { - return; - } - } - pj_cs_add_alpha(cs_token); - pj_cs_add_num(cs_token); - pj_cs_add_str( cs_token, TOKEN); -} - -static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap, - char *buf, int len) -{ - char *p = buf; - - if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) { - return -1; - } - - /* colon and payload type. */ - *p++ = ':'; - len = pj_utoa(rtpmap->payload_type, p); - p += len; - - /* space, encoding name */ - *p++ = ' '; - pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen); - p += rtpmap->encoding_name.slen; - - /* slash, clock-rate. */ - *p++ = '/'; - len = pj_utoa(rtpmap->clock_rate, p); - p += len; - - /* optionally add encoding parameter. */ - if (rtpmap->parameter.slen) { - *p++ = '/'; - pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen); - p += rtpmap->parameter.slen; - } - - return p-buf; -} - -static int print_generic_string_attr(const pjsdp_attr_string *attr, - char *buf, int len) -{ - char *p = buf; - - if (len < attr->value.slen + 4) { - return -1; - } - - /* colon and attribute value. */ - *p++ = ':'; - pj_memcpy(p, attr->value.ptr, attr->value.slen); - p += attr->value.slen; - - return p-buf; -} - -static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len) -{ - char *p = buf; - - if (len < 10) { - return -1; - } - *p++ = ':'; - return pj_utoa(attr->value, p); -} - -static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len) -{ - PJ_UNUSED_ARG(attr) - PJ_UNUSED_ARG(buf) - PJ_UNUSED_ARG(len) - return 0; -} - -static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len) -{ - char *p = buf; - - if (len < 4+fmtp->format.slen+fmtp->param.slen) { - return -1; - } - - /* colon and format. */ - *p++ = ':'; - pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen); - p += fmtp->format.slen; - - /* space and parameter. */ - *p++ = ' '; - pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen); - p += fmtp->param.slen; - - return p-buf; -} - - -static int print_attr(const pjsdp_attr *attr, char *buf, int len) -{ - char *p = buf; - struct attr_map_rec *desc = &attr_map[attr->type]; - - if (len < 16) { - return -1; - } - - *p++ = 'a'; - *p++ = '='; - pj_memcpy(p, desc->name.ptr, desc->name.slen); - p += desc->name.slen; - - len = (*desc->print_attr)(attr, p, (buf+len)-p); - if (len < 0) { - return -1; - } - p += len; - *p++ = '\r'; - *p++ = '\n'; - return p-buf; -} - -static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p) -{ - const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p; - pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr)); - if (!attr) - return NULL; - - attr->type = rhs->type; - attr->payload_type = rhs->payload_type; - if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL; - attr->clock_rate = rhs->clock_rate; - if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL; - - return (pjsdp_attr*)attr; -} - -static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p) -{ - const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p; - pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string)); - if (!attr) - return NULL; - - attr->type = rhs->type; - if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL; - - return (pjsdp_attr*)attr; -} - -static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p) -{ - const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p; - pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num)); - if (!attr) - return NULL; - - attr->type = rhs->type; - attr->value = rhs->value; - - return (pjsdp_attr*)attr; -} - -static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs) -{ - pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr)); - if (!attr) - return NULL; - - attr->type = rhs->type; - return attr; -} - -static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p) -{ - const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p; - pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr)); - if (!attr) - return NULL; - - attr->type = rhs->type; - if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL; - if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL; - - return (pjsdp_attr*)attr; -} - -PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs) -{ - struct attr_map_rec *desc; - - if (rhs->type >= PJSDP_END_OF_ATTR) { - pj_assert(0); - return NULL; - } - - desc = &attr_map[rhs->type]; - return (*desc->clone) (pool, rhs); -} - -PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type) -{ - int i; - - for (i=0; itype == type) - return attr_array[i]; - } - return NULL; -} - -static int print_connection_info( pjsdp_conn_info *c, char *buf, int len) -{ - char *p = buf; - - if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) { - return -1; - } - *p++ = 'c'; - *p++ = '='; - pj_memcpy(p, c->net_type.ptr, c->net_type.slen); - p += c->net_type.slen; - *p++ = ' '; - pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen); - p += c->addr_type.slen; - *p++ = ' '; - pj_memcpy(p, c->addr.ptr, c->addr.slen); - p += c->addr.slen; - *p++ = '\r'; - *p++ = '\n'; - - return p-buf; -} - -PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs) -{ - pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); - if (!c) return NULL; - - if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL; - if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL; - if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL; - - return c; -} - -static int print_media_desc( pjsdp_media_desc *m, char *buf, int len) -{ - char *p = buf; - char *end = buf+len; - unsigned i; - int printed; - - /* check length for the "m=" line. */ - if (len < m->desc.media.slen+m->desc.transport.slen+12+24) { - return -1; - } - *p++ = 'm'; /* m= */ - *p++ = '='; - pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen); - p += m->desc.media.slen; - *p++ = ' '; - printed = pj_utoa(m->desc.port, p); - p += printed; - if (m->desc.port_count > 1) { - *p++ = '/'; - printed = pj_utoa(m->desc.port_count, p); - p += printed; - } - *p++ = ' '; - pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen); - p += m->desc.transport.slen; - for (i=0; idesc.fmt_count; ++i) { - *p++ = ' '; - pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen); - p += m->desc.fmt[i].slen; - } - *p++ = '\r'; - *p++ = '\n'; - - /* print connection info, if present. */ - if (m->conn) { - printed = print_connection_info(m->conn, p, end-p); - if (printed < 0) { - return -1; - } - p += printed; - } - - /* print attributes. */ - for (i=0; iattr_count; ++i) { - printed = print_attr(m->attr[i], p, end-p); - if (printed < 0) { - return -1; - } - p += printed; - } - - return p-buf; -} - -PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, - const pjsdp_media_desc *rhs) -{ - unsigned int i; - pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc)); - if (!m) - return NULL; - - pj_strdup (pool, &m->desc.media, &rhs->desc.media); - m->desc.port = rhs->desc.port; - m->desc.port_count = rhs->desc.port_count; - pj_strdup (pool, &m->desc.transport, &rhs->desc.transport); - m->desc.fmt_count = rhs->desc.fmt_count; - for (i=0; idesc.fmt_count; ++i) - m->desc.fmt[i] = rhs->desc.fmt[i]; - - if (rhs->conn) { - m->conn = pjsdp_conn_info_clone (pool, rhs->conn); - if (!m->conn) - return NULL; - } else { - m->conn = NULL; - } - - m->attr_count = rhs->attr_count; - for (i=0; i < rhs->attr_count; ++i) { - m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]); - if (!m->attr[i]) - return NULL; - } - - return m; -} - -/** Check if the media description has the specified attribute. */ -PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, - pjsdp_attr_type_e attr_type) -{ - unsigned i; - for (i=0; iattr_count; ++i) { - pjsdp_attr *attr = m->attr[i]; - if (attr->type == attr_type) - return 1; - } - return 0; -} - -/** Find rtpmap attribute for the specified payload type. */ -PJ_DEF(const pjsdp_rtpmap_attr*) -pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt) -{ - unsigned i; - for (i=0; iattr_count; ++i) { - pjsdp_attr *attr = m->attr[i]; - if (attr->type == PJSDP_ATTR_RTPMAP) { - const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr; - if (rtpmap->payload_type == pt) - return rtpmap; - } - } - return NULL; -} - - -static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len) -{ - char *p = buf; - char *end = buf+len; - unsigned i; - int printed; - - /* Check length for v= and o= lines. */ - if (len < 5+ - 2+ses->origin.user.slen+18+ - ses->origin.net_type.slen+ses->origin.addr.slen + 2) - { - return -1; - } - - /* SDP version (v= line) */ - pj_memcpy(p, "v=0\r\n", 5); - p += 5; - - /* Owner (o=) line. */ - *p++ = 'o'; - *p++ = '='; - pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen); - p += ses->origin.user.slen; - *p++ = ' '; - printed = pj_utoa(ses->origin.id, p); - p += printed; - *p++ = ' '; - printed = pj_utoa(ses->origin.version, p); - p += printed; - *p++ = ' '; - pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen); - p += ses->origin.net_type.slen; - *p++ = ' '; - pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen); - p += ses->origin.addr_type.slen; - *p++ = ' '; - pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen); - p += ses->origin.addr.slen; - *p++ = '\r'; - *p++ = '\n'; - - /* Session name (s=) line. */ - if ((end-p) < 8+ses->name.slen) { - return -1; - } - *p++ = 's'; - *p++ = '='; - pj_memcpy(p, ses->name.ptr, ses->name.slen); - p += ses->name.slen; - *p++ = '\r'; - *p++ = '\n'; - - /* Time */ - if ((end-p) < 24) { - return -1; - } - *p++ = 't'; - *p++ = '='; - printed = pj_utoa(ses->time.start, p); - p += printed; - *p++ = ' '; - printed = pj_utoa(ses->time.stop, p); - p += printed; - *p++ = '\r'; - *p++ = '\n'; - - /* Connection line (c=) if exist. */ - if (ses->conn) { - printed = print_connection_info(ses->conn, p, end-p); - if (printed < 1) { - return -1; - } - p += printed; - } - - /* Print all attribute (a=) lines. */ - for (i=0; iattr_count; ++i) { - printed = print_attr(ses->attr[i], p, end-p); - if (printed < 0) { - return -1; - } - p += printed; - } - - /* Print media (m=) lines. */ - for (i=0; imedia_count; ++i) { - printed = print_media_desc(ses->media[i], p, end-p); - if (printed < 0) { - return -1; - } - p += printed; - } - - return p-buf; -} - -/****************************************************************************** - * PARSERS - */ - -static void parse_version(pj_scanner *scanner) -{ - pj_scan_advance_n(scanner, 3, SKIP_WS); - pj_scan_get_newline(scanner); -} - -static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses) -{ - pj_str_t str; - - /* o= */ - pj_scan_advance_n(scanner, 2, SKIP_WS); - - /* username. */ - pj_scan_get_until_ch(scanner, ' ', &ses->origin.user); - pj_scan_get_char(scanner); - - /* id */ - pj_scan_get_until_ch(scanner, ' ', &str); - ses->origin.id = pj_strtoul(&str); - pj_scan_get_char(scanner); - - /* version */ - pj_scan_get_until_ch(scanner, ' ', &str); - ses->origin.version = pj_strtoul(&str); - pj_scan_get_char(scanner); - - /* network-type */ - pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type); - pj_scan_get_char(scanner); - - /* addr-type */ - pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type); - pj_scan_get_char(scanner); - - /* address */ - pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr); - - /* newline */ - pj_scan_get_newline(scanner); -} - -static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses) -{ - pj_str_t str; - - /* t= */ - pj_scan_advance_n(scanner, 2, SKIP_WS); - - /* start time */ - pj_scan_get_until_ch(scanner, ' ', &str); - ses->time.start = pj_strtoul(&str); - - pj_scan_get_char(scanner); - - /* stop time */ - pj_scan_get_until_ch(scanner, '\r', &str); - ses->time.stop = pj_strtoul(&str); - - /* newline */ - pj_scan_get_newline(scanner); -} - -static void parse_generic_line(pj_scanner *scanner, pj_str_t *str) -{ - /* x= */ - pj_scan_advance_n(scanner, 2, SKIP_WS); - - /* get anything until newline. */ - pj_scan_get_until_ch(scanner, '\r', str); - - /* newline. */ - pj_scan_get_newline(scanner); -} - -static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn) -{ - /* c= */ - pj_scan_advance_n(scanner, 2, SKIP_WS); - - /* network-type */ - pj_scan_get_until_ch(scanner, ' ', &conn->net_type); - pj_scan_get_char(scanner); - - /* addr-type */ - pj_scan_get_until_ch(scanner, ' ', &conn->addr_type); - pj_scan_get_char(scanner); - - /* address. */ - pj_scan_get_until_ch(scanner, '\r', &conn->addr); - - /* newline */ - pj_scan_get_newline(scanner); -} - -static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med) -{ - pj_str_t str; - - /* m= */ - pj_scan_advance_n(scanner, 2, SKIP_WS); - - /* type */ - pj_scan_get_until_ch(scanner, ' ', &med->desc.media); - pj_scan_get_char(scanner); - - /* port */ - pj_scan_get(scanner, cs_token, &str); - med->desc.port = (unsigned short)pj_strtoul(&str); - if (*scanner->current == '/') { - /* port count */ - pj_scan_get_char(scanner); - pj_scan_get(scanner, cs_token, &str); - med->desc.port_count = pj_strtoul(&str); - - } else { - med->desc.port_count = 0; - } - - if (pj_scan_get_char(scanner) != ' ') { - PJ_THROW(SYNTAX_ERROR); - } - - /* transport */ - pj_scan_get_until_ch(scanner, ' ', &med->desc.transport); - - /* format list */ - med->desc.fmt_count = 0; - while (*scanner->current == ' ') { - pj_scan_get_char(scanner); - pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]); - } - - /* newline */ - pj_scan_get_newline(scanner); -} - -static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner ) -{ - pjsdp_rtpmap_attr *rtpmap; - pj_str_t str; - - rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap)); - if (pj_scan_get_char(scanner) != ':') { - PJ_THROW(SYNTAX_ERROR); - } - pj_scan_get_until_ch(scanner, ' ', &str); - rtpmap->payload_type = pj_strtoul(&str); - pj_scan_get_char(scanner); - - pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name); - pj_scan_get_char(scanner); - pj_scan_get(scanner, cs_token, &str); - rtpmap->clock_rate = pj_strtoul(&str); - - if (*scanner->current == '/') { - pj_scan_get_char(scanner); - pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter); - } - - return rtpmap; -} - -static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner ) -{ - pjsdp_attr_string *attr; - attr = pj_pool_calloc(pool, 1, sizeof(*attr)); - - if (pj_scan_get_char(scanner) != ':') { - PJ_THROW(SYNTAX_ERROR); - } - pj_scan_get_until_ch(scanner, '\r', &attr->value); - return attr; -} - -static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner ) -{ - pjsdp_attr_num *attr; - pj_str_t str; - - attr = pj_pool_calloc(pool, 1, sizeof(*attr)); - - if (pj_scan_get_char(scanner) != ':') { - PJ_THROW(SYNTAX_ERROR); - } - pj_scan_get_until_ch(scanner, '\r', &str); - attr->value = pj_strtoul(&str); - return attr; -} - -static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner ) -{ - pjsdp_attr *attr; - - PJ_UNUSED_ARG(scanner) - attr = pj_pool_calloc(pool, 1, sizeof(*attr)); - return attr; -} - -static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner ) -{ - pjsdp_fmtp_attr *fmtp; - - fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp)); - - if (pj_scan_get_char(scanner) != ':') { - PJ_THROW(SYNTAX_ERROR); - } - pj_scan_get_until_ch(scanner, ' ', &fmtp->format); - pj_scan_get_char(scanner); - pj_scan_get_until_ch(scanner, '\r', &fmtp->param); - return fmtp; -} - -static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner) -{ - void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL; - pj_str_t attrname; - unsigned i; - pjsdp_attr *attr; - - /* skip a= */ - pj_scan_advance_n(scanner, 2, SKIP_WS); - - /* get attr name. */ - pj_scan_get(scanner, cs_token, &attrname); - - /* find entry to handle attrname */ - for (i=0; iname) == 0) { - parse_func = p->parse_attr; - break; - } - } - - /* fallback to generic string parser. */ - if (parse_func == NULL) { - parse_func = &parse_generic_string_attr; - } - - attr = (*parse_func)(pool, scanner); - attr->type = i; - - /* newline */ - pj_scan_get_newline(scanner); - - return attr; -} - -static void on_scanner_error(pj_scanner *scanner) -{ - PJ_UNUSED_ARG(scanner) - - PJ_THROW(SYNTAX_ERROR); -} - -/* - * Parse SDP message. - */ -PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, - pj_pool_t *pool) -{ - pj_scanner scanner; - pjsdp_session_desc *session; - pjsdp_media_desc *media = NULL; - void *attr; - pjsdp_conn_info *conn; - pj_str_t dummy; - int cur_name = 254; - PJ_USE_EXCEPTION; - - init_sdp_parser(); - - pj_scan_init(&scanner, buf, len, 0, &on_scanner_error); - session = pj_pool_calloc(pool, 1, sizeof(*session)); - - PJ_TRY { - while (!pj_scan_is_eof(&scanner)) { - cur_name = *scanner.current; - switch (cur_name) { - case 'a': - attr = parse_attr(pool, &scanner); - if (attr) { - if (media) { - media->attr[media->attr_count++] = attr; - } else { - session->attr[session->attr_count++] = attr; - } - } - break; - case 'o': - parse_origin(&scanner, session); - break; - case 's': - parse_generic_line(&scanner, &session->name); - break; - case 'c': - conn = pj_pool_calloc(pool, 1, sizeof(*conn)); - parse_connection_info(&scanner, conn); - if (media) { - media->conn = conn; - } else { - session->conn = conn; - } - break; - case 't': - parse_time(&scanner, session); - break; - case 'm': - media = pj_pool_calloc(pool, 1, sizeof(*media)); - parse_media(&scanner, media); - session->media[ session->media_count++ ] = media; - break; - case 'v': - parse_version(&scanner); - break; - default: - parse_generic_line(&scanner, &dummy); - break; - } - } - } - PJ_CATCH(SYNTAX_ERROR) { - PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d", - cur_name, scanner.line, scanner.col)); - if (!pj_scan_is_eof(&scanner)) { - if (*scanner.current != '\r') { - pj_scan_get_until_ch(&scanner, '\r', &dummy); - } - pj_scan_get_newline(&scanner); - } - } - PJ_END; - - pj_scan_fini(&scanner); - return session; -} - -/* - * Print SDP description. - */ -PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size) -{ - return print_session(desc, buf, size); -} - - +/* $Header: /pjproject/pjmedia/src/pjmedia/sdp.c 9 6/12/05 11:36a Bennylp $ */ + + +#include +#include +#include +#include +#include +#include +#include + +enum { + SKIP_WS = 0, + SYNTAX_ERROR = 1, +}; +#define TOKEN "-.!%*_=`'~" +#define NTP_OFFSET ((pj_uint32_t)2208988800) +#define LOG_THIS "sdp" + +/* + * Prototypes for line parser. + */ +static void parse_version(pj_scanner *scanner); +static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses); +static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses); +static void parse_generic_line(pj_scanner *scanner, pj_str_t *str); +static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn); +static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner); +static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med); + +/* + * Prototypes for attribute parsers. + */ +static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner ); +static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner ); + + +/* + * Prototypes for functions to print attribute. + * All of them returns integer for the length printed, or -1 on error. + */ +static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr, + char *buf, int length); +static int print_generic_string_attr(const pjsdp_attr_string *attr, + char *buf, int length); +static int print_generic_num_attr(const pjsdp_attr_num *attr, + char *buf, int length); +static int print_name_only_attr(const pjsdp_attr *attr, + char *buf, int length); +static int print_fmtp_attr(const pjsdp_fmtp_attr *attr, + char *buf, int length); + +/* + * Prototypes for cloning attributes. + */ +static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs); +static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs); + + +/* + * Prototypes + */ +static void init_sdp_parser(void); + + +typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner); +typedef int (*FPRINT)(const void *attr, char *buf, int length); +typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs); + +/* + * Array of functions to print attribute. + */ +static struct attr_map_rec +{ + pj_str_t name; + FPARSE parse_attr; + FPRINT print_attr; + FCLONE clone; +} attr_map[] = +{ + {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr}, + {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr}, + {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}, + {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr}, + {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr}, + {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr}, + {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr} +}; + +/* + * Scanner character specification. + */ +static int is_initialized; +static pj_char_spec cs_token; + +static void init_sdp_parser(void) +{ + if (is_initialized == 0) { + is_initialized = 1; + if (is_initialized != 1) { + return; + } + } + pj_cs_add_alpha(cs_token); + pj_cs_add_num(cs_token); + pj_cs_add_str( cs_token, TOKEN); +} + +static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap, + char *buf, int len) +{ + char *p = buf; + + if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) { + return -1; + } + + /* colon and payload type. */ + *p++ = ':'; + len = pj_utoa(rtpmap->payload_type, p); + p += len; + + /* space, encoding name */ + *p++ = ' '; + pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen); + p += rtpmap->encoding_name.slen; + + /* slash, clock-rate. */ + *p++ = '/'; + len = pj_utoa(rtpmap->clock_rate, p); + p += len; + + /* optionally add encoding parameter. */ + if (rtpmap->parameter.slen) { + *p++ = '/'; + pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen); + p += rtpmap->parameter.slen; + } + + return p-buf; +} + +static int print_generic_string_attr(const pjsdp_attr_string *attr, + char *buf, int len) +{ + char *p = buf; + + if (len < attr->value.slen + 4) { + return -1; + } + + /* colon and attribute value. */ + *p++ = ':'; + pj_memcpy(p, attr->value.ptr, attr->value.slen); + p += attr->value.slen; + + return p-buf; +} + +static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len) +{ + char *p = buf; + + if (len < 10) { + return -1; + } + *p++ = ':'; + return pj_utoa(attr->value, p); +} + +static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len) +{ + PJ_UNUSED_ARG(attr) + PJ_UNUSED_ARG(buf) + PJ_UNUSED_ARG(len) + return 0; +} + +static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len) +{ + char *p = buf; + + if (len < 4+fmtp->format.slen+fmtp->param.slen) { + return -1; + } + + /* colon and format. */ + *p++ = ':'; + pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen); + p += fmtp->format.slen; + + /* space and parameter. */ + *p++ = ' '; + pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen); + p += fmtp->param.slen; + + return p-buf; +} + + +static int print_attr(const pjsdp_attr *attr, char *buf, int len) +{ + char *p = buf; + struct attr_map_rec *desc = &attr_map[attr->type]; + + if (len < 16) { + return -1; + } + + *p++ = 'a'; + *p++ = '='; + pj_memcpy(p, desc->name.ptr, desc->name.slen); + p += desc->name.slen; + + len = (*desc->print_attr)(attr, p, (buf+len)-p); + if (len < 0) { + return -1; + } + p += len; + *p++ = '\r'; + *p++ = '\n'; + return p-buf; +} + +static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p; + pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr)); + if (!attr) + return NULL; + + attr->type = rhs->type; + attr->payload_type = rhs->payload_type; + if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL; + attr->clock_rate = rhs->clock_rate; + if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL; + + return (pjsdp_attr*)attr; +} + +static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p; + pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string)); + if (!attr) + return NULL; + + attr->type = rhs->type; + if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL; + + return (pjsdp_attr*)attr; +} + +static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p; + pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num)); + if (!attr) + return NULL; + + attr->type = rhs->type; + attr->value = rhs->value; + + return (pjsdp_attr*)attr; +} + +static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs) +{ + pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr)); + if (!attr) + return NULL; + + attr->type = rhs->type; + return attr; +} + +static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p) +{ + const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p; + pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr)); + if (!attr) + return NULL; + + attr->type = rhs->type; + if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL; + if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL; + + return (pjsdp_attr*)attr; +} + +PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs) +{ + struct attr_map_rec *desc; + + if (rhs->type >= PJSDP_END_OF_ATTR) { + pj_assert(0); + return NULL; + } + + desc = &attr_map[rhs->type]; + return (*desc->clone) (pool, rhs); +} + +PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type) +{ + int i; + + for (i=0; itype == type) + return attr_array[i]; + } + return NULL; +} + +static int print_connection_info( pjsdp_conn_info *c, char *buf, int len) +{ + char *p = buf; + + if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) { + return -1; + } + *p++ = 'c'; + *p++ = '='; + pj_memcpy(p, c->net_type.ptr, c->net_type.slen); + p += c->net_type.slen; + *p++ = ' '; + pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen); + p += c->addr_type.slen; + *p++ = ' '; + pj_memcpy(p, c->addr.ptr, c->addr.slen); + p += c->addr.slen; + *p++ = '\r'; + *p++ = '\n'; + + return p-buf; +} + +PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs) +{ + pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); + if (!c) return NULL; + + if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL; + if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL; + if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL; + + return c; +} + +static int print_media_desc( pjsdp_media_desc *m, char *buf, int len) +{ + char *p = buf; + char *end = buf+len; + unsigned i; + int printed; + + /* check length for the "m=" line. */ + if (len < m->desc.media.slen+m->desc.transport.slen+12+24) { + return -1; + } + *p++ = 'm'; /* m= */ + *p++ = '='; + pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen); + p += m->desc.media.slen; + *p++ = ' '; + printed = pj_utoa(m->desc.port, p); + p += printed; + if (m->desc.port_count > 1) { + *p++ = '/'; + printed = pj_utoa(m->desc.port_count, p); + p += printed; + } + *p++ = ' '; + pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen); + p += m->desc.transport.slen; + for (i=0; idesc.fmt_count; ++i) { + *p++ = ' '; + pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen); + p += m->desc.fmt[i].slen; + } + *p++ = '\r'; + *p++ = '\n'; + + /* print connection info, if present. */ + if (m->conn) { + printed = print_connection_info(m->conn, p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + /* print attributes. */ + for (i=0; iattr_count; ++i) { + printed = print_attr(m->attr[i], p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + return p-buf; +} + +PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, + const pjsdp_media_desc *rhs) +{ + unsigned int i; + pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc)); + if (!m) + return NULL; + + pj_strdup (pool, &m->desc.media, &rhs->desc.media); + m->desc.port = rhs->desc.port; + m->desc.port_count = rhs->desc.port_count; + pj_strdup (pool, &m->desc.transport, &rhs->desc.transport); + m->desc.fmt_count = rhs->desc.fmt_count; + for (i=0; idesc.fmt_count; ++i) + m->desc.fmt[i] = rhs->desc.fmt[i]; + + if (rhs->conn) { + m->conn = pjsdp_conn_info_clone (pool, rhs->conn); + if (!m->conn) + return NULL; + } else { + m->conn = NULL; + } + + m->attr_count = rhs->attr_count; + for (i=0; i < rhs->attr_count; ++i) { + m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]); + if (!m->attr[i]) + return NULL; + } + + return m; +} + +/** Check if the media description has the specified attribute. */ +PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, + pjsdp_attr_type_e attr_type) +{ + unsigned i; + for (i=0; iattr_count; ++i) { + pjsdp_attr *attr = m->attr[i]; + if (attr->type == attr_type) + return 1; + } + return 0; +} + +/** Find rtpmap attribute for the specified payload type. */ +PJ_DEF(const pjsdp_rtpmap_attr*) +pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt) +{ + unsigned i; + for (i=0; iattr_count; ++i) { + pjsdp_attr *attr = m->attr[i]; + if (attr->type == PJSDP_ATTR_RTPMAP) { + const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr; + if (rtpmap->payload_type == pt) + return rtpmap; + } + } + return NULL; +} + + +static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len) +{ + char *p = buf; + char *end = buf+len; + unsigned i; + int printed; + + /* Check length for v= and o= lines. */ + if (len < 5+ + 2+ses->origin.user.slen+18+ + ses->origin.net_type.slen+ses->origin.addr.slen + 2) + { + return -1; + } + + /* SDP version (v= line) */ + pj_memcpy(p, "v=0\r\n", 5); + p += 5; + + /* Owner (o=) line. */ + *p++ = 'o'; + *p++ = '='; + pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen); + p += ses->origin.user.slen; + *p++ = ' '; + printed = pj_utoa(ses->origin.id, p); + p += printed; + *p++ = ' '; + printed = pj_utoa(ses->origin.version, p); + p += printed; + *p++ = ' '; + pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen); + p += ses->origin.net_type.slen; + *p++ = ' '; + pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen); + p += ses->origin.addr_type.slen; + *p++ = ' '; + pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen); + p += ses->origin.addr.slen; + *p++ = '\r'; + *p++ = '\n'; + + /* Session name (s=) line. */ + if ((end-p) < 8+ses->name.slen) { + return -1; + } + *p++ = 's'; + *p++ = '='; + pj_memcpy(p, ses->name.ptr, ses->name.slen); + p += ses->name.slen; + *p++ = '\r'; + *p++ = '\n'; + + /* Time */ + if ((end-p) < 24) { + return -1; + } + *p++ = 't'; + *p++ = '='; + printed = pj_utoa(ses->time.start, p); + p += printed; + *p++ = ' '; + printed = pj_utoa(ses->time.stop, p); + p += printed; + *p++ = '\r'; + *p++ = '\n'; + + /* Connection line (c=) if exist. */ + if (ses->conn) { + printed = print_connection_info(ses->conn, p, end-p); + if (printed < 1) { + return -1; + } + p += printed; + } + + /* Print all attribute (a=) lines. */ + for (i=0; iattr_count; ++i) { + printed = print_attr(ses->attr[i], p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + /* Print media (m=) lines. */ + for (i=0; imedia_count; ++i) { + printed = print_media_desc(ses->media[i], p, end-p); + if (printed < 0) { + return -1; + } + p += printed; + } + + return p-buf; +} + +/****************************************************************************** + * PARSERS + */ + +static void parse_version(pj_scanner *scanner) +{ + pj_scan_advance_n(scanner, 3, SKIP_WS); + pj_scan_get_newline(scanner); +} + +static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses) +{ + pj_str_t str; + + /* o= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* username. */ + pj_scan_get_until_ch(scanner, ' ', &ses->origin.user); + pj_scan_get_char(scanner); + + /* id */ + pj_scan_get_until_ch(scanner, ' ', &str); + ses->origin.id = pj_strtoul(&str); + pj_scan_get_char(scanner); + + /* version */ + pj_scan_get_until_ch(scanner, ' ', &str); + ses->origin.version = pj_strtoul(&str); + pj_scan_get_char(scanner); + + /* network-type */ + pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type); + pj_scan_get_char(scanner); + + /* addr-type */ + pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type); + pj_scan_get_char(scanner); + + /* address */ + pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr); + + /* newline */ + pj_scan_get_newline(scanner); +} + +static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses) +{ + pj_str_t str; + + /* t= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* start time */ + pj_scan_get_until_ch(scanner, ' ', &str); + ses->time.start = pj_strtoul(&str); + + pj_scan_get_char(scanner); + + /* stop time */ + pj_scan_get_until_ch(scanner, '\r', &str); + ses->time.stop = pj_strtoul(&str); + + /* newline */ + pj_scan_get_newline(scanner); +} + +static void parse_generic_line(pj_scanner *scanner, pj_str_t *str) +{ + /* x= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* get anything until newline. */ + pj_scan_get_until_ch(scanner, '\r', str); + + /* newline. */ + pj_scan_get_newline(scanner); +} + +static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn) +{ + /* c= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* network-type */ + pj_scan_get_until_ch(scanner, ' ', &conn->net_type); + pj_scan_get_char(scanner); + + /* addr-type */ + pj_scan_get_until_ch(scanner, ' ', &conn->addr_type); + pj_scan_get_char(scanner); + + /* address. */ + pj_scan_get_until_ch(scanner, '\r', &conn->addr); + + /* newline */ + pj_scan_get_newline(scanner); +} + +static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med) +{ + pj_str_t str; + + /* m= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* type */ + pj_scan_get_until_ch(scanner, ' ', &med->desc.media); + pj_scan_get_char(scanner); + + /* port */ + pj_scan_get(scanner, cs_token, &str); + med->desc.port = (unsigned short)pj_strtoul(&str); + if (*scanner->current == '/') { + /* port count */ + pj_scan_get_char(scanner); + pj_scan_get(scanner, cs_token, &str); + med->desc.port_count = pj_strtoul(&str); + + } else { + med->desc.port_count = 0; + } + + if (pj_scan_get_char(scanner) != ' ') { + PJ_THROW(SYNTAX_ERROR); + } + + /* transport */ + pj_scan_get_until_ch(scanner, ' ', &med->desc.transport); + + /* format list */ + med->desc.fmt_count = 0; + while (*scanner->current == ' ') { + pj_scan_get_char(scanner); + pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]); + } + + /* newline */ + pj_scan_get_newline(scanner); +} + +static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_rtpmap_attr *rtpmap; + pj_str_t str; + + rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap)); + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, ' ', &str); + rtpmap->payload_type = pj_strtoul(&str); + pj_scan_get_char(scanner); + + pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name); + pj_scan_get_char(scanner); + pj_scan_get(scanner, cs_token, &str); + rtpmap->clock_rate = pj_strtoul(&str); + + if (*scanner->current == '/') { + pj_scan_get_char(scanner); + pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter); + } + + return rtpmap; +} + +static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_attr_string *attr; + attr = pj_pool_calloc(pool, 1, sizeof(*attr)); + + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, '\r', &attr->value); + return attr; +} + +static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_attr_num *attr; + pj_str_t str; + + attr = pj_pool_calloc(pool, 1, sizeof(*attr)); + + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, '\r', &str); + attr->value = pj_strtoul(&str); + return attr; +} + +static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_attr *attr; + + PJ_UNUSED_ARG(scanner) + attr = pj_pool_calloc(pool, 1, sizeof(*attr)); + return attr; +} + +static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner ) +{ + pjsdp_fmtp_attr *fmtp; + + fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp)); + + if (pj_scan_get_char(scanner) != ':') { + PJ_THROW(SYNTAX_ERROR); + } + pj_scan_get_until_ch(scanner, ' ', &fmtp->format); + pj_scan_get_char(scanner); + pj_scan_get_until_ch(scanner, '\r', &fmtp->param); + return fmtp; +} + +static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner) +{ + void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL; + pj_str_t attrname; + unsigned i; + pjsdp_attr *attr; + + /* skip a= */ + pj_scan_advance_n(scanner, 2, SKIP_WS); + + /* get attr name. */ + pj_scan_get(scanner, cs_token, &attrname); + + /* find entry to handle attrname */ + for (i=0; iname) == 0) { + parse_func = p->parse_attr; + break; + } + } + + /* fallback to generic string parser. */ + if (parse_func == NULL) { + parse_func = &parse_generic_string_attr; + } + + attr = (*parse_func)(pool, scanner); + attr->type = i; + + /* newline */ + pj_scan_get_newline(scanner); + + return attr; +} + +static void on_scanner_error(pj_scanner *scanner) +{ + PJ_UNUSED_ARG(scanner) + + PJ_THROW(SYNTAX_ERROR); +} + +/* + * Parse SDP message. + */ +PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, + pj_pool_t *pool) +{ + pj_scanner scanner; + pjsdp_session_desc *session; + pjsdp_media_desc *media = NULL; + void *attr; + pjsdp_conn_info *conn; + pj_str_t dummy; + int cur_name = 254; + PJ_USE_EXCEPTION; + + init_sdp_parser(); + + pj_scan_init(&scanner, buf, len, 0, &on_scanner_error); + session = pj_pool_calloc(pool, 1, sizeof(*session)); + + PJ_TRY { + while (!pj_scan_is_eof(&scanner)) { + cur_name = *scanner.current; + switch (cur_name) { + case 'a': + attr = parse_attr(pool, &scanner); + if (attr) { + if (media) { + media->attr[media->attr_count++] = attr; + } else { + session->attr[session->attr_count++] = attr; + } + } + break; + case 'o': + parse_origin(&scanner, session); + break; + case 's': + parse_generic_line(&scanner, &session->name); + break; + case 'c': + conn = pj_pool_calloc(pool, 1, sizeof(*conn)); + parse_connection_info(&scanner, conn); + if (media) { + media->conn = conn; + } else { + session->conn = conn; + } + break; + case 't': + parse_time(&scanner, session); + break; + case 'm': + media = pj_pool_calloc(pool, 1, sizeof(*media)); + parse_media(&scanner, media); + session->media[ session->media_count++ ] = media; + break; + case 'v': + parse_version(&scanner); + break; + default: + parse_generic_line(&scanner, &dummy); + break; + } + } + } + PJ_CATCH(SYNTAX_ERROR) { + PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d", + cur_name, scanner.line, scanner.col)); + if (!pj_scan_is_eof(&scanner)) { + if (*scanner.current != '\r') { + pj_scan_get_until_ch(&scanner, '\r', &dummy); + } + pj_scan_get_newline(&scanner); + } + } + PJ_END; + + pj_scan_fini(&scanner); + return session; +} + +/* + * Print SDP description. + */ +PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size) +{ + return print_session(desc, buf, size); +} + + diff --git a/pjmedia/src/pjmedia/sdp.h b/pjmedia/src/pjmedia/sdp.h index 4bbcbeaff..8d65ec769 100644 --- a/pjmedia/src/pjmedia/sdp.h +++ b/pjmedia/src/pjmedia/sdp.h @@ -1,316 +1,316 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/sdp.h 9 6/17/05 11:16p Bennylp $ */ - -#ifndef __PJSDP_SDP_H__ -#define __PJSDP_SDP_H__ - -/** - * @defgroup PJSDP SDP Library - */ -/** - * @file sdp.h - * @brief SDP header file. - */ -/** - * @defgroup PJ_SDP_H SDP stack. - * @ingroup PJSDP - * @{ - * - * This SDP module consists of SDP parser, SDP structure, and function to - * print back the structure as SDP message. - */ - -#include - -PJ_BEGIN_DECL - -#define PJSDP_MAX_FMT 32 -#define PJSDP_MAX_ATTR 32 -#define PJSDP_MAX_MEDIA 16 - -/** - * This enumeration describes the attribute type. - */ -typedef enum pjsdp_attr_type_e -{ - PJSDP_ATTR_RTPMAP, - PJSDP_ATTR_CAT, - PJSDP_ATTR_KEYWORDS, - PJSDP_ATTR_TOOL, - PJSDP_ATTR_PTIME, - PJSDP_ATTR_RECV_ONLY, - PJSDP_ATTR_SEND_ONLY, - PJSDP_ATTR_SEND_RECV, - PJSDP_ATTR_ORIENT, - PJSDP_ATTR_TYPE, - PJSDP_ATTR_CHARSET, - PJSDP_ATTR_SDP_LANG, - PJSDP_ATTR_LANG, - PJSDP_ATTR_FRAME_RATE, - PJSDP_ATTR_QUALITY, - PJSDP_ATTR_FMTP, - PJSDP_ATTR_INACTIVE, - PJSDP_ATTR_GENERIC, - PJSDP_END_OF_ATTR, -} pjsdp_attr_type_e; - - -/** - * This structure keeps the common attributes that all 'descendants' - * will have. - */ -typedef struct pjsdp_attr -{ - pjsdp_attr_type_e type; /**< Attribute type. */ -} pjsdp_attr; - - -/** - * This is the structure to represent generic attribute which has a - * string value. - */ -typedef struct pjsdp_attr_string -{ - pjsdp_attr_type_e type; - pj_str_t value; -} pjsdp_attr_string; - - -/** - * This is the structure to represent generic SDP attribute which has - * a numeric value. - */ -typedef struct pjsdp_attr_num -{ - pjsdp_attr_type_e type; - pj_uint32_t value; -} pjsdp_attr_num; - - -/** - * SDP \a rtpmap attribute. - */ -typedef struct pjsdp_rtpmap_attr -{ - pjsdp_attr_type_e type; - unsigned payload_type; - pj_str_t encoding_name; - unsigned clock_rate; - pj_str_t parameter; -} pjsdp_rtpmap_attr; - - -/** - * SDP \a fmtp attribute. - */ -typedef struct pjsdp_fmtp_attr -{ - pjsdp_attr_type_e type; - pj_str_t format; - pj_str_t param; -} pjsdp_fmtp_attr; - - -/** - * SDP generic attribute. - */ -typedef struct pjsdp_generic_attr -{ - pjsdp_attr_type_e type; - pj_str_t name; - pj_str_t value; -} pjsdp_generic_attr; - - -/** SDP \a cat attribute. */ -typedef struct pjsdp_attr_string pjsdp_cat_attr; - -/** SDP \a keywds attribute. */ -typedef struct pjsdp_attr_string pjsdp_keywds_attr; - -/** SDP \a tool attribute. */ -typedef struct pjsdp_attr_string pjsdp_tool_attr; - -/** SDP \a ptime attribute. */ -typedef struct pjsdp_attr_num pjsdp_ptime_attr; - -/** SDP \a recvonly attribute. */ -typedef struct pjsdp_attr pjsdp_recv_only_attr; - -/** SDP \a sendonly attribute. */ -typedef struct pjsdp_attr pjsdp_send_only_attr; - -/** SDP \a sendrecv attribute. */ -typedef struct pjsdp_attr pjsdp_send_recv_attr; - -/** SDP \a orient attribute. */ -typedef struct pjsdp_attr_string pjsdp_orient_attr; - -/** SDP \a type attribute. */ -typedef struct pjsdp_attr_string pjsdp_type_attr; - -/** SDP \a charset attribute. */ -typedef struct pjsdp_attr_string pjsdp_charset_attr; - -/** SDP \a sdplang attribute. */ -typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr; - -/** SDP \a lang attribute. */ -typedef struct pjsdp_attr_string pjsdp_lang_attr; - -/** SDP \a framerate attribute. */ -typedef struct pjsdp_attr_string pjsdp_frame_rate_attr; - -/** SDP \a quality attribute. */ -typedef struct pjsdp_attr_num pjsdp_quality_attr; - -/** SDP \a inactive attribute. */ -typedef struct pjsdp_attr pjsdp_inactive_attr; - -/** Clone attribute */ -PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs); - -/** Find attribute */ -PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type); - -/** - * SDP connection info. - */ -typedef struct pjsdp_conn_info -{ - pj_str_t net_type; - pj_str_t addr_type; - pj_str_t addr; -} pjsdp_conn_info; - -/** - *Clone connection info. - * - * @param pool Pool to allocate memory for the new connection info. - * @param rhs The connection into to clone. - * - * @return the new connection info. - */ -PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, - const pjsdp_conn_info *rhs); - -/** - * SDP media description. - */ -typedef struct pjsdp_media_desc -{ - struct - { - pj_str_t media; - pj_uint16_t port; - unsigned port_count; - pj_str_t transport; - unsigned fmt_count; - pj_str_t fmt[PJSDP_MAX_FMT]; - } desc; - - pjsdp_conn_info *conn; - unsigned attr_count; - pjsdp_attr *attr[PJSDP_MAX_ATTR]; - -} pjsdp_media_desc; - -/** - * Clone SDP media description. - * - * @param pool Pool to allocate memory for the new media description. - * @param rhs The media descriptin to clone. - * - * @return a new media description. - */ -PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, - const pjsdp_media_desc *rhs); - -/** - * Check if the media description has the specified attribute. - * - * @param m The SDP media description. - * @param attr_type The attribute type. - * - * @return nonzero if true. - */ -PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, - pjsdp_attr_type_e attr_type); - -/** - * Find rtpmap attribute for the specified payload type. - * - * @param m The SDP media description. - * @param pt RTP payload type. - * - * @return the SDP rtpmap attribute for the payload type, or NULL if not found. - */ -PJ_DECL(const pjsdp_rtpmap_attr*) -pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt); - - -/** - * This structure describes SDP session description. - */ -typedef struct pjsdp_session_desc -{ - struct - { - pj_str_t user; - pj_uint32_t id; - pj_uint32_t version; - pj_str_t net_type; - pj_str_t addr_type; - pj_str_t addr; - } origin; - - pj_str_t name; - pjsdp_conn_info *conn; - - struct - { - pj_uint32_t start; - pj_uint32_t stop; - } time; - - unsigned attr_count; - pjsdp_attr *attr[PJSDP_MAX_ATTR]; - - unsigned media_count; - pjsdp_media_desc *media[PJSDP_MAX_MEDIA]; - -} pjsdp_session_desc; - - -/** - * Parse SDP message. - * - * @param buf The message buffer. - * @param len The length of the message. - * @param pool The pool to allocate SDP session description. - * - * @return SDP session description. - */ -PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, - pj_pool_t *pool); - -/** - * Print SDP description to a buffer. - * - * @param buf The buffer. - * @param size The buffer length. - * @param desc The SDP session description. - * - * @return the length printed, or -1. - */ -PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc, - char *buf, pj_size_t size); - - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSDP_SDP_H__ */ - +/* $Header: /pjproject/pjmedia/src/pjmedia/sdp.h 9 6/17/05 11:16p Bennylp $ */ + +#ifndef __PJSDP_SDP_H__ +#define __PJSDP_SDP_H__ + +/** + * @defgroup PJSDP SDP Library + */ +/** + * @file sdp.h + * @brief SDP header file. + */ +/** + * @defgroup PJ_SDP_H SDP stack. + * @ingroup PJSDP + * @{ + * + * This SDP module consists of SDP parser, SDP structure, and function to + * print back the structure as SDP message. + */ + +#include + +PJ_BEGIN_DECL + +#define PJSDP_MAX_FMT 32 +#define PJSDP_MAX_ATTR 32 +#define PJSDP_MAX_MEDIA 16 + +/** + * This enumeration describes the attribute type. + */ +typedef enum pjsdp_attr_type_e +{ + PJSDP_ATTR_RTPMAP, + PJSDP_ATTR_CAT, + PJSDP_ATTR_KEYWORDS, + PJSDP_ATTR_TOOL, + PJSDP_ATTR_PTIME, + PJSDP_ATTR_RECV_ONLY, + PJSDP_ATTR_SEND_ONLY, + PJSDP_ATTR_SEND_RECV, + PJSDP_ATTR_ORIENT, + PJSDP_ATTR_TYPE, + PJSDP_ATTR_CHARSET, + PJSDP_ATTR_SDP_LANG, + PJSDP_ATTR_LANG, + PJSDP_ATTR_FRAME_RATE, + PJSDP_ATTR_QUALITY, + PJSDP_ATTR_FMTP, + PJSDP_ATTR_INACTIVE, + PJSDP_ATTR_GENERIC, + PJSDP_END_OF_ATTR, +} pjsdp_attr_type_e; + + +/** + * This structure keeps the common attributes that all 'descendants' + * will have. + */ +typedef struct pjsdp_attr +{ + pjsdp_attr_type_e type; /**< Attribute type. */ +} pjsdp_attr; + + +/** + * This is the structure to represent generic attribute which has a + * string value. + */ +typedef struct pjsdp_attr_string +{ + pjsdp_attr_type_e type; + pj_str_t value; +} pjsdp_attr_string; + + +/** + * This is the structure to represent generic SDP attribute which has + * a numeric value. + */ +typedef struct pjsdp_attr_num +{ + pjsdp_attr_type_e type; + pj_uint32_t value; +} pjsdp_attr_num; + + +/** + * SDP \a rtpmap attribute. + */ +typedef struct pjsdp_rtpmap_attr +{ + pjsdp_attr_type_e type; + unsigned payload_type; + pj_str_t encoding_name; + unsigned clock_rate; + pj_str_t parameter; +} pjsdp_rtpmap_attr; + + +/** + * SDP \a fmtp attribute. + */ +typedef struct pjsdp_fmtp_attr +{ + pjsdp_attr_type_e type; + pj_str_t format; + pj_str_t param; +} pjsdp_fmtp_attr; + + +/** + * SDP generic attribute. + */ +typedef struct pjsdp_generic_attr +{ + pjsdp_attr_type_e type; + pj_str_t name; + pj_str_t value; +} pjsdp_generic_attr; + + +/** SDP \a cat attribute. */ +typedef struct pjsdp_attr_string pjsdp_cat_attr; + +/** SDP \a keywds attribute. */ +typedef struct pjsdp_attr_string pjsdp_keywds_attr; + +/** SDP \a tool attribute. */ +typedef struct pjsdp_attr_string pjsdp_tool_attr; + +/** SDP \a ptime attribute. */ +typedef struct pjsdp_attr_num pjsdp_ptime_attr; + +/** SDP \a recvonly attribute. */ +typedef struct pjsdp_attr pjsdp_recv_only_attr; + +/** SDP \a sendonly attribute. */ +typedef struct pjsdp_attr pjsdp_send_only_attr; + +/** SDP \a sendrecv attribute. */ +typedef struct pjsdp_attr pjsdp_send_recv_attr; + +/** SDP \a orient attribute. */ +typedef struct pjsdp_attr_string pjsdp_orient_attr; + +/** SDP \a type attribute. */ +typedef struct pjsdp_attr_string pjsdp_type_attr; + +/** SDP \a charset attribute. */ +typedef struct pjsdp_attr_string pjsdp_charset_attr; + +/** SDP \a sdplang attribute. */ +typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr; + +/** SDP \a lang attribute. */ +typedef struct pjsdp_attr_string pjsdp_lang_attr; + +/** SDP \a framerate attribute. */ +typedef struct pjsdp_attr_string pjsdp_frame_rate_attr; + +/** SDP \a quality attribute. */ +typedef struct pjsdp_attr_num pjsdp_quality_attr; + +/** SDP \a inactive attribute. */ +typedef struct pjsdp_attr pjsdp_inactive_attr; + +/** Clone attribute */ +PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs); + +/** Find attribute */ +PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type); + +/** + * SDP connection info. + */ +typedef struct pjsdp_conn_info +{ + pj_str_t net_type; + pj_str_t addr_type; + pj_str_t addr; +} pjsdp_conn_info; + +/** + *Clone connection info. + * + * @param pool Pool to allocate memory for the new connection info. + * @param rhs The connection into to clone. + * + * @return the new connection info. + */ +PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, + const pjsdp_conn_info *rhs); + +/** + * SDP media description. + */ +typedef struct pjsdp_media_desc +{ + struct + { + pj_str_t media; + pj_uint16_t port; + unsigned port_count; + pj_str_t transport; + unsigned fmt_count; + pj_str_t fmt[PJSDP_MAX_FMT]; + } desc; + + pjsdp_conn_info *conn; + unsigned attr_count; + pjsdp_attr *attr[PJSDP_MAX_ATTR]; + +} pjsdp_media_desc; + +/** + * Clone SDP media description. + * + * @param pool Pool to allocate memory for the new media description. + * @param rhs The media descriptin to clone. + * + * @return a new media description. + */ +PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool, + const pjsdp_media_desc *rhs); + +/** + * Check if the media description has the specified attribute. + * + * @param m The SDP media description. + * @param attr_type The attribute type. + * + * @return nonzero if true. + */ +PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m, + pjsdp_attr_type_e attr_type); + +/** + * Find rtpmap attribute for the specified payload type. + * + * @param m The SDP media description. + * @param pt RTP payload type. + * + * @return the SDP rtpmap attribute for the payload type, or NULL if not found. + */ +PJ_DECL(const pjsdp_rtpmap_attr*) +pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt); + + +/** + * This structure describes SDP session description. + */ +typedef struct pjsdp_session_desc +{ + struct + { + pj_str_t user; + pj_uint32_t id; + pj_uint32_t version; + pj_str_t net_type; + pj_str_t addr_type; + pj_str_t addr; + } origin; + + pj_str_t name; + pjsdp_conn_info *conn; + + struct + { + pj_uint32_t start; + pj_uint32_t stop; + } time; + + unsigned attr_count; + pjsdp_attr *attr[PJSDP_MAX_ATTR]; + + unsigned media_count; + pjsdp_media_desc *media[PJSDP_MAX_MEDIA]; + +} pjsdp_session_desc; + + +/** + * Parse SDP message. + * + * @param buf The message buffer. + * @param len The length of the message. + * @param pool The pool to allocate SDP session description. + * + * @return SDP session description. + */ +PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len, + pj_pool_t *pool); + +/** + * Print SDP description to a buffer. + * + * @param buf The buffer. + * @param size The buffer length. + * @param desc The SDP session description. + * + * @return the length printed, or -1. + */ +PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc, + char *buf, pj_size_t size); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSDP_SDP_H__ */ + diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c index 033b779be..d21bf97b1 100644 --- a/pjmedia/src/pjmedia/session.c +++ b/pjmedia/src/pjmedia/session.c @@ -1,816 +1,816 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/session.c 10 6/14/05 12:54a Bennylp $ */ -#include -#include -#include -#include -#include - -typedef struct pj_media_stream_desc -{ - pj_media_stream_info info; - pj_media_stream_t *enc_stream, *dec_stream; -} pj_media_stream_desc; - -struct pj_media_session_t -{ - pj_pool_t *pool; - pj_med_mgr_t *mediamgr; - unsigned stream_cnt; - pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA]; -}; - -#define THIS_FILE "session.c" - -#define PJ_MEDIA_SESSION_SIZE (48*1024) -#define PJ_MEDIA_SESSION_INC 1024 - -static const pj_str_t ID_AUDIO = { "audio", 5}; -static const pj_str_t ID_VIDEO = { "video", 5}; -static const pj_str_t ID_IN = { "IN", 2 }; -static const pj_str_t ID_IP4 = { "IP4", 3}; -static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; -static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; - -static void session_init (pj_media_session_t *ses) -{ - pj_memset (ses, 0, sizeof(pj_media_session_t)); -} - - -/** - * Create new session offering. - */ -PJ_DEF(pj_media_session_t*) -pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info) -{ - pj_pool_factory *pf; - pj_pool_t *pool; - pj_media_session_t *session; - pj_media_stream_desc *sd; - unsigned i, codec_cnt; - pj_codec_mgr *cm; - const pj_codec_id *codecs[PJSDP_MAX_FMT]; - - pf = pj_med_mgr_get_pool_factory(mgr); - - pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); - if (!pool) - return NULL; - - session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); - if (!session) - return NULL; - - session_init (session); - - session->pool = pool; - session->mediamgr = mgr; - - /* Create first stream */ - sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); - if (!sd) - return NULL; - - sd->info.type = ID_AUDIO; - sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; - sd->info.transport = ID_RTP_AVP; - pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); - - /* Enum audio codecs. */ - sd->info.fmt_cnt = 0; - cm = pj_med_mgr_get_codec_mgr (mgr); - codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs); - if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; - for (i=0; itype != PJ_MEDIA_TYPE_AUDIO) - continue; - - sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt; - sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate; - pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name); - ++sd->info.fmt_cnt; - } - - session->stream_desc[session->stream_cnt++] = sd; - - return session; -} - -static int sdp_check (const pjsdp_session_desc *sdp) -{ - int has_conn = 0; - unsigned i; - - if (sdp->conn) - has_conn = 1; - - if (sdp->media_count == 0) { - PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition")); - return -1; - } - - for (i=0; imedia_count; ++i) { - pjsdp_media_desc *m = sdp->media[i]; - - if (!m) { - pj_assert(0); - return -1; - } - - if (m->desc.fmt_count == 0) { - PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream")); - return -1; - } - - if (!has_conn && m->conn == NULL) { - PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media")); - return -1; - } - } - - return 0; -} - -/* - * Create local stream definition that matches SDP received from peer. - */ -static pj_media_stream_desc* -create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn, - const pjsdp_media_desc *m, const pj_media_sock_info *sock_info) -{ - pj_media_stream_desc *sd; - - sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); - if (!sd) { - PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor")); - return NULL; - } - - if (pj_stricmp(&conn->net_type, &ID_IN)==0 && - pj_stricmp(&conn->addr_type, &ID_IP4)==0 && - pj_stricmp(&m->desc.media, &ID_AUDIO)==0 && - pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0) - { - /* - * Got audio stream. - */ - unsigned i, codec_cnt; - pj_codec_mgr *cm; - const pj_codec_id *codecs[PJSDP_MAX_FMT]; - - sd->info.type = ID_AUDIO; - sd->info.transport = ID_RTP_AVP; - pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); - sd->info.rem_port = m->desc.port; - pj_strdup (pool, &sd->info.rem_addr, &conn->addr); - - /* Enum audio codecs. */ - sd->info.fmt_cnt = 0; - cm = pj_med_mgr_get_codec_mgr (mgr); - codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs); - if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; - - /* Find just one codec which we can support. */ - for (i=0; idesc.fmt_count && sd->info.fmt_cnt == 0; ++i) { - unsigned j, fmt_i; - - /* For static payload, just match payload type. */ - /* Else match clock rate and encoding name. */ - fmt_i = pj_strtoul(&m->desc.fmt[i]); - if (fmt_i < PJ_RTP_PT_DYNAMIC) { - for (j=0; jpt == fmt_i) { - sd->info.fmt_cnt = 1; - sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; - sd->info.fmt[0].pt = codecs[j]->pt; - sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; - pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name); - break; - } - } - } else { - - /* Find the rtpmap for the payload type. */ - const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i); - - /* Don't accept the media if no rtpmap for dynamic PT. */ - if (rtpmap == NULL) { - PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i])); - continue; - } - - /* Check whether we can take this codec. */ - for (j=0; jclock_rate == codecs[j]->sample_rate && - pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0) - { - sd->info.fmt_cnt = 1; - sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; - sd->info.fmt[0].pt = codecs[j]->pt; - sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; - sd->info.fmt[0].encoding_name = codecs[j]->encoding_name; - break; - } - } - } - } - - /* Match codec and direction. */ - if (sd->info.fmt_cnt == 0 || m->desc.port == 0 || - pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) - { - sd->info.dir = PJ_MEDIA_DIR_NONE; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_ENCODING; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_DECODING; - } - else { - sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; - } - - } else { - /* Unsupported media stream. */ - unsigned fmt_num; - const pjsdp_rtpmap_attr *rtpmap = NULL; - - pj_strdup(pool, &sd->info.type, &m->desc.media); - pj_strdup(pool, &sd->info.transport, &m->desc.transport); - pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info)); - pj_strdup (pool, &sd->info.rem_addr, &conn->addr); - sd->info.rem_port = m->desc.port; - - /* Just put one format and rtpmap, so that we don't have to make - * special exception when we convert this stream to SDP. - */ - - /* Find the rtpmap for the payload type. */ - fmt_num = pj_strtoul(&m->desc.fmt[0]); - rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num); - - sd->info.fmt_cnt = 1; - if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) { - sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO; - sd->info.fmt[0].pt = fmt_num; - if (rtpmap) { - pj_strdup (pool, &sd->info.fmt[0].encoding_name, - &rtpmap->encoding_name); - sd->info.fmt[0].sample_rate = rtpmap->clock_rate; - } - } else { - sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN; - pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]); - } - - sd->info.dir = PJ_MEDIA_DIR_NONE; - } - - return sd; -} - -/** - * Create new session based on peer's offering. - */ -PJ_DEF(pj_media_session_t*) -pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, - const pj_media_sock_info *sock_info) -{ - pj_pool_factory *pf; - pj_pool_t *pool; - pj_media_session_t *session; - unsigned i; - - if (sdp_check(sdp) != 0) - return NULL; - - pf = pj_med_mgr_get_pool_factory(mgr); - pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); - if (!pool) - return NULL; - - session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); - if (!session) { - PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); - pj_pool_release (pool); - return NULL; - } - - session_init (session); - - session->pool = pool; - session->mediamgr = mgr; - - /* Enumerate each media stream and create our peer. */ - for (i=0; imedia_count; ++i) { - const pjsdp_conn_info *conn; - const pjsdp_media_desc *m; - pj_media_stream_desc *sd; - - m = sdp->media[i]; - conn = m->conn ? m->conn : sdp->conn; - - /* - * Bug: - * the sock_info below is used by more than one 'm' lines - */ - PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES) - - sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info); - pj_assert (sd); - - session->stream_desc[session->stream_cnt++] = sd; - } - - return session; -} - -/** - * Duplicate session. The new session is inactive. - */ -PJ_DEF(pj_media_session_t*) -pj_media_session_clone (const pj_media_session_t *rhs) -{ - pj_pool_factory *pf; - pj_pool_t *pool; - pj_media_session_t *session; - unsigned i; - - pf = pj_med_mgr_get_pool_factory(rhs->mediamgr); - pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); - if (!pool) { - return NULL; - } - - session = pj_pool_alloc (pool, sizeof(*session)); - if (!session) { - PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); - pj_pool_release (pool); - return NULL; - } - - session->pool = pool; - session->mediamgr = rhs->mediamgr; - session->stream_cnt = rhs->stream_cnt; - - for (i=0; istream_cnt; ++i) { - pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc)); - const pj_media_stream_desc *sd2 = rhs->stream_desc[i]; - - if (!sd1) { - PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor")); - pj_pool_release (pool); - return NULL; - } - - session->stream_desc[i] = sd1; - sd1->enc_stream = sd1->dec_stream = NULL; - pj_strdup (pool, &sd1->info.type, &sd2->info.type); - sd1->info.dir = sd2->info.dir; - pj_strdup (pool, &sd1->info.transport, &sd2->info.transport); - pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info)); - pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr); - sd1->info.rem_port = sd2->info.rem_port; - sd1->info.fmt_cnt = sd2->info.fmt_cnt; - pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt)); - } - - return session; -} - -/** - * Create SDP description from the session. - */ -PJ_DEF(pjsdp_session_desc*) -pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool, - pj_bool_t only_first_fmt) -{ - pjsdp_session_desc *sdp; - pj_time_val tv; - unsigned i; - pj_media_sock_info *c_addr = NULL; - - if (session->stream_cnt == 0) { - pj_assert(0); - return NULL; - } - - sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc)); - if (!sdp) { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor")); - return NULL; - } - - pj_gettimeofday(&tv); - - sdp->origin.user = pj_str("-"); - sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL; - sdp->origin.net_type = ID_IN; - sdp->origin.addr_type = ID_IP4; - sdp->origin.addr = *pj_gethostname(); - - sdp->name = ID_SDP_NAME; - - /* If all media addresses are the same, then put the connection - * info in the session level, otherwise put it in media stream - * level. - */ - for (i=0; istream_cnt; ++i) { - if (c_addr == NULL) { - c_addr = &session->stream_desc[i]->info.sock_info; - } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr) - { - c_addr = NULL; - break; - } - } - - if (c_addr) { - /* All addresses are the same, put connection info in session level. */ - sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); - if (!sdp->conn) { - PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info")); - return NULL; - } - - sdp->conn->net_type = ID_IN; - sdp->conn->addr_type = ID_IP4; - pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr)); - } - - sdp->time.start = sdp->time.stop = 0; - sdp->attr_count = 0; - - /* Create each media. */ - sdp->media_count = 0; - for (i=0; istream_cnt; ++i) { - const pj_media_stream_desc *sd = session->stream_desc[i]; - pjsdp_media_desc *m; - unsigned j; - unsigned fmt_cnt; - pjsdp_attr *attr; - - m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc)); - if (!m) { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor")); - return NULL; - } - - sdp->media[sdp->media_count++] = m; - - pj_strdup (pool, &m->desc.media, &sd->info.type); - m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port); - m->desc.port_count = 1; - pj_strdup (pool, &m->desc.transport, &sd->info.transport); - - /* Add format and rtpmap for each codec. */ - m->desc.fmt_count = 0; - m->attr_count = 0; - fmt_cnt = sd->info.fmt_cnt; - if (fmt_cnt > 0 && only_first_fmt) - fmt_cnt = 1; - for (j=0; jdesc.fmt[m->desc.fmt_count++]; - - if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) { - pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name); - } else { - fmt->ptr = pj_pool_alloc(pool, 8); - fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr); - - rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr)); - if (rtpmap) { - m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap; - rtpmap->type = PJSDP_ATTR_RTPMAP; - rtpmap->payload_type = sd->info.fmt[j].pt; - rtpmap->clock_rate = sd->info.fmt[j].sample_rate; - pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name); - } else { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor")); - } - } - } - - /* If we don't have connection info in session level, create one. */ - if (sdp->conn == NULL) { - m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); - if (m->conn) { - m->conn->net_type = ID_IN; - m->conn->addr_type = ID_IP4; - pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr)); - } else { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info")); - return NULL; - } - } - - /* Add additional attribute to the media stream. */ - attr = pj_pool_alloc(pool, sizeof(pjsdp_attr)); - if (!attr) { - PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute")); - return NULL; - } - m->attr[m->attr_count++] = attr; - - switch (sd->info.dir) { - case PJ_MEDIA_DIR_NONE: - attr->type = PJSDP_ATTR_INACTIVE; - break; - case PJ_MEDIA_DIR_ENCODING: - attr->type = PJSDP_ATTR_SEND_ONLY; - break; - case PJ_MEDIA_DIR_DECODING: - attr->type = PJSDP_ATTR_RECV_ONLY; - break; - case PJ_MEDIA_DIR_ENCODING_DECODING: - attr->type = PJSDP_ATTR_SEND_RECV; - break; - } - } - - return sdp; -} - -/** - * Update session with SDP answer from peer. - */ -PJ_DEF(pj_status_t) -pj_media_session_update (pj_media_session_t *session, - const pjsdp_session_desc *sdp) -{ - unsigned i; - unsigned count; - - /* Check SDP */ - if (sdp_check (sdp) != 0) { - return -1; - } - - /* If the media stream count doesn't match, only update one. */ - if (session->stream_cnt != sdp->media_count) { - PJ_LOG(3,(THIS_FILE, "pj_media_session_update : " - "SDP media count mismatch! (rmt=%d, lcl=%d)", - sdp->media_count, session->stream_cnt)); - count = (session->stream_cnt < sdp->media_count) ? - session->stream_cnt : sdp->media_count; - } else { - count = session->stream_cnt; - } - - for (i=0; istream_desc[i]; - const pjsdp_media_desc *m = sdp->media[i]; - const pjsdp_conn_info *conn; - unsigned j; - - /* Check that the session is not active. */ - pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL); - - conn = m->conn ? m->conn : sdp->conn; - pj_assert(conn); - - /* Update remote address. */ - sd->info.rem_port = m->desc.port; - pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr); - - /* Select one active codec according to what peer wants. */ - for (j=0; jinfo.fmt_cnt; ++j) { - unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]); - if (sd->info.fmt[j].pt == fmt_0) { - pj_codec_id temp; - - /* Put active format to the front. */ - if (j == 0) - break; - - pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp)); - pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp)); - pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp)); - break; - } - } - - if (j == sd->info.fmt_cnt) { - /* Peer has answered SDP with new codec, which doesn't exist - * in the offer! - * Mute this media. - */ - PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!")); - sd->info.dir = PJ_MEDIA_DIR_NONE; - continue; - } - - /* Check direction. */ - if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) { - sd->info.dir = PJ_MEDIA_DIR_NONE; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_ENCODING; - } - else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { - sd->info.dir = PJ_MEDIA_DIR_DECODING; - } - else { - sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; - } - } - - return 0; -} - -/** - * Enumerate media streams in the session. - */ -PJ_DEF(unsigned) -pj_media_session_enum_streams (const pj_media_session_t *session, - unsigned count, const pj_media_stream_info *info[]) -{ - unsigned i; - - if (count > session->stream_cnt) - count = session->stream_cnt; - - for (i=0; istream_desc[i]->info; - } - - return session->stream_cnt; -} - -/** - * Get statistics - */ -PJ_DEF(pj_status_t) -pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, - pj_media_stream_stat *tx_stat, - pj_media_stream_stat *rx_stat) -{ - pj_media_stream_desc *sd; - int stat_cnt = 0; - - if (index >= session->stream_cnt) { - pj_assert(0); - return -1; - } - - sd = session->stream_desc[index]; - - if (sd->enc_stream && tx_stat) { - pj_media_stream_get_stat (sd->enc_stream, tx_stat); - ++stat_cnt; - } else if (tx_stat) { - pj_memset (tx_stat, 0, sizeof(*tx_stat)); - } - - if (sd->dec_stream && rx_stat) { - pj_media_stream_get_stat (sd->dec_stream, rx_stat); - ++stat_cnt; - } else if (rx_stat) { - pj_memset (rx_stat, 0, sizeof(*rx_stat)); - } - - return stat_cnt ? 0 : -1; -} - -/** - * Modify stream, only when stream is inactive. - */ -PJ_DEF(pj_status_t) -pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, - unsigned modify_flag, const pj_media_stream_info *info) -{ - pj_media_stream_desc *sd; - - if (index >= session->stream_cnt) { - pj_assert(0); - return -1; - } - - sd = session->stream_desc[index]; - - if (sd->enc_stream || sd->dec_stream) { - pj_assert(0); - return -1; - } - - if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) { - sd->info.dir = info->dir; - } - - return 0; -} - -/** - * Activate media session. - */ -PJ_DEF(pj_status_t) -pj_media_session_activate (pj_media_session_t *session) -{ - unsigned i; - pj_status_t status = 0; - - for (i=0; istream_cnt; ++i) { - pj_status_t rc; - rc = pj_media_session_activate_stream (session, i); - if (status == 0) - status = rc; - } - return status; -} - -/** - * Activate individual stream in media session. - */ -PJ_DEF(pj_status_t) -pj_media_session_activate_stream (pj_media_session_t *session, unsigned index) -{ - pj_media_stream_desc *sd; - pj_media_stream_create_param scp; - pj_status_t status; - pj_time_val tv; - - if (index < 0 || index >= session->stream_cnt) { - pj_assert(0); - return -1; - } - - sd = session->stream_desc[index]; - - if (sd->enc_stream || sd->dec_stream) { - /* Stream already active. */ - pj_assert(0); - return 0; - } - - pj_gettimeofday(&tv); - - /* Initialize parameter to create stream. */ - pj_memset (&scp, 0, sizeof(scp)); - scp.codec_id = &sd->info.fmt[0]; - scp.mediamgr = session->mediamgr; - scp.dir = sd->info.dir; - scp.rtp_sock = sd->info.sock_info.rtp_sock; - scp.rtcp_sock = sd->info.sock_info.rtcp_sock; - scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in)); - pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port); - scp.ssrc = tv.sec; - scp.jb_min = 1; - scp.jb_max = 15; - scp.jb_maxcnt = 16; - - /* Build the stream! */ - status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp); - - if (status==0 && sd->enc_stream) { - status = pj_media_stream_start (sd->enc_stream); - if (status != 0) - goto on_error; - } - if (status==0 && sd->dec_stream) { - status = pj_media_stream_start (sd->dec_stream); - if (status != 0) - goto on_error; - } - return status; - -on_error: - if (sd->enc_stream) { - pj_media_stream_destroy (sd->enc_stream); - sd->enc_stream = NULL; - } - if (sd->dec_stream) { - pj_media_stream_destroy (sd->dec_stream); - sd->dec_stream = NULL; - } - return status; -} - -/** - * Destroy media session. - */ -PJ_DEF(pj_status_t) -pj_media_session_destroy (pj_media_session_t *session) -{ - unsigned i; - - if (!session) - return -1; - - for (i=0; istream_cnt; ++i) { - pj_media_stream_desc *sd = session->stream_desc[i]; - - if (sd->enc_stream) { - pj_media_stream_destroy (sd->enc_stream); - sd->enc_stream = NULL; - } - if (sd->dec_stream) { - pj_media_stream_destroy (sd->dec_stream); - sd->dec_stream = NULL; - } - } - pj_pool_release (session->pool); - return 0; -} - +/* $Header: /pjproject/pjmedia/src/pjmedia/session.c 10 6/14/05 12:54a Bennylp $ */ +#include +#include +#include +#include +#include + +typedef struct pj_media_stream_desc +{ + pj_media_stream_info info; + pj_media_stream_t *enc_stream, *dec_stream; +} pj_media_stream_desc; + +struct pj_media_session_t +{ + pj_pool_t *pool; + pj_med_mgr_t *mediamgr; + unsigned stream_cnt; + pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA]; +}; + +#define THIS_FILE "session.c" + +#define PJ_MEDIA_SESSION_SIZE (48*1024) +#define PJ_MEDIA_SESSION_INC 1024 + +static const pj_str_t ID_AUDIO = { "audio", 5}; +static const pj_str_t ID_VIDEO = { "video", 5}; +static const pj_str_t ID_IN = { "IN", 2 }; +static const pj_str_t ID_IP4 = { "IP4", 3}; +static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; +static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; + +static void session_init (pj_media_session_t *ses) +{ + pj_memset (ses, 0, sizeof(pj_media_session_t)); +} + + +/** + * Create new session offering. + */ +PJ_DEF(pj_media_session_t*) +pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info) +{ + pj_pool_factory *pf; + pj_pool_t *pool; + pj_media_session_t *session; + pj_media_stream_desc *sd; + unsigned i, codec_cnt; + pj_codec_mgr *cm; + const pj_codec_id *codecs[PJSDP_MAX_FMT]; + + pf = pj_med_mgr_get_pool_factory(mgr); + + pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); + if (!pool) + return NULL; + + session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); + if (!session) + return NULL; + + session_init (session); + + session->pool = pool; + session->mediamgr = mgr; + + /* Create first stream */ + sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); + if (!sd) + return NULL; + + sd->info.type = ID_AUDIO; + sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; + sd->info.transport = ID_RTP_AVP; + pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); + + /* Enum audio codecs. */ + sd->info.fmt_cnt = 0; + cm = pj_med_mgr_get_codec_mgr (mgr); + codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs); + if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; + for (i=0; itype != PJ_MEDIA_TYPE_AUDIO) + continue; + + sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt; + sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate; + pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name); + ++sd->info.fmt_cnt; + } + + session->stream_desc[session->stream_cnt++] = sd; + + return session; +} + +static int sdp_check (const pjsdp_session_desc *sdp) +{ + int has_conn = 0; + unsigned i; + + if (sdp->conn) + has_conn = 1; + + if (sdp->media_count == 0) { + PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition")); + return -1; + } + + for (i=0; imedia_count; ++i) { + pjsdp_media_desc *m = sdp->media[i]; + + if (!m) { + pj_assert(0); + return -1; + } + + if (m->desc.fmt_count == 0) { + PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream")); + return -1; + } + + if (!has_conn && m->conn == NULL) { + PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media")); + return -1; + } + } + + return 0; +} + +/* + * Create local stream definition that matches SDP received from peer. + */ +static pj_media_stream_desc* +create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn, + const pjsdp_media_desc *m, const pj_media_sock_info *sock_info) +{ + pj_media_stream_desc *sd; + + sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc)); + if (!sd) { + PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor")); + return NULL; + } + + if (pj_stricmp(&conn->net_type, &ID_IN)==0 && + pj_stricmp(&conn->addr_type, &ID_IP4)==0 && + pj_stricmp(&m->desc.media, &ID_AUDIO)==0 && + pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0) + { + /* + * Got audio stream. + */ + unsigned i, codec_cnt; + pj_codec_mgr *cm; + const pj_codec_id *codecs[PJSDP_MAX_FMT]; + + sd->info.type = ID_AUDIO; + sd->info.transport = ID_RTP_AVP; + pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info)); + sd->info.rem_port = m->desc.port; + pj_strdup (pool, &sd->info.rem_addr, &conn->addr); + + /* Enum audio codecs. */ + sd->info.fmt_cnt = 0; + cm = pj_med_mgr_get_codec_mgr (mgr); + codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs); + if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT; + + /* Find just one codec which we can support. */ + for (i=0; idesc.fmt_count && sd->info.fmt_cnt == 0; ++i) { + unsigned j, fmt_i; + + /* For static payload, just match payload type. */ + /* Else match clock rate and encoding name. */ + fmt_i = pj_strtoul(&m->desc.fmt[i]); + if (fmt_i < PJ_RTP_PT_DYNAMIC) { + for (j=0; jpt == fmt_i) { + sd->info.fmt_cnt = 1; + sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; + sd->info.fmt[0].pt = codecs[j]->pt; + sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; + pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name); + break; + } + } + } else { + + /* Find the rtpmap for the payload type. */ + const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i); + + /* Don't accept the media if no rtpmap for dynamic PT. */ + if (rtpmap == NULL) { + PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i])); + continue; + } + + /* Check whether we can take this codec. */ + for (j=0; jclock_rate == codecs[j]->sample_rate && + pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0) + { + sd->info.fmt_cnt = 1; + sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO; + sd->info.fmt[0].pt = codecs[j]->pt; + sd->info.fmt[0].sample_rate = codecs[j]->sample_rate; + sd->info.fmt[0].encoding_name = codecs[j]->encoding_name; + break; + } + } + } + } + + /* Match codec and direction. */ + if (sd->info.fmt_cnt == 0 || m->desc.port == 0 || + pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) + { + sd->info.dir = PJ_MEDIA_DIR_NONE; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_ENCODING; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_DECODING; + } + else { + sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; + } + + } else { + /* Unsupported media stream. */ + unsigned fmt_num; + const pjsdp_rtpmap_attr *rtpmap = NULL; + + pj_strdup(pool, &sd->info.type, &m->desc.media); + pj_strdup(pool, &sd->info.transport, &m->desc.transport); + pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info)); + pj_strdup (pool, &sd->info.rem_addr, &conn->addr); + sd->info.rem_port = m->desc.port; + + /* Just put one format and rtpmap, so that we don't have to make + * special exception when we convert this stream to SDP. + */ + + /* Find the rtpmap for the payload type. */ + fmt_num = pj_strtoul(&m->desc.fmt[0]); + rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num); + + sd->info.fmt_cnt = 1; + if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) { + sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO; + sd->info.fmt[0].pt = fmt_num; + if (rtpmap) { + pj_strdup (pool, &sd->info.fmt[0].encoding_name, + &rtpmap->encoding_name); + sd->info.fmt[0].sample_rate = rtpmap->clock_rate; + } + } else { + sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN; + pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]); + } + + sd->info.dir = PJ_MEDIA_DIR_NONE; + } + + return sd; +} + +/** + * Create new session based on peer's offering. + */ +PJ_DEF(pj_media_session_t*) +pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, + const pj_media_sock_info *sock_info) +{ + pj_pool_factory *pf; + pj_pool_t *pool; + pj_media_session_t *session; + unsigned i; + + if (sdp_check(sdp) != 0) + return NULL; + + pf = pj_med_mgr_get_pool_factory(mgr); + pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); + if (!pool) + return NULL; + + session = pj_pool_alloc(pool, sizeof(pj_media_session_t)); + if (!session) { + PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); + pj_pool_release (pool); + return NULL; + } + + session_init (session); + + session->pool = pool; + session->mediamgr = mgr; + + /* Enumerate each media stream and create our peer. */ + for (i=0; imedia_count; ++i) { + const pjsdp_conn_info *conn; + const pjsdp_media_desc *m; + pj_media_stream_desc *sd; + + m = sdp->media[i]; + conn = m->conn ? m->conn : sdp->conn; + + /* + * Bug: + * the sock_info below is used by more than one 'm' lines + */ + PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES) + + sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info); + pj_assert (sd); + + session->stream_desc[session->stream_cnt++] = sd; + } + + return session; +} + +/** + * Duplicate session. The new session is inactive. + */ +PJ_DEF(pj_media_session_t*) +pj_media_session_clone (const pj_media_session_t *rhs) +{ + pj_pool_factory *pf; + pj_pool_t *pool; + pj_media_session_t *session; + unsigned i; + + pf = pj_med_mgr_get_pool_factory(rhs->mediamgr); + pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL); + if (!pool) { + return NULL; + } + + session = pj_pool_alloc (pool, sizeof(*session)); + if (!session) { + PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor")); + pj_pool_release (pool); + return NULL; + } + + session->pool = pool; + session->mediamgr = rhs->mediamgr; + session->stream_cnt = rhs->stream_cnt; + + for (i=0; istream_cnt; ++i) { + pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc)); + const pj_media_stream_desc *sd2 = rhs->stream_desc[i]; + + if (!sd1) { + PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor")); + pj_pool_release (pool); + return NULL; + } + + session->stream_desc[i] = sd1; + sd1->enc_stream = sd1->dec_stream = NULL; + pj_strdup (pool, &sd1->info.type, &sd2->info.type); + sd1->info.dir = sd2->info.dir; + pj_strdup (pool, &sd1->info.transport, &sd2->info.transport); + pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info)); + pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr); + sd1->info.rem_port = sd2->info.rem_port; + sd1->info.fmt_cnt = sd2->info.fmt_cnt; + pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt)); + } + + return session; +} + +/** + * Create SDP description from the session. + */ +PJ_DEF(pjsdp_session_desc*) +pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool, + pj_bool_t only_first_fmt) +{ + pjsdp_session_desc *sdp; + pj_time_val tv; + unsigned i; + pj_media_sock_info *c_addr = NULL; + + if (session->stream_cnt == 0) { + pj_assert(0); + return NULL; + } + + sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc)); + if (!sdp) { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor")); + return NULL; + } + + pj_gettimeofday(&tv); + + sdp->origin.user = pj_str("-"); + sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL; + sdp->origin.net_type = ID_IN; + sdp->origin.addr_type = ID_IP4; + sdp->origin.addr = *pj_gethostname(); + + sdp->name = ID_SDP_NAME; + + /* If all media addresses are the same, then put the connection + * info in the session level, otherwise put it in media stream + * level. + */ + for (i=0; istream_cnt; ++i) { + if (c_addr == NULL) { + c_addr = &session->stream_desc[i]->info.sock_info; + } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr) + { + c_addr = NULL; + break; + } + } + + if (c_addr) { + /* All addresses are the same, put connection info in session level. */ + sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); + if (!sdp->conn) { + PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info")); + return NULL; + } + + sdp->conn->net_type = ID_IN; + sdp->conn->addr_type = ID_IP4; + pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr)); + } + + sdp->time.start = sdp->time.stop = 0; + sdp->attr_count = 0; + + /* Create each media. */ + sdp->media_count = 0; + for (i=0; istream_cnt; ++i) { + const pj_media_stream_desc *sd = session->stream_desc[i]; + pjsdp_media_desc *m; + unsigned j; + unsigned fmt_cnt; + pjsdp_attr *attr; + + m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc)); + if (!m) { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor")); + return NULL; + } + + sdp->media[sdp->media_count++] = m; + + pj_strdup (pool, &m->desc.media, &sd->info.type); + m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port); + m->desc.port_count = 1; + pj_strdup (pool, &m->desc.transport, &sd->info.transport); + + /* Add format and rtpmap for each codec. */ + m->desc.fmt_count = 0; + m->attr_count = 0; + fmt_cnt = sd->info.fmt_cnt; + if (fmt_cnt > 0 && only_first_fmt) + fmt_cnt = 1; + for (j=0; jdesc.fmt[m->desc.fmt_count++]; + + if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) { + pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name); + } else { + fmt->ptr = pj_pool_alloc(pool, 8); + fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr); + + rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr)); + if (rtpmap) { + m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap; + rtpmap->type = PJSDP_ATTR_RTPMAP; + rtpmap->payload_type = sd->info.fmt[j].pt; + rtpmap->clock_rate = sd->info.fmt[j].sample_rate; + pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name); + } else { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor")); + } + } + } + + /* If we don't have connection info in session level, create one. */ + if (sdp->conn == NULL) { + m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info)); + if (m->conn) { + m->conn->net_type = ID_IN; + m->conn->addr_type = ID_IP4; + pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr)); + } else { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info")); + return NULL; + } + } + + /* Add additional attribute to the media stream. */ + attr = pj_pool_alloc(pool, sizeof(pjsdp_attr)); + if (!attr) { + PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute")); + return NULL; + } + m->attr[m->attr_count++] = attr; + + switch (sd->info.dir) { + case PJ_MEDIA_DIR_NONE: + attr->type = PJSDP_ATTR_INACTIVE; + break; + case PJ_MEDIA_DIR_ENCODING: + attr->type = PJSDP_ATTR_SEND_ONLY; + break; + case PJ_MEDIA_DIR_DECODING: + attr->type = PJSDP_ATTR_RECV_ONLY; + break; + case PJ_MEDIA_DIR_ENCODING_DECODING: + attr->type = PJSDP_ATTR_SEND_RECV; + break; + } + } + + return sdp; +} + +/** + * Update session with SDP answer from peer. + */ +PJ_DEF(pj_status_t) +pj_media_session_update (pj_media_session_t *session, + const pjsdp_session_desc *sdp) +{ + unsigned i; + unsigned count; + + /* Check SDP */ + if (sdp_check (sdp) != 0) { + return -1; + } + + /* If the media stream count doesn't match, only update one. */ + if (session->stream_cnt != sdp->media_count) { + PJ_LOG(3,(THIS_FILE, "pj_media_session_update : " + "SDP media count mismatch! (rmt=%d, lcl=%d)", + sdp->media_count, session->stream_cnt)); + count = (session->stream_cnt < sdp->media_count) ? + session->stream_cnt : sdp->media_count; + } else { + count = session->stream_cnt; + } + + for (i=0; istream_desc[i]; + const pjsdp_media_desc *m = sdp->media[i]; + const pjsdp_conn_info *conn; + unsigned j; + + /* Check that the session is not active. */ + pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL); + + conn = m->conn ? m->conn : sdp->conn; + pj_assert(conn); + + /* Update remote address. */ + sd->info.rem_port = m->desc.port; + pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr); + + /* Select one active codec according to what peer wants. */ + for (j=0; jinfo.fmt_cnt; ++j) { + unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]); + if (sd->info.fmt[j].pt == fmt_0) { + pj_codec_id temp; + + /* Put active format to the front. */ + if (j == 0) + break; + + pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp)); + pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp)); + pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp)); + break; + } + } + + if (j == sd->info.fmt_cnt) { + /* Peer has answered SDP with new codec, which doesn't exist + * in the offer! + * Mute this media. + */ + PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!")); + sd->info.dir = PJ_MEDIA_DIR_NONE; + continue; + } + + /* Check direction. */ + if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) { + sd->info.dir = PJ_MEDIA_DIR_NONE; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_ENCODING; + } + else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) { + sd->info.dir = PJ_MEDIA_DIR_DECODING; + } + else { + sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING; + } + } + + return 0; +} + +/** + * Enumerate media streams in the session. + */ +PJ_DEF(unsigned) +pj_media_session_enum_streams (const pj_media_session_t *session, + unsigned count, const pj_media_stream_info *info[]) +{ + unsigned i; + + if (count > session->stream_cnt) + count = session->stream_cnt; + + for (i=0; istream_desc[i]->info; + } + + return session->stream_cnt; +} + +/** + * Get statistics + */ +PJ_DEF(pj_status_t) +pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, + pj_media_stream_stat *tx_stat, + pj_media_stream_stat *rx_stat) +{ + pj_media_stream_desc *sd; + int stat_cnt = 0; + + if (index >= session->stream_cnt) { + pj_assert(0); + return -1; + } + + sd = session->stream_desc[index]; + + if (sd->enc_stream && tx_stat) { + pj_media_stream_get_stat (sd->enc_stream, tx_stat); + ++stat_cnt; + } else if (tx_stat) { + pj_memset (tx_stat, 0, sizeof(*tx_stat)); + } + + if (sd->dec_stream && rx_stat) { + pj_media_stream_get_stat (sd->dec_stream, rx_stat); + ++stat_cnt; + } else if (rx_stat) { + pj_memset (rx_stat, 0, sizeof(*rx_stat)); + } + + return stat_cnt ? 0 : -1; +} + +/** + * Modify stream, only when stream is inactive. + */ +PJ_DEF(pj_status_t) +pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, + unsigned modify_flag, const pj_media_stream_info *info) +{ + pj_media_stream_desc *sd; + + if (index >= session->stream_cnt) { + pj_assert(0); + return -1; + } + + sd = session->stream_desc[index]; + + if (sd->enc_stream || sd->dec_stream) { + pj_assert(0); + return -1; + } + + if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) { + sd->info.dir = info->dir; + } + + return 0; +} + +/** + * Activate media session. + */ +PJ_DEF(pj_status_t) +pj_media_session_activate (pj_media_session_t *session) +{ + unsigned i; + pj_status_t status = 0; + + for (i=0; istream_cnt; ++i) { + pj_status_t rc; + rc = pj_media_session_activate_stream (session, i); + if (status == 0) + status = rc; + } + return status; +} + +/** + * Activate individual stream in media session. + */ +PJ_DEF(pj_status_t) +pj_media_session_activate_stream (pj_media_session_t *session, unsigned index) +{ + pj_media_stream_desc *sd; + pj_media_stream_create_param scp; + pj_status_t status; + pj_time_val tv; + + if (index < 0 || index >= session->stream_cnt) { + pj_assert(0); + return -1; + } + + sd = session->stream_desc[index]; + + if (sd->enc_stream || sd->dec_stream) { + /* Stream already active. */ + pj_assert(0); + return 0; + } + + pj_gettimeofday(&tv); + + /* Initialize parameter to create stream. */ + pj_memset (&scp, 0, sizeof(scp)); + scp.codec_id = &sd->info.fmt[0]; + scp.mediamgr = session->mediamgr; + scp.dir = sd->info.dir; + scp.rtp_sock = sd->info.sock_info.rtp_sock; + scp.rtcp_sock = sd->info.sock_info.rtcp_sock; + scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in)); + pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port); + scp.ssrc = tv.sec; + scp.jb_min = 1; + scp.jb_max = 15; + scp.jb_maxcnt = 16; + + /* Build the stream! */ + status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp); + + if (status==0 && sd->enc_stream) { + status = pj_media_stream_start (sd->enc_stream); + if (status != 0) + goto on_error; + } + if (status==0 && sd->dec_stream) { + status = pj_media_stream_start (sd->dec_stream); + if (status != 0) + goto on_error; + } + return status; + +on_error: + if (sd->enc_stream) { + pj_media_stream_destroy (sd->enc_stream); + sd->enc_stream = NULL; + } + if (sd->dec_stream) { + pj_media_stream_destroy (sd->dec_stream); + sd->dec_stream = NULL; + } + return status; +} + +/** + * Destroy media session. + */ +PJ_DEF(pj_status_t) +pj_media_session_destroy (pj_media_session_t *session) +{ + unsigned i; + + if (!session) + return -1; + + for (i=0; istream_cnt; ++i) { + pj_media_stream_desc *sd = session->stream_desc[i]; + + if (sd->enc_stream) { + pj_media_stream_destroy (sd->enc_stream); + sd->enc_stream = NULL; + } + if (sd->dec_stream) { + pj_media_stream_destroy (sd->dec_stream); + sd->dec_stream = NULL; + } + } + pj_pool_release (session->pool); + return 0; +} + diff --git a/pjmedia/src/pjmedia/session.h b/pjmedia/src/pjmedia/session.h index ce8cc2d15..281e6f305 100644 --- a/pjmedia/src/pjmedia/session.h +++ b/pjmedia/src/pjmedia/session.h @@ -1,136 +1,136 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/session.h 8 8/24/05 10:30a Bennylp $ */ - -#ifndef __PJMEDIA_SESSION_H__ -#define __PJMEDIA_SESSION_H__ - - -/** - * @file session.h - * @brief Media Session. - */ - -#include -#include -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJMED_SES Media session - * @ingroup PJMEDIA - * @{ - */ - -/** Opaque declaration of media session. */ -typedef struct pj_media_session_t pj_media_session_t; - -/** Media socket info. */ -typedef struct pj_media_sock_info -{ - pj_sock_t rtp_sock, rtcp_sock; - pj_sockaddr_in rtp_addr_name; -} pj_media_sock_info; - -/** Stream info. */ -typedef struct pj_media_stream_info -{ - pj_str_t type; - pj_media_dir_t dir; - pj_str_t transport; - pj_media_sock_info sock_info; - pj_str_t rem_addr; - unsigned short rem_port; - unsigned fmt_cnt; - pj_codec_id fmt[PJSDP_MAX_FMT]; - -} pj_media_stream_info; - -/** Flag for modifying stream. */ -enum -{ - PJ_MEDIA_STREAM_MODIFY_DIR = 1, -}; - -/** - * Create new session offering. - */ -PJ_DECL(pj_media_session_t*) -pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo ); - -/** - * Create new session based on peer's offering. - */ -PJ_DECL(pj_media_session_t*) -pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, - const pj_media_sock_info *skinfo); - -/** - * Duplicate session. The new session is inactive. - */ -PJ_DECL(pj_media_session_t*) -pj_media_session_clone (const pj_media_session_t *session); - -/** - * Create SDP description from the session. - */ -PJ_DECL(pjsdp_session_desc*) -pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool, - pj_bool_t only_first_fmt); - -/** - * Update session with SDP answer from peer. The session must NOT active. - */ -PJ_DECL(pj_status_t) -pj_media_session_update ( pj_media_session_t *session, - const pjsdp_session_desc *sdp); - -/** - * Enumerate media streams in the session. - * @return the actual number of streams. - */ -PJ_DECL(unsigned) -pj_media_session_enum_streams (const pj_media_session_t *session, - unsigned count, const pj_media_stream_info *info[]); - -/** - * Get stream statistics. - */ -PJ_DECL(pj_status_t) -pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, - pj_media_stream_stat *tx_stat, - pj_media_stream_stat *rx_stat); - -/** - * Modify stream, only when stream is inactive. - */ -PJ_DECL(pj_status_t) -pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, - unsigned modify_flag, const pj_media_stream_info *info); - -/** - * Activate all streams in media session. - */ -PJ_DECL(pj_status_t) -pj_media_session_activate (pj_media_session_t *session); - -/** - * Activate individual stream in media session. - */ -PJ_DECL(pj_status_t) -pj_media_session_activate_stream (pj_media_session_t *session, unsigned index); - -/** - * Destroy media session. - */ -PJ_DECL(pj_status_t) -pj_media_session_destroy (pj_media_session_t *session); - - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJMEDIA_SESSION_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/session.h 8 8/24/05 10:30a Bennylp $ */ + +#ifndef __PJMEDIA_SESSION_H__ +#define __PJMEDIA_SESSION_H__ + + +/** + * @file session.h + * @brief Media Session. + */ + +#include +#include +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJMED_SES Media session + * @ingroup PJMEDIA + * @{ + */ + +/** Opaque declaration of media session. */ +typedef struct pj_media_session_t pj_media_session_t; + +/** Media socket info. */ +typedef struct pj_media_sock_info +{ + pj_sock_t rtp_sock, rtcp_sock; + pj_sockaddr_in rtp_addr_name; +} pj_media_sock_info; + +/** Stream info. */ +typedef struct pj_media_stream_info +{ + pj_str_t type; + pj_media_dir_t dir; + pj_str_t transport; + pj_media_sock_info sock_info; + pj_str_t rem_addr; + unsigned short rem_port; + unsigned fmt_cnt; + pj_codec_id fmt[PJSDP_MAX_FMT]; + +} pj_media_stream_info; + +/** Flag for modifying stream. */ +enum +{ + PJ_MEDIA_STREAM_MODIFY_DIR = 1, +}; + +/** + * Create new session offering. + */ +PJ_DECL(pj_media_session_t*) +pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo ); + +/** + * Create new session based on peer's offering. + */ +PJ_DECL(pj_media_session_t*) +pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp, + const pj_media_sock_info *skinfo); + +/** + * Duplicate session. The new session is inactive. + */ +PJ_DECL(pj_media_session_t*) +pj_media_session_clone (const pj_media_session_t *session); + +/** + * Create SDP description from the session. + */ +PJ_DECL(pjsdp_session_desc*) +pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool, + pj_bool_t only_first_fmt); + +/** + * Update session with SDP answer from peer. The session must NOT active. + */ +PJ_DECL(pj_status_t) +pj_media_session_update ( pj_media_session_t *session, + const pjsdp_session_desc *sdp); + +/** + * Enumerate media streams in the session. + * @return the actual number of streams. + */ +PJ_DECL(unsigned) +pj_media_session_enum_streams (const pj_media_session_t *session, + unsigned count, const pj_media_stream_info *info[]); + +/** + * Get stream statistics. + */ +PJ_DECL(pj_status_t) +pj_media_session_get_stat (const pj_media_session_t *session, unsigned index, + pj_media_stream_stat *tx_stat, + pj_media_stream_stat *rx_stat); + +/** + * Modify stream, only when stream is inactive. + */ +PJ_DECL(pj_status_t) +pj_media_session_modify_stream (pj_media_session_t *session, unsigned index, + unsigned modify_flag, const pj_media_stream_info *info); + +/** + * Activate all streams in media session. + */ +PJ_DECL(pj_status_t) +pj_media_session_activate (pj_media_session_t *session); + +/** + * Activate individual stream in media session. + */ +PJ_DECL(pj_status_t) +pj_media_session_activate_stream (pj_media_session_t *session, unsigned index); + +/** + * Destroy media session. + */ +PJ_DECL(pj_status_t) +pj_media_session_destroy (pj_media_session_t *session); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJMEDIA_SESSION_H__ */ diff --git a/pjmedia/src/pjmedia/sound.h b/pjmedia/src/pjmedia/sound.h index e6963663f..9e0066210 100644 --- a/pjmedia/src/pjmedia/sound.h +++ b/pjmedia/src/pjmedia/sound.h @@ -1,185 +1,185 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/sound.h 8 8/24/05 10:30a Bennylp $ */ - -#ifndef __PJMEDIA_SOUND_H__ -#define __PJMEDIA_SOUND_H__ - - -/** - * @file sound.h - * @brief Sound player and recorder device framework. - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJMED_SND Sound device abstraction. - * @ingroup PJMEDIA - * @{ - */ - -/** Opaque data type for audio stream. */ -typedef struct pj_snd_stream pj_snd_stream; - -/** - * Device information structure returned by #pj_snd_get_dev_info. - */ -typedef struct pj_snd_dev_info -{ - char name[64]; /**< Device name. */ - unsigned input_count; /**< Max number of input channels. */ - unsigned output_count; /**< Max number of output channels. */ - unsigned default_samples_per_sec;/**< Default sampling rate. */ -} pj_snd_dev_info; - -/** - * Sound device parameter, to be specified when calling #pj_snd_open_recorder - * or #pj_snd_open_player. - */ -typedef struct pj_snd_stream_info -{ - unsigned samples_per_sec; /* Sampling rate. */ - unsigned bits_per_sample; /* No of bits per sample. */ - unsigned samples_per_frame; /* No of samples per frame. */ - unsigned bytes_per_frame; /* No of bytes per frame. */ - unsigned frames_per_packet; /* No of frames per packet. */ -} pj_snd_stream_info; - -/** - * This callback is called by player stream when it needs additional data - * to be played by the device. Application must fill in the whole of output - * buffer with sound samples. - * - * @param user_data User data associated with the stream. - * @param timestamp Timestamp, in samples. - * @param output Buffer to be filled out by application. - * @param size The size requested in bytes, which will be equal to - * the size of one whole packet. - * - * @return Non-zero to stop the stream. - */ -typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data, - /* in */ pj_uint32_t timestamp, - /* out */ void *output, - /* out */ unsigned size); - -/** - * This callback is called by recorder stream when it has captured the whole - * packet worth of audio samples. - * - * @param user_data User data associated with the stream. - * @param timestamp Timestamp, in samples. - * @param output Buffer containing the captured audio samples. - * @param size The size of the data in the buffer, in bytes. - * - * @return Non-zero to stop the stream. - */ -typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data, - /* in */ pj_uint32_t timestamp, - /* in */ const void *input, - /* in*/ unsigned size); - -/** - * Init the sound library. - * - * @param factory The sound factory. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory); - - -/** - * Get the number of devices detected by the library. - * - * @return Number of devices. - */ -PJ_DECL(int) pj_snd_get_dev_count(void); - - -/** - * Get device info. - * - * @param index The index of the device, which should be in the range - * from zero to #pj_snd_get_dev_count - 1. - */ -PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index); - - -/** - * Create a new audio stream for audio capture purpose. - * - * @param index Device index, or -1 to let the library choose the first - * available device, or -2 to use NULL device. - * @param param Stream parameters. - * @param rec_cb Callback to handle captured audio samples. - * @param user_data User data to be associated with the stream. - * - * @return Audio stream, or NULL if failed. - */ -PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index, - const pj_snd_stream_info *param, - pj_snd_rec_cb rec_cb, - void *user_data); - -/** - * Create a new audio stream for playing audio samples. - * - * @param index Device index, or -1 to let the library choose the first - * available device, or -2 to use NULL device. - * @param param Stream parameters. - * @param play_cb Callback to supply audio samples. - * @param user_data User data to be associated with the stream. - * - * @return Audio stream, or NULL if failed. - */ -PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index, - const pj_snd_stream_info *param, - pj_snd_play_cb play_cb, - void *user_data); - -/** - * Start the stream. - * - * @param stream The recorder or player stream. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream); - -/** - * Stop the stream. - * - * @param stream The recorder or player stream. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream); - -/** - * Destroy the stream. - * - * @param stream The recorder of player stream. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream); - -/** - * Deinitialize sound library. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pj_snd_deinit(void); - - - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJMEDIA_SOUND_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/sound.h 8 8/24/05 10:30a Bennylp $ */ + +#ifndef __PJMEDIA_SOUND_H__ +#define __PJMEDIA_SOUND_H__ + + +/** + * @file sound.h + * @brief Sound player and recorder device framework. + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJMED_SND Sound device abstraction. + * @ingroup PJMEDIA + * @{ + */ + +/** Opaque data type for audio stream. */ +typedef struct pj_snd_stream pj_snd_stream; + +/** + * Device information structure returned by #pj_snd_get_dev_info. + */ +typedef struct pj_snd_dev_info +{ + char name[64]; /**< Device name. */ + unsigned input_count; /**< Max number of input channels. */ + unsigned output_count; /**< Max number of output channels. */ + unsigned default_samples_per_sec;/**< Default sampling rate. */ +} pj_snd_dev_info; + +/** + * Sound device parameter, to be specified when calling #pj_snd_open_recorder + * or #pj_snd_open_player. + */ +typedef struct pj_snd_stream_info +{ + unsigned samples_per_sec; /* Sampling rate. */ + unsigned bits_per_sample; /* No of bits per sample. */ + unsigned samples_per_frame; /* No of samples per frame. */ + unsigned bytes_per_frame; /* No of bytes per frame. */ + unsigned frames_per_packet; /* No of frames per packet. */ +} pj_snd_stream_info; + +/** + * This callback is called by player stream when it needs additional data + * to be played by the device. Application must fill in the whole of output + * buffer with sound samples. + * + * @param user_data User data associated with the stream. + * @param timestamp Timestamp, in samples. + * @param output Buffer to be filled out by application. + * @param size The size requested in bytes, which will be equal to + * the size of one whole packet. + * + * @return Non-zero to stop the stream. + */ +typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* out */ void *output, + /* out */ unsigned size); + +/** + * This callback is called by recorder stream when it has captured the whole + * packet worth of audio samples. + * + * @param user_data User data associated with the stream. + * @param timestamp Timestamp, in samples. + * @param output Buffer containing the captured audio samples. + * @param size The size of the data in the buffer, in bytes. + * + * @return Non-zero to stop the stream. + */ +typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* in */ const void *input, + /* in*/ unsigned size); + +/** + * Init the sound library. + * + * @param factory The sound factory. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory); + + +/** + * Get the number of devices detected by the library. + * + * @return Number of devices. + */ +PJ_DECL(int) pj_snd_get_dev_count(void); + + +/** + * Get device info. + * + * @param index The index of the device, which should be in the range + * from zero to #pj_snd_get_dev_count - 1. + */ +PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index); + + +/** + * Create a new audio stream for audio capture purpose. + * + * @param index Device index, or -1 to let the library choose the first + * available device, or -2 to use NULL device. + * @param param Stream parameters. + * @param rec_cb Callback to handle captured audio samples. + * @param user_data User data to be associated with the stream. + * + * @return Audio stream, or NULL if failed. + */ +PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index, + const pj_snd_stream_info *param, + pj_snd_rec_cb rec_cb, + void *user_data); + +/** + * Create a new audio stream for playing audio samples. + * + * @param index Device index, or -1 to let the library choose the first + * available device, or -2 to use NULL device. + * @param param Stream parameters. + * @param play_cb Callback to supply audio samples. + * @param user_data User data to be associated with the stream. + * + * @return Audio stream, or NULL if failed. + */ +PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index, + const pj_snd_stream_info *param, + pj_snd_play_cb play_cb, + void *user_data); + +/** + * Start the stream. + * + * @param stream The recorder or player stream. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream); + +/** + * Stop the stream. + * + * @param stream The recorder or player stream. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream); + +/** + * Destroy the stream. + * + * @param stream The recorder of player stream. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream); + +/** + * Deinitialize sound library. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pj_snd_deinit(void); + + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_SOUND_H__ */ diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index ed7d612e5..8ca9989ff 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -1,628 +1,628 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/stream.c 14 6/24/05 11:14p Bennylp $ */ - -#include -#include -#include -#include -#include -#include -#include /* memcpy() */ -#include -#include - -#define THISFILE "stream.c" -#define ERRLEVEL 1 - -#define PJ_MAX_FRAME_DURATION_MS 200 -#define PJ_MAX_BUFFER_SIZE_MS 2000 -#define PJ_MAX_MTU 1500 - -struct jb_frame -{ - unsigned size; - void *buf; -}; - -#define pj_fifobuf_alloc(fifo,size) malloc(size) -#define pj_fifobuf_unalloc(fifo,buf) free(buf) -#define pj_fifobuf_free(fifo, buf) free(buf) - -enum stream_state -{ - STREAM_STOPPED, - STREAM_STARTED, -}; - -struct pj_media_stream_t -{ - pj_media_dir_t dir; - int pt; - int state; - pj_media_stream_stat stat; - pj_media_stream_t *peer; - pj_snd_stream_info snd_info; - pj_snd_stream *snd_stream; - pj_mutex_t *mutex; - unsigned in_pkt_size; - void *in_pkt; - unsigned out_pkt_size; - void *out_pkt; - unsigned pcm_buf_size; - void *pcm_buf; - //pj_fifobuf_t fifobuf; - pj_codec_mgr *codec_mgr; - pj_codec *codec; - pj_rtp_session rtp; - pj_rtcp_session *rtcp; - pj_jitter_buffer *jb; - pj_sock_t rtp_sock; - pj_sock_t rtcp_sock; - pj_sockaddr_in dst_addr; - pj_thread_t *transport_thread; - int thread_quit_flag; -}; - - -static pj_status_t play_callback(/* in */ void *user_data, - /* in */ pj_uint32_t timestamp, - /* out */ void *frame, - /*inout*/ unsigned size) -{ - pj_media_stream_t *channel = user_data; - struct jb_frame *jb_frame; - void *p; - pj_uint32_t extseq; - pj_status_t status; - struct pj_audio_frame frame_in, frame_out; - - PJ_UNUSED_ARG(timestamp) - - /* Lock mutex */ - pj_mutex_lock (channel->mutex); - - if (!channel->codec) { - pj_mutex_unlock (channel->mutex); - return -1; - } - - /* Get frame from jitter buffer. */ - status = pj_jb_get (channel->jb, &extseq, &p); - jb_frame = p; - if (status != 0 || jb_frame == NULL) { - pj_memset(frame, 0, size); - pj_mutex_unlock(channel->mutex); - return 0; - } - - /* Decode */ - frame_in.buf = jb_frame->buf; - frame_in.size = jb_frame->size; - frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */ - frame_out.buf = channel->pcm_buf; - status = channel->codec->op->decode (channel->codec, &frame_in, - channel->pcm_buf_size, &frame_out); - if (status != 0) { - PJ_LOG(3, (THISFILE, "decode() has return error status %d", - status)); - - pj_memset(frame, 0, size); - pj_fifobuf_free (&channel->fifobuf, jb_frame); - pj_mutex_unlock(channel->mutex); - return 0; - } - - /* Put in sound buffer. */ - if (frame_out.size > size) { - PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes", - frame_out.size - size)); - frame_out.size = size; - } - - pj_memcpy(frame, frame_out.buf, size); - - pj_fifobuf_free (&channel->fifobuf, jb_frame); - pj_mutex_unlock(channel->mutex); - return 0; -} - -static pj_status_t rec_callback( /* in */ void *user_data, - /* in */ pj_uint32_t timestamp, - /* in */ const void *frame, - /* in */ unsigned size) -{ - pj_media_stream_t *channel = user_data; - pj_status_t status = 0; - struct pj_audio_frame frame_in, frame_out; - int ts_len; - void *rtphdr; - int rtphdrlen; - int sent; -#if 0 - static FILE *fhnd = NULL; -#endif - - PJ_UNUSED_ARG(timestamp) - - /* Start locking channel mutex */ - pj_mutex_lock (channel->mutex); - - if (!channel->codec) { - status = -1; - goto on_return; - } - - /* Encode. */ - frame_in.type = PJ_MEDIA_TYPE_AUDIO; - frame_in.buf = (void*)frame; - frame_in.size = size; - frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr); - status = channel->codec->op->encode (channel->codec, &frame_in, - channel->out_pkt_size - sizeof(pj_rtp_hdr), - &frame_out); - if (status != 0) { - PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d", - status)); - goto on_return; - } - - /* Encapsulate. */ - ts_len = size / (channel->snd_info.bits_per_sample / 8); - status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0, - frame_out.size, ts_len, - (const void**)&rtphdr, &rtphdrlen); - if (status != 0) { - PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d", - status)); - goto on_return; - } - - if (rtphdrlen != sizeof(pj_rtp_hdr)) { - /* We don't support RTP with extended header yet. */ - PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER); - PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission")); - goto on_return; - } - - pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr)); - - /* Send. */ - sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0, - &channel->dst_addr, sizeof(channel->dst_addr)); - if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) { - pj_perror(THISFILE, "Error sending RTP packet to %s:%d", - pj_sockaddr_get_str_addr(&channel->dst_addr), - pj_sockaddr_get_port(&channel->dst_addr)); - goto on_return; - } - - /* Update stat */ - channel->stat.pkt_tx++; - channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr); - -#if 0 - if (fhnd == NULL) { - fhnd = fopen("RTP.DAT", "wb"); - if (fhnd) { - fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd); - fclose(fhnd); - } - } -#endif - -on_return: - pj_mutex_unlock (channel->mutex); - return status; -} - - -static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg) -{ - pj_media_stream_t *channel = arg; - - while (!channel->thread_quit_flag) { - int len, size; - const pj_rtp_hdr *hdr; - const void *payload; - unsigned payloadlen; - int status; - struct jb_frame *jb_frame; - - /* Wait for packet. */ - fd_set fds; - pj_time_val timeout; - - PJ_FD_ZERO (&fds); - PJ_FD_SET (channel->rtp_sock, &fds); - timeout.sec = 0; - timeout.msec = 100; - - /* Wait with timeout. */ - status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout); - if (status != 1) - continue; - - /* Get packet from socket. */ - len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0); - if (len < 1) { - if (pj_getlasterror() == PJ_ECONNRESET) { - /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET - when the sending side receives ICMP port unreachable. - */ - continue; - } - pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len); - pj_thread_sleep(1); - continue; - } - - if (channel->state != STREAM_STARTED) - continue; - - if (channel->thread_quit_flag) - break; - - /* Start locking the channel. */ - pj_mutex_lock (channel->mutex); - - /* Update RTP and RTCP session. */ - status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen); - if (status != 0) { - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status)); - continue; - } - status = pj_rtp_session_update (&channel->rtp, hdr); - if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) { - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status)); - continue; - } - pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts)); - - /* Update stat */ - channel->stat.pkt_rx++; - channel->stat.oct_rx += len; - - /* Copy to FIFO buffer. */ - size = payloadlen+sizeof(struct jb_frame); - jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size); - if (jb_frame == NULL) { - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size)); - continue; - } - - /* Copy the payload */ - jb_frame->size = payloadlen; - jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame); - pj_memcpy (jb_frame->buf, payload, payloadlen); - - /* Put to jitter buffer. */ - status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame); - if (status != 0) { - pj_fifobuf_unalloc (&channel->fifobuf, jb_frame); - pj_mutex_unlock (channel->mutex); - PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status)); - continue; - } - - pj_mutex_unlock (channel->mutex); - } - - return NULL; -} - -static void init_snd_param_from_codec_attr (pj_snd_stream_info *param, - const pj_codec_attr *attr) -{ - param->bits_per_sample = attr->pcm_bits_per_sample; - param->bytes_per_frame = 2; - param->frames_per_packet = attr->sample_rate * attr->ptime / 1000; - param->samples_per_frame = 1; - param->samples_per_sec = attr->sample_rate; -} - -static pj_media_stream_t *create_channel ( pj_pool_t *pool, - pj_media_dir_t dir, - pj_media_stream_t *peer, - pj_codec_id *codec_id, - pj_media_stream_create_param *param) -{ - pj_media_stream_t *channel; - pj_codec_attr codec_attr; - void *ptr; - unsigned size; - int status; - - /* Allocate memory for channel descriptor */ - size = sizeof(pj_media_stream_t); - channel = pj_pool_calloc(pool, 1, size); - if (!channel) { - PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor", - size)); - return NULL; - } - - channel->dir = dir; - channel->pt = codec_id->pt; - channel->peer = peer; - channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr); - channel->rtp_sock = param->rtp_sock; - channel->rtcp_sock = param->rtcp_sock; - channel->dst_addr = *param->remote_addr; - channel->state = STREAM_STOPPED; - - /* Create mutex for the channel. */ - channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE); - if (channel->mutex == NULL) - goto err_cleanup; - - /* Create and initialize codec, only if peer is not present. - We only use one codec instance for both encoder and decoder. - */ - if (peer && peer->codec) { - channel->codec = peer->codec; - status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, - &codec_attr); - if (status != 0) { - goto err_cleanup; - } - - } else { - channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id); - if (channel->codec == NULL) { - goto err_cleanup; - } - - status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, - &codec_attr); - if (status != 0) { - goto err_cleanup; - } - - codec_attr.pt = codec_id->pt; - status = channel->codec->op->open(channel->codec, &codec_attr); - if (status != 0) { - goto err_cleanup; - } - } - - /* Allocate buffer for incoming packet. */ - channel->in_pkt_size = PJ_MAX_MTU; - channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size); - if (!channel->in_pkt) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer", - channel->in_pkt_size)); - goto err_cleanup; - } - - /* Allocate buffer for outgoing packet. */ - channel->out_pkt_size = sizeof(pj_rtp_hdr) + - codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000; - if (channel->out_pkt_size > PJ_MAX_MTU) - channel->out_pkt_size = PJ_MAX_MTU; - channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size); - if (!channel->out_pkt) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer", - channel->out_pkt_size)); - goto err_cleanup; - } - - /* Allocate buffer for decoding to PCM */ - channel->pcm_buf_size = codec_attr.sample_rate * - codec_attr.pcm_bits_per_sample / 8 * - PJ_MAX_FRAME_DURATION_MS / 1000; - channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size); - if (!channel->pcm_buf) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer", - channel->pcm_buf_size)); - goto err_cleanup; - } - - /* Allocate buffer for frames put in jitter buffer. */ - size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000; - ptr = pj_pool_alloc(pool, size); - if (!ptr) { - PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer", - channel->pcm_buf_size)); - goto err_cleanup; - } - //pj_fifobuf_init (&channel->fifobuf, ptr, size); - - /* Create and initialize sound device */ - init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr); - - if (dir == PJ_MEDIA_DIR_ENCODING) - channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info, - &rec_callback, channel); - else - channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info, - &play_callback, channel); - - if (!channel->snd_stream) - goto err_cleanup; - - /* Create RTP and RTCP sessions. */ - if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) { - PJ_LOG(1, (THISFILE, "RTP session initialization error")); - goto err_cleanup; - } - - /* For decoder, create RTCP session, jitter buffer, and transport thread. */ - if (dir == PJ_MEDIA_DIR_DECODING) { - channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session)); - if (!channel->rtcp) { - PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session")); - goto err_cleanup; - } - - pj_rtcp_init(channel->rtcp, param->ssrc); - - channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer)); - if (!channel->jb) { - PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor")); - goto err_cleanup; - } - if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) { - PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer")); - goto err_cleanup; - } - - channel->transport_thread = pj_thread_create(pool, "decode", - &stream_decoder_transport_thread, channel, - 0, NULL, 0); - if (!channel->transport_thread) { - pj_perror(THISFILE, "Unable to create transport thread"); - goto err_cleanup; - } - } - - /* Done. */ - return channel; - -err_cleanup: - pj_media_stream_destroy(channel); - return NULL; -} - - -PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool, - pj_media_stream_t **enc_stream, - pj_media_stream_t **dec_stream, - pj_media_stream_create_param *param) -{ - *dec_stream = *enc_stream = NULL; - - if (param->dir & PJ_MEDIA_DIR_DECODING) { - *dec_stream = - create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param); - if (!*dec_stream) - return -1; - } - - if (param->dir & PJ_MEDIA_DIR_ENCODING) { - *enc_stream = - create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param); - if (!*enc_stream) { - if (*dec_stream) { - pj_media_stream_destroy(*dec_stream); - *dec_stream = NULL; - } - return -1; - } - - if (*dec_stream) { - (*dec_stream)->peer = *enc_stream; - } - } - - return 0; -} - -PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel) -{ - pj_status_t status; - - status = pj_snd_stream_start(channel->snd_stream); - - if (status == 0) - channel->state = STREAM_STARTED; - return status; -} - -PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, - pj_media_stream_stat *stat) -{ - if (stream->dir == PJ_MEDIA_DIR_ENCODING) { - pj_memcpy (stat, &stream->stat, sizeof(*stat)); - } else { - pj_rtcp_pkt *rtcp_pkt; - int len; - - pj_memset (stat, 0, sizeof(*stat)); - pj_assert (stream->rtcp != 0); - pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len); - - stat->pkt_rx = stream->stat.pkt_rx; - stat->oct_rx = stream->stat.oct_rx; - - PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE) - stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8; - stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) + - (rtcp_pkt->rr.total_lost_1 << 8) + - rtcp_pkt->rr.total_lost_0; - } - return 0; -} - -PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel) -{ - PJ_UNUSED_ARG(channel) - return -1; -} - -PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel) -{ - PJ_UNUSED_ARG(channel) - return -1; -} - -PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel) -{ - channel->thread_quit_flag = 1; - - pj_mutex_lock (channel->mutex); - if (channel->peer) - pj_mutex_lock (channel->peer->mutex); - - if (channel->jb) { - /* No need to deinitialize jitter buffer. */ - } - if (channel->transport_thread) { - pj_thread_join(channel->transport_thread); - pj_thread_destroy(channel->transport_thread); - channel->transport_thread = NULL; - } - if (channel->snd_stream != NULL) { - pj_mutex_unlock (channel->mutex); - pj_snd_stream_stop(channel->snd_stream); - pj_mutex_lock (channel->mutex); - pj_snd_stream_close(channel->snd_stream); - channel->snd_stream = NULL; - } - if (channel->codec) { - channel->codec->op->close(channel->codec); - pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec); - channel->codec = NULL; - } - if (channel->peer) { - pj_media_stream_t *peer = channel->peer; - peer->peer = NULL; - peer->codec = NULL; - peer->thread_quit_flag = 1; - if (peer->transport_thread) { - pj_mutex_unlock (peer->mutex); - pj_thread_join(peer->transport_thread); - pj_mutex_lock (peer->mutex); - pj_thread_destroy(peer->transport_thread); - peer->transport_thread = NULL; - } - if (peer->snd_stream) { - pj_mutex_unlock (peer->mutex); - pj_snd_stream_stop(peer->snd_stream); - pj_mutex_lock (peer->mutex); - pj_snd_stream_close(peer->snd_stream); - peer->snd_stream = NULL; - } - } - - channel->state = STREAM_STOPPED; - - if (channel->peer) - pj_mutex_unlock (channel->peer->mutex); - pj_mutex_unlock(channel->mutex); - pj_mutex_destroy(channel->mutex); - - return 0; -} - +/* $Header: /pjproject/pjmedia/src/pjmedia/stream.c 14 6/24/05 11:14p Bennylp $ */ + +#include +#include +#include +#include +#include +#include +#include /* memcpy() */ +#include +#include + +#define THISFILE "stream.c" +#define ERRLEVEL 1 + +#define PJ_MAX_FRAME_DURATION_MS 200 +#define PJ_MAX_BUFFER_SIZE_MS 2000 +#define PJ_MAX_MTU 1500 + +struct jb_frame +{ + unsigned size; + void *buf; +}; + +#define pj_fifobuf_alloc(fifo,size) malloc(size) +#define pj_fifobuf_unalloc(fifo,buf) free(buf) +#define pj_fifobuf_free(fifo, buf) free(buf) + +enum stream_state +{ + STREAM_STOPPED, + STREAM_STARTED, +}; + +struct pj_media_stream_t +{ + pj_media_dir_t dir; + int pt; + int state; + pj_media_stream_stat stat; + pj_media_stream_t *peer; + pj_snd_stream_info snd_info; + pj_snd_stream *snd_stream; + pj_mutex_t *mutex; + unsigned in_pkt_size; + void *in_pkt; + unsigned out_pkt_size; + void *out_pkt; + unsigned pcm_buf_size; + void *pcm_buf; + //pj_fifobuf_t fifobuf; + pj_codec_mgr *codec_mgr; + pj_codec *codec; + pj_rtp_session rtp; + pj_rtcp_session *rtcp; + pj_jitter_buffer *jb; + pj_sock_t rtp_sock; + pj_sock_t rtcp_sock; + pj_sockaddr_in dst_addr; + pj_thread_t *transport_thread; + int thread_quit_flag; +}; + + +static pj_status_t play_callback(/* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* out */ void *frame, + /*inout*/ unsigned size) +{ + pj_media_stream_t *channel = user_data; + struct jb_frame *jb_frame; + void *p; + pj_uint32_t extseq; + pj_status_t status; + struct pj_audio_frame frame_in, frame_out; + + PJ_UNUSED_ARG(timestamp) + + /* Lock mutex */ + pj_mutex_lock (channel->mutex); + + if (!channel->codec) { + pj_mutex_unlock (channel->mutex); + return -1; + } + + /* Get frame from jitter buffer. */ + status = pj_jb_get (channel->jb, &extseq, &p); + jb_frame = p; + if (status != 0 || jb_frame == NULL) { + pj_memset(frame, 0, size); + pj_mutex_unlock(channel->mutex); + return 0; + } + + /* Decode */ + frame_in.buf = jb_frame->buf; + frame_in.size = jb_frame->size; + frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */ + frame_out.buf = channel->pcm_buf; + status = channel->codec->op->decode (channel->codec, &frame_in, + channel->pcm_buf_size, &frame_out); + if (status != 0) { + PJ_LOG(3, (THISFILE, "decode() has return error status %d", + status)); + + pj_memset(frame, 0, size); + pj_fifobuf_free (&channel->fifobuf, jb_frame); + pj_mutex_unlock(channel->mutex); + return 0; + } + + /* Put in sound buffer. */ + if (frame_out.size > size) { + PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes", + frame_out.size - size)); + frame_out.size = size; + } + + pj_memcpy(frame, frame_out.buf, size); + + pj_fifobuf_free (&channel->fifobuf, jb_frame); + pj_mutex_unlock(channel->mutex); + return 0; +} + +static pj_status_t rec_callback( /* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* in */ const void *frame, + /* in */ unsigned size) +{ + pj_media_stream_t *channel = user_data; + pj_status_t status = 0; + struct pj_audio_frame frame_in, frame_out; + int ts_len; + void *rtphdr; + int rtphdrlen; + int sent; +#if 0 + static FILE *fhnd = NULL; +#endif + + PJ_UNUSED_ARG(timestamp) + + /* Start locking channel mutex */ + pj_mutex_lock (channel->mutex); + + if (!channel->codec) { + status = -1; + goto on_return; + } + + /* Encode. */ + frame_in.type = PJ_MEDIA_TYPE_AUDIO; + frame_in.buf = (void*)frame; + frame_in.size = size; + frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr); + status = channel->codec->op->encode (channel->codec, &frame_in, + channel->out_pkt_size - sizeof(pj_rtp_hdr), + &frame_out); + if (status != 0) { + PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d", + status)); + goto on_return; + } + + /* Encapsulate. */ + ts_len = size / (channel->snd_info.bits_per_sample / 8); + status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0, + frame_out.size, ts_len, + (const void**)&rtphdr, &rtphdrlen); + if (status != 0) { + PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d", + status)); + goto on_return; + } + + if (rtphdrlen != sizeof(pj_rtp_hdr)) { + /* We don't support RTP with extended header yet. */ + PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER); + PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission")); + goto on_return; + } + + pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr)); + + /* Send. */ + sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0, + &channel->dst_addr, sizeof(channel->dst_addr)); + if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) { + pj_perror(THISFILE, "Error sending RTP packet to %s:%d", + pj_sockaddr_get_str_addr(&channel->dst_addr), + pj_sockaddr_get_port(&channel->dst_addr)); + goto on_return; + } + + /* Update stat */ + channel->stat.pkt_tx++; + channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr); + +#if 0 + if (fhnd == NULL) { + fhnd = fopen("RTP.DAT", "wb"); + if (fhnd) { + fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd); + fclose(fhnd); + } + } +#endif + +on_return: + pj_mutex_unlock (channel->mutex); + return status; +} + + +static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg) +{ + pj_media_stream_t *channel = arg; + + while (!channel->thread_quit_flag) { + int len, size; + const pj_rtp_hdr *hdr; + const void *payload; + unsigned payloadlen; + int status; + struct jb_frame *jb_frame; + + /* Wait for packet. */ + fd_set fds; + pj_time_val timeout; + + PJ_FD_ZERO (&fds); + PJ_FD_SET (channel->rtp_sock, &fds); + timeout.sec = 0; + timeout.msec = 100; + + /* Wait with timeout. */ + status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout); + if (status != 1) + continue; + + /* Get packet from socket. */ + len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0); + if (len < 1) { + if (pj_getlasterror() == PJ_ECONNRESET) { + /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET + when the sending side receives ICMP port unreachable. + */ + continue; + } + pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len); + pj_thread_sleep(1); + continue; + } + + if (channel->state != STREAM_STARTED) + continue; + + if (channel->thread_quit_flag) + break; + + /* Start locking the channel. */ + pj_mutex_lock (channel->mutex); + + /* Update RTP and RTCP session. */ + status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen); + if (status != 0) { + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status)); + continue; + } + status = pj_rtp_session_update (&channel->rtp, hdr); + if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) { + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status)); + continue; + } + pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts)); + + /* Update stat */ + channel->stat.pkt_rx++; + channel->stat.oct_rx += len; + + /* Copy to FIFO buffer. */ + size = payloadlen+sizeof(struct jb_frame); + jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size); + if (jb_frame == NULL) { + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size)); + continue; + } + + /* Copy the payload */ + jb_frame->size = payloadlen; + jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame); + pj_memcpy (jb_frame->buf, payload, payloadlen); + + /* Put to jitter buffer. */ + status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame); + if (status != 0) { + pj_fifobuf_unalloc (&channel->fifobuf, jb_frame); + pj_mutex_unlock (channel->mutex); + PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status)); + continue; + } + + pj_mutex_unlock (channel->mutex); + } + + return NULL; +} + +static void init_snd_param_from_codec_attr (pj_snd_stream_info *param, + const pj_codec_attr *attr) +{ + param->bits_per_sample = attr->pcm_bits_per_sample; + param->bytes_per_frame = 2; + param->frames_per_packet = attr->sample_rate * attr->ptime / 1000; + param->samples_per_frame = 1; + param->samples_per_sec = attr->sample_rate; +} + +static pj_media_stream_t *create_channel ( pj_pool_t *pool, + pj_media_dir_t dir, + pj_media_stream_t *peer, + pj_codec_id *codec_id, + pj_media_stream_create_param *param) +{ + pj_media_stream_t *channel; + pj_codec_attr codec_attr; + void *ptr; + unsigned size; + int status; + + /* Allocate memory for channel descriptor */ + size = sizeof(pj_media_stream_t); + channel = pj_pool_calloc(pool, 1, size); + if (!channel) { + PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor", + size)); + return NULL; + } + + channel->dir = dir; + channel->pt = codec_id->pt; + channel->peer = peer; + channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr); + channel->rtp_sock = param->rtp_sock; + channel->rtcp_sock = param->rtcp_sock; + channel->dst_addr = *param->remote_addr; + channel->state = STREAM_STOPPED; + + /* Create mutex for the channel. */ + channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE); + if (channel->mutex == NULL) + goto err_cleanup; + + /* Create and initialize codec, only if peer is not present. + We only use one codec instance for both encoder and decoder. + */ + if (peer && peer->codec) { + channel->codec = peer->codec; + status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, + &codec_attr); + if (status != 0) { + goto err_cleanup; + } + + } else { + channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id); + if (channel->codec == NULL) { + goto err_cleanup; + } + + status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id, + &codec_attr); + if (status != 0) { + goto err_cleanup; + } + + codec_attr.pt = codec_id->pt; + status = channel->codec->op->open(channel->codec, &codec_attr); + if (status != 0) { + goto err_cleanup; + } + } + + /* Allocate buffer for incoming packet. */ + channel->in_pkt_size = PJ_MAX_MTU; + channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size); + if (!channel->in_pkt) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer", + channel->in_pkt_size)); + goto err_cleanup; + } + + /* Allocate buffer for outgoing packet. */ + channel->out_pkt_size = sizeof(pj_rtp_hdr) + + codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000; + if (channel->out_pkt_size > PJ_MAX_MTU) + channel->out_pkt_size = PJ_MAX_MTU; + channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size); + if (!channel->out_pkt) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer", + channel->out_pkt_size)); + goto err_cleanup; + } + + /* Allocate buffer for decoding to PCM */ + channel->pcm_buf_size = codec_attr.sample_rate * + codec_attr.pcm_bits_per_sample / 8 * + PJ_MAX_FRAME_DURATION_MS / 1000; + channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size); + if (!channel->pcm_buf) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer", + channel->pcm_buf_size)); + goto err_cleanup; + } + + /* Allocate buffer for frames put in jitter buffer. */ + size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000; + ptr = pj_pool_alloc(pool, size); + if (!ptr) { + PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer", + channel->pcm_buf_size)); + goto err_cleanup; + } + //pj_fifobuf_init (&channel->fifobuf, ptr, size); + + /* Create and initialize sound device */ + init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr); + + if (dir == PJ_MEDIA_DIR_ENCODING) + channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info, + &rec_callback, channel); + else + channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info, + &play_callback, channel); + + if (!channel->snd_stream) + goto err_cleanup; + + /* Create RTP and RTCP sessions. */ + if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) { + PJ_LOG(1, (THISFILE, "RTP session initialization error")); + goto err_cleanup; + } + + /* For decoder, create RTCP session, jitter buffer, and transport thread. */ + if (dir == PJ_MEDIA_DIR_DECODING) { + channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session)); + if (!channel->rtcp) { + PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session")); + goto err_cleanup; + } + + pj_rtcp_init(channel->rtcp, param->ssrc); + + channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer)); + if (!channel->jb) { + PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor")); + goto err_cleanup; + } + if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) { + PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer")); + goto err_cleanup; + } + + channel->transport_thread = pj_thread_create(pool, "decode", + &stream_decoder_transport_thread, channel, + 0, NULL, 0); + if (!channel->transport_thread) { + pj_perror(THISFILE, "Unable to create transport thread"); + goto err_cleanup; + } + } + + /* Done. */ + return channel; + +err_cleanup: + pj_media_stream_destroy(channel); + return NULL; +} + + +PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool, + pj_media_stream_t **enc_stream, + pj_media_stream_t **dec_stream, + pj_media_stream_create_param *param) +{ + *dec_stream = *enc_stream = NULL; + + if (param->dir & PJ_MEDIA_DIR_DECODING) { + *dec_stream = + create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param); + if (!*dec_stream) + return -1; + } + + if (param->dir & PJ_MEDIA_DIR_ENCODING) { + *enc_stream = + create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param); + if (!*enc_stream) { + if (*dec_stream) { + pj_media_stream_destroy(*dec_stream); + *dec_stream = NULL; + } + return -1; + } + + if (*dec_stream) { + (*dec_stream)->peer = *enc_stream; + } + } + + return 0; +} + +PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel) +{ + pj_status_t status; + + status = pj_snd_stream_start(channel->snd_stream); + + if (status == 0) + channel->state = STREAM_STARTED; + return status; +} + +PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, + pj_media_stream_stat *stat) +{ + if (stream->dir == PJ_MEDIA_DIR_ENCODING) { + pj_memcpy (stat, &stream->stat, sizeof(*stat)); + } else { + pj_rtcp_pkt *rtcp_pkt; + int len; + + pj_memset (stat, 0, sizeof(*stat)); + pj_assert (stream->rtcp != 0); + pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len); + + stat->pkt_rx = stream->stat.pkt_rx; + stat->oct_rx = stream->stat.oct_rx; + + PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE) + stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8; + stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) + + (rtcp_pkt->rr.total_lost_1 << 8) + + rtcp_pkt->rr.total_lost_0; + } + return 0; +} + +PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel) +{ + PJ_UNUSED_ARG(channel) + return -1; +} + +PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel) +{ + PJ_UNUSED_ARG(channel) + return -1; +} + +PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel) +{ + channel->thread_quit_flag = 1; + + pj_mutex_lock (channel->mutex); + if (channel->peer) + pj_mutex_lock (channel->peer->mutex); + + if (channel->jb) { + /* No need to deinitialize jitter buffer. */ + } + if (channel->transport_thread) { + pj_thread_join(channel->transport_thread); + pj_thread_destroy(channel->transport_thread); + channel->transport_thread = NULL; + } + if (channel->snd_stream != NULL) { + pj_mutex_unlock (channel->mutex); + pj_snd_stream_stop(channel->snd_stream); + pj_mutex_lock (channel->mutex); + pj_snd_stream_close(channel->snd_stream); + channel->snd_stream = NULL; + } + if (channel->codec) { + channel->codec->op->close(channel->codec); + pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec); + channel->codec = NULL; + } + if (channel->peer) { + pj_media_stream_t *peer = channel->peer; + peer->peer = NULL; + peer->codec = NULL; + peer->thread_quit_flag = 1; + if (peer->transport_thread) { + pj_mutex_unlock (peer->mutex); + pj_thread_join(peer->transport_thread); + pj_mutex_lock (peer->mutex); + pj_thread_destroy(peer->transport_thread); + peer->transport_thread = NULL; + } + if (peer->snd_stream) { + pj_mutex_unlock (peer->mutex); + pj_snd_stream_stop(peer->snd_stream); + pj_mutex_lock (peer->mutex); + pj_snd_stream_close(peer->snd_stream); + peer->snd_stream = NULL; + } + } + + channel->state = STREAM_STOPPED; + + if (channel->peer) + pj_mutex_unlock (channel->peer->mutex); + pj_mutex_unlock(channel->mutex); + pj_mutex_destroy(channel->mutex); + + return 0; +} + diff --git a/pjmedia/src/pjmedia/stream.h b/pjmedia/src/pjmedia/stream.h index 9bb03890f..4ee5d58a6 100644 --- a/pjmedia/src/pjmedia/stream.h +++ b/pjmedia/src/pjmedia/stream.h @@ -1,83 +1,83 @@ -/* $Header: /pjproject/pjmedia/src/pjmedia/stream.h 6 8/24/05 10:30a Bennylp $ */ - -#ifndef __PJMEDIA_STREAM_H__ -#define __PJMEDIA_STREAM_H__ - - -/** - * @file stream.h - * @brief Stream of media. - */ - -#include -#include -#include -#include - -PJ_BEGIN_DECL - - -/** - * @defgroup PJMED_SES Media session - * @ingroup PJMEDIA - * @{ - */ - -typedef struct pj_media_stream_t pj_media_stream_t; - -/** Parameter for creating channel. */ -typedef struct pj_media_stream_create_param -{ - /** Codec ID, must NOT be NULL. */ - pj_codec_id *codec_id; - - /** Media manager, must NOT be NULL. */ - pj_med_mgr_t *mediamgr; - - /** Direction: IN_OUT, or IN only, or OUT only. */ - pj_media_dir_t dir; - - /** RTP socket. */ - pj_sock_t rtp_sock; - - /** RTCP socket. */ - pj_sock_t rtcp_sock; - - /** Address of remote */ - pj_sockaddr_in *remote_addr; - - /** RTP SSRC */ - pj_uint32_t ssrc; - - /** Jitter buffer parameters. */ - int jb_min, jb_max, jb_maxcnt; - -} pj_media_stream_create_param; - -typedef struct pj_media_stream_stat -{ - pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */ - pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */ - pj_uint32_t jitter; /* receive jitter in ms */ - pj_uint32_t pkt_lost; /* total packet lost count */ -} pj_media_stream_stat; - -PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool, - pj_media_stream_t **enc_stream, - pj_media_stream_t **dec_stream, - pj_media_stream_create_param *param); -PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream); -PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, - pj_media_stream_stat *stat); -PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream); -PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream); -PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream); - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJMEDIA_STREAM_H__ */ +/* $Header: /pjproject/pjmedia/src/pjmedia/stream.h 6 8/24/05 10:30a Bennylp $ */ + +#ifndef __PJMEDIA_STREAM_H__ +#define __PJMEDIA_STREAM_H__ + + +/** + * @file stream.h + * @brief Stream of media. + */ + +#include +#include +#include +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJMED_SES Media session + * @ingroup PJMEDIA + * @{ + */ + +typedef struct pj_media_stream_t pj_media_stream_t; + +/** Parameter for creating channel. */ +typedef struct pj_media_stream_create_param +{ + /** Codec ID, must NOT be NULL. */ + pj_codec_id *codec_id; + + /** Media manager, must NOT be NULL. */ + pj_med_mgr_t *mediamgr; + + /** Direction: IN_OUT, or IN only, or OUT only. */ + pj_media_dir_t dir; + + /** RTP socket. */ + pj_sock_t rtp_sock; + + /** RTCP socket. */ + pj_sock_t rtcp_sock; + + /** Address of remote */ + pj_sockaddr_in *remote_addr; + + /** RTP SSRC */ + pj_uint32_t ssrc; + + /** Jitter buffer parameters. */ + int jb_min, jb_max, jb_maxcnt; + +} pj_media_stream_create_param; + +typedef struct pj_media_stream_stat +{ + pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */ + pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */ + pj_uint32_t jitter; /* receive jitter in ms */ + pj_uint32_t pkt_lost; /* total packet lost count */ +} pj_media_stream_stat; + +PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool, + pj_media_stream_t **enc_stream, + pj_media_stream_t **dec_stream, + pj_media_stream_create_param *param); +PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream); +PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream, + pj_media_stream_stat *stat); +PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream); +PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream); +PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream); + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJMEDIA_STREAM_H__ */ diff --git a/pjmedia/src/test/audio_tool.c b/pjmedia/src/test/audio_tool.c index 31e1e9fc3..a4080ca85 100644 --- a/pjmedia/src/test/audio_tool.c +++ b/pjmedia/src/test/audio_tool.c @@ -1,392 +1,392 @@ -/* $Header: /pjproject/pjmedia/src/test/audio_tool.c 11 6/24/05 11:17p Bennylp $ */ -#include -#include -#include - -#define THIS_FILE "audio_tool.c" - -static pj_caching_pool caching_pool; -static pj_pool_factory *pf; -static FILE *fhnd; -static pj_med_mgr_t *mm; -static pj_codec *codec; -static pj_codec_attr cattr; - - -#define WRITE_ORIGINAL_PCM 0 -#if WRITE_ORIGINAL_PCM -static FILE *fhnd_pcm; -#endif - -static char talker_sdp[] = - "v=0\r\n" - "o=- 0 0 IN IP4 127.0.0.1\r\n" - "s=-\r\n" - "c=IN IP4 127.0.0.1\r\n" - "t=0 0\r\n" - "m=audio 4002 RTP/AVP 0\r\n" - "a=rtpmap:0 PCMU/8000\r\n" - "a=sendonly\r\n"; -static char listener_sdp[] = - "v=0\r\n" - "o=- 0 0 IN IP4 127.0.0.1\r\n" - "s=-\r\n" - "c=IN IP4 127.0.0.1\r\n" - "t=0 0\r\n" - "m=audio 4000 RTP/AVP 0\r\n" - "a=rtpmap:0 PCMU/8000\r\n" - "a=recvonly\r\n"; - -static pj_status_t play_callback(/* in */ void *user_data, - /* in */ pj_uint32_t timestamp, - /* out */ void *frame, - /* out */ unsigned size) -{ - char pkt[160]; - struct pj_audio_frame in, out; - int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000; - - if (fread(pkt, frmsz, 1, fhnd) != 1) { - puts("EOF"); - return -1; - } else { - in.type = PJ_AUDIO_FRAME_AUDIO; - in.buf = pkt; - in.size = frmsz; - out.buf = frame; - if (codec->op->decode (codec, &in, size, &out) != 0) - return -1; - - size = out.size; - return 0; - } -} - -static pj_status_t rec_callback( /* in */ void *user_data, - /* in */ pj_uint32_t timestamp, - /* in */ const void *frame, - /* in*/ unsigned size) -{ - char pkt[160]; - struct pj_audio_frame in, out; - //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000; - -#if WRITE_ORIGINAL_PCM - fwrite(frame, size, 1, fhnd_pcm); -#endif - - in.type = PJ_AUDIO_FRAME_AUDIO; - in.buf = (void*)frame; - in.size = size; - out.buf = pkt; - - if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0) - return -1; - - if (fwrite(pkt, out.size, 1, fhnd) != 1) - return -1; - return 0; -} - -static pj_status_t init() -{ - pj_codec_mgr *cm; - pj_codec_id id; - int i; - - pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); - pf = &caching_pool.factory; - - if (pj_snd_init(&caching_pool.factory)) - return -1; - - PJ_LOG(3,(THIS_FILE, "Dumping audio devices:")); - for (i=0; iname, - info->input_count, info->output_count)); - } - - mm = pj_med_mgr_create (&caching_pool.factory); - cm = pj_med_mgr_get_codec_mgr (mm); - - id.type = PJ_MEDIA_TYPE_AUDIO; - id.pt = 0; - id.encoding_name = pj_str("PCMU"); - id.sample_rate = 8000; - - codec = pj_codec_mgr_alloc_codec (cm, &id); - codec->op->default_attr(codec, &cattr); - codec->op->open(codec, &cattr); - return 0; -} - -static pj_status_t deinit() -{ - pj_codec_mgr *cm; - cm = pj_med_mgr_get_codec_mgr (mm); - codec->op->close(codec); - pj_codec_mgr_dealloc_codec (cm, codec); - pj_med_mgr_destroy (mm); - pj_caching_pool_destroy(&caching_pool); - return 0; -} - -static pj_status_t record_file (const char *filename) -{ - pj_snd_stream *stream; - pj_snd_stream_info info; - int status; - char s[10]; - - printf("Recording to file %s...\n", filename); - - fhnd = fopen(filename, "wb"); - if (!fhnd) - return -1; - -#if WRITE_ORIGINAL_PCM - fhnd_pcm = fopen("ORIGINAL.PCM", "wb"); - if (!fhnd_pcm) - return -1; -#endif - - pj_memset(&info, 0, sizeof(info)); - info.bits_per_sample = 16; - info.bytes_per_frame = 2; - info.frames_per_packet = 160; - info.samples_per_frame = 1; - info.samples_per_sec = 8000; - - stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL); - if (!stream) - return -1; - - status = pj_snd_stream_start(stream); - if (status != 0) - goto on_error; - - puts("Press to exit recording"); - fgets(s, sizeof(s), stdin); - - pj_snd_stream_stop(stream); - pj_snd_stream_close(stream); - -#if WRITE_ORIGINAL_PCM - fclose(fhnd_pcm); -#endif - fclose(fhnd); - return 0; - -on_error: - pj_snd_stream_stop(stream); - pj_snd_stream_close(stream); - return -1; -} - - -static pj_status_t play_file (const char *filename) -{ - pj_snd_stream *stream; - pj_snd_stream_info info; - int status; - char s[10]; - - printf("Playing file %s...\n", filename); - - fhnd = fopen(filename, "rb"); - if (!fhnd) - return -1; - - pj_memset(&info, 0, sizeof(info)); - info.bits_per_sample = 16; - info.bytes_per_frame = 2; - info.frames_per_packet = 160; - info.samples_per_frame = 1; - info.samples_per_sec = 8000; - - stream = pj_snd_open_player(-1, &info, &play_callback, NULL); - if (!stream) - return -1; - - status = pj_snd_stream_start(stream); - if (status != 0) - goto on_error; - - puts("Press to exit playing"); - fgets(s, sizeof(s), stdin); - - pj_snd_stream_stop(stream); - pj_snd_stream_close(stream); - - fclose(fhnd); - return 0; - -on_error: - pj_snd_stream_stop(stream); - pj_snd_stream_close(stream); - return -1; -} - -static int create_ses_by_remote_sdp(int local_port, char *sdp) -{ - pj_media_session_t *ses = NULL; - pjsdp_session_desc *sdp_ses; - pj_media_sock_info skinfo; - pj_pool_t *pool; - char s[4]; - const pj_media_stream_info *info[2]; - int i, count; - - pool = pj_pool_create(pf, "sdp", 1024, 0, NULL); - if (!pool) { - PJ_LOG(1,(THIS_FILE, "Unable to create pool")); - return -1; - } - - pj_memset(&skinfo, 0, sizeof(skinfo)); - skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); - if (skinfo.rtp_sock == PJ_INVALID_SOCKET) { - PJ_LOG(1,(THIS_FILE, "Unable to create socket")); - goto on_error; - } - - pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port); - if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) { - PJ_LOG(1,(THIS_FILE, "Unable to bind socket")); - goto on_error; - } - - sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool); - if (!sdp_ses) { - PJ_LOG(1,(THIS_FILE, "Error parsing SDP")); - goto on_error; - } - - ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo); - if (!ses) { - PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP")); - goto on_error; - } - - if (pj_media_session_activate(ses) != 0) { - PJ_LOG(1,(THIS_FILE, "Error activating session")); - goto on_error; - } - - count = pj_media_session_enum_streams(ses, 2, info); - printf("\nDumping streams: \n"); - for (i=0; idir) { - case PJ_MEDIA_DIR_NONE: - dir = "- NONE -"; break; - case PJ_MEDIA_DIR_ENCODING: - dir = "SENDONLY"; break; - case PJ_MEDIA_DIR_DECODING: - dir = "RECVONLY"; break; - case PJ_MEDIA_DIR_ENCODING_DECODING: - dir = "SENDRECV"; break; - default: - dir = "?UNKNOWN"; break; - } - - local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name); - - printf(" Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n", - i, info[i]->type.slen, info[i]->type.ptr, - dir, - local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name), - info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port); - } - - puts("Press to quit"); - fgets(s, sizeof(s), stdin); - - pj_media_session_destroy(ses); - pj_sock_close(skinfo.rtp_sock); - pj_pool_release(pool); - - return 0; - -on_error: - if (ses) - pj_media_session_destroy(ses); - if (skinfo.rtp_sock != PJ_INVALID_SOCKET) - pj_sock_close(skinfo.rtp_sock); - if (pool) - pj_pool_release(pool); - return -1; -} - -#if WRITE_ORIGINAL_PCM -static pj_status_t convert(const char *src, const char *dst) -{ - char pcm[320]; - char frame[160]; - struct pj_audio_frame in, out; - - fhnd_pcm = fopen(src, "rb"); - if (!fhnd_pcm) - return -1; - fhnd = fopen(dst, "wb"); - if (!fhnd) - return -1; - - while (fread(pcm, 320, 1, fhnd_pcm) == 1) { - - in.type = PJ_AUDIO_FRAME_AUDIO; - in.buf = pcm; - in.size = 320; - out.buf = frame; - - if (codec->op->encode(codec, &in, 160, &out) != 0) - break; - - if (fwrite(frame, out.size, 1, fhnd) != 1) - break; - - } - - fclose(fhnd); - fclose(fhnd_pcm); - return 0; -} -#endif - -static void usage(const char *exe) -{ - printf("Usage: %s \n", exe); - puts("where:"); - puts(" play|record|send|recv"); -} - -int main(int argc, char *argv[]) -{ - if (argc < 2) { - usage(argv[0]); - return 1; - } - - pj_init(); - - init(); - - if (stricmp(argv[1], "record")==0) { - record_file("FILE.PCM"); - } else if (stricmp(argv[1], "play")==0) { - play_file("FILE.PCM"); - } else if (stricmp(argv[1], "send")==0) { - create_ses_by_remote_sdp(4002, listener_sdp); - } else if (stricmp(argv[1], "recv")==0) { - create_ses_by_remote_sdp(4000, talker_sdp); - } else { - usage(argv[0]); - } - deinit(); - return 0; -} +/* $Header: /pjproject/pjmedia/src/test/audio_tool.c 11 6/24/05 11:17p Bennylp $ */ +#include +#include +#include + +#define THIS_FILE "audio_tool.c" + +static pj_caching_pool caching_pool; +static pj_pool_factory *pf; +static FILE *fhnd; +static pj_med_mgr_t *mm; +static pj_codec *codec; +static pj_codec_attr cattr; + + +#define WRITE_ORIGINAL_PCM 0 +#if WRITE_ORIGINAL_PCM +static FILE *fhnd_pcm; +#endif + +static char talker_sdp[] = + "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "c=IN IP4 127.0.0.1\r\n" + "t=0 0\r\n" + "m=audio 4002 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=sendonly\r\n"; +static char listener_sdp[] = + "v=0\r\n" + "o=- 0 0 IN IP4 127.0.0.1\r\n" + "s=-\r\n" + "c=IN IP4 127.0.0.1\r\n" + "t=0 0\r\n" + "m=audio 4000 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=recvonly\r\n"; + +static pj_status_t play_callback(/* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* out */ void *frame, + /* out */ unsigned size) +{ + char pkt[160]; + struct pj_audio_frame in, out; + int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000; + + if (fread(pkt, frmsz, 1, fhnd) != 1) { + puts("EOF"); + return -1; + } else { + in.type = PJ_AUDIO_FRAME_AUDIO; + in.buf = pkt; + in.size = frmsz; + out.buf = frame; + if (codec->op->decode (codec, &in, size, &out) != 0) + return -1; + + size = out.size; + return 0; + } +} + +static pj_status_t rec_callback( /* in */ void *user_data, + /* in */ pj_uint32_t timestamp, + /* in */ const void *frame, + /* in*/ unsigned size) +{ + char pkt[160]; + struct pj_audio_frame in, out; + //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000; + +#if WRITE_ORIGINAL_PCM + fwrite(frame, size, 1, fhnd_pcm); +#endif + + in.type = PJ_AUDIO_FRAME_AUDIO; + in.buf = (void*)frame; + in.size = size; + out.buf = pkt; + + if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0) + return -1; + + if (fwrite(pkt, out.size, 1, fhnd) != 1) + return -1; + return 0; +} + +static pj_status_t init() +{ + pj_codec_mgr *cm; + pj_codec_id id; + int i; + + pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); + pf = &caching_pool.factory; + + if (pj_snd_init(&caching_pool.factory)) + return -1; + + PJ_LOG(3,(THIS_FILE, "Dumping audio devices:")); + for (i=0; iname, + info->input_count, info->output_count)); + } + + mm = pj_med_mgr_create (&caching_pool.factory); + cm = pj_med_mgr_get_codec_mgr (mm); + + id.type = PJ_MEDIA_TYPE_AUDIO; + id.pt = 0; + id.encoding_name = pj_str("PCMU"); + id.sample_rate = 8000; + + codec = pj_codec_mgr_alloc_codec (cm, &id); + codec->op->default_attr(codec, &cattr); + codec->op->open(codec, &cattr); + return 0; +} + +static pj_status_t deinit() +{ + pj_codec_mgr *cm; + cm = pj_med_mgr_get_codec_mgr (mm); + codec->op->close(codec); + pj_codec_mgr_dealloc_codec (cm, codec); + pj_med_mgr_destroy (mm); + pj_caching_pool_destroy(&caching_pool); + return 0; +} + +static pj_status_t record_file (const char *filename) +{ + pj_snd_stream *stream; + pj_snd_stream_info info; + int status; + char s[10]; + + printf("Recording to file %s...\n", filename); + + fhnd = fopen(filename, "wb"); + if (!fhnd) + return -1; + +#if WRITE_ORIGINAL_PCM + fhnd_pcm = fopen("ORIGINAL.PCM", "wb"); + if (!fhnd_pcm) + return -1; +#endif + + pj_memset(&info, 0, sizeof(info)); + info.bits_per_sample = 16; + info.bytes_per_frame = 2; + info.frames_per_packet = 160; + info.samples_per_frame = 1; + info.samples_per_sec = 8000; + + stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL); + if (!stream) + return -1; + + status = pj_snd_stream_start(stream); + if (status != 0) + goto on_error; + + puts("Press to exit recording"); + fgets(s, sizeof(s), stdin); + + pj_snd_stream_stop(stream); + pj_snd_stream_close(stream); + +#if WRITE_ORIGINAL_PCM + fclose(fhnd_pcm); +#endif + fclose(fhnd); + return 0; + +on_error: + pj_snd_stream_stop(stream); + pj_snd_stream_close(stream); + return -1; +} + + +static pj_status_t play_file (const char *filename) +{ + pj_snd_stream *stream; + pj_snd_stream_info info; + int status; + char s[10]; + + printf("Playing file %s...\n", filename); + + fhnd = fopen(filename, "rb"); + if (!fhnd) + return -1; + + pj_memset(&info, 0, sizeof(info)); + info.bits_per_sample = 16; + info.bytes_per_frame = 2; + info.frames_per_packet = 160; + info.samples_per_frame = 1; + info.samples_per_sec = 8000; + + stream = pj_snd_open_player(-1, &info, &play_callback, NULL); + if (!stream) + return -1; + + status = pj_snd_stream_start(stream); + if (status != 0) + goto on_error; + + puts("Press to exit playing"); + fgets(s, sizeof(s), stdin); + + pj_snd_stream_stop(stream); + pj_snd_stream_close(stream); + + fclose(fhnd); + return 0; + +on_error: + pj_snd_stream_stop(stream); + pj_snd_stream_close(stream); + return -1; +} + +static int create_ses_by_remote_sdp(int local_port, char *sdp) +{ + pj_media_session_t *ses = NULL; + pjsdp_session_desc *sdp_ses; + pj_media_sock_info skinfo; + pj_pool_t *pool; + char s[4]; + const pj_media_stream_info *info[2]; + int i, count; + + pool = pj_pool_create(pf, "sdp", 1024, 0, NULL); + if (!pool) { + PJ_LOG(1,(THIS_FILE, "Unable to create pool")); + return -1; + } + + pj_memset(&skinfo, 0, sizeof(skinfo)); + skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); + if (skinfo.rtp_sock == PJ_INVALID_SOCKET) { + PJ_LOG(1,(THIS_FILE, "Unable to create socket")); + goto on_error; + } + + pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port); + if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) { + PJ_LOG(1,(THIS_FILE, "Unable to bind socket")); + goto on_error; + } + + sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool); + if (!sdp_ses) { + PJ_LOG(1,(THIS_FILE, "Error parsing SDP")); + goto on_error; + } + + ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo); + if (!ses) { + PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP")); + goto on_error; + } + + if (pj_media_session_activate(ses) != 0) { + PJ_LOG(1,(THIS_FILE, "Error activating session")); + goto on_error; + } + + count = pj_media_session_enum_streams(ses, 2, info); + printf("\nDumping streams: \n"); + for (i=0; idir) { + case PJ_MEDIA_DIR_NONE: + dir = "- NONE -"; break; + case PJ_MEDIA_DIR_ENCODING: + dir = "SENDONLY"; break; + case PJ_MEDIA_DIR_DECODING: + dir = "RECVONLY"; break; + case PJ_MEDIA_DIR_ENCODING_DECODING: + dir = "SENDRECV"; break; + default: + dir = "?UNKNOWN"; break; + } + + local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name); + + printf(" Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n", + i, info[i]->type.slen, info[i]->type.ptr, + dir, + local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name), + info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port); + } + + puts("Press to quit"); + fgets(s, sizeof(s), stdin); + + pj_media_session_destroy(ses); + pj_sock_close(skinfo.rtp_sock); + pj_pool_release(pool); + + return 0; + +on_error: + if (ses) + pj_media_session_destroy(ses); + if (skinfo.rtp_sock != PJ_INVALID_SOCKET) + pj_sock_close(skinfo.rtp_sock); + if (pool) + pj_pool_release(pool); + return -1; +} + +#if WRITE_ORIGINAL_PCM +static pj_status_t convert(const char *src, const char *dst) +{ + char pcm[320]; + char frame[160]; + struct pj_audio_frame in, out; + + fhnd_pcm = fopen(src, "rb"); + if (!fhnd_pcm) + return -1; + fhnd = fopen(dst, "wb"); + if (!fhnd) + return -1; + + while (fread(pcm, 320, 1, fhnd_pcm) == 1) { + + in.type = PJ_AUDIO_FRAME_AUDIO; + in.buf = pcm; + in.size = 320; + out.buf = frame; + + if (codec->op->encode(codec, &in, 160, &out) != 0) + break; + + if (fwrite(frame, out.size, 1, fhnd) != 1) + break; + + } + + fclose(fhnd); + fclose(fhnd_pcm); + return 0; +} +#endif + +static void usage(const char *exe) +{ + printf("Usage: %s \n", exe); + puts("where:"); + puts(" play|record|send|recv"); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + usage(argv[0]); + return 1; + } + + pj_init(); + + init(); + + if (stricmp(argv[1], "record")==0) { + record_file("FILE.PCM"); + } else if (stricmp(argv[1], "play")==0) { + play_file("FILE.PCM"); + } else if (stricmp(argv[1], "send")==0) { + create_ses_by_remote_sdp(4002, listener_sdp); + } else if (stricmp(argv[1], "recv")==0) { + create_ses_by_remote_sdp(4000, talker_sdp); + } else { + usage(argv[0]); + } + deinit(); + return 0; +} diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c index 741140013..2a89a770d 100644 --- a/pjmedia/src/test/jbuf_test.c +++ b/pjmedia/src/test/jbuf_test.c @@ -1,137 +1,137 @@ -/* $Header: /pjproject/pjmedia/src/test/jbuf_test.c 9 6/24/05 11:18p Bennylp $ */ -#include -#include -#include -#include - -#define JB_MIN 1 -#define JB_MAX 8 -#define JB_BUF_SIZE 10 - -#define REPORT -//#define PRINT_COMMENT - -int jbuf_main(pj_pool_factory *pf) -{ - pj_jitter_buffer jb; - FILE *input = fopen("JBTEST.DAT", "rt"); - unsigned lastseq; - void *data = "Hello world"; - char line[1024], *p; - int lastget = 0, lastput = 0; - pj_pool_t *pool; - - pj_init(); - pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL); - - pj_jb_init(&jb, pool, JB_MIN, JB_MAX, JB_BUF_SIZE); - - lastseq = 1; - - while ((p=fgets(line, sizeof(line), input)) != NULL) { - - while (*p && isspace(*p)) - ++p; - - if (!*p) - continue; - - if (*p == '#') { -#ifdef PRINT_COMMENT - printf("\n%s", p); -#endif - continue; - } - - pj_jb_reset(&jb); -#ifdef REPORT - printf( "Initial\t%c size=%d prefetch=%d level=%d\n", - ' ', jb.lst.count, jb.prefetch, jb.level); -#endif - - while (*p) { - int c; - unsigned seq = 0; - void *thedata; - int status = 1234; - - c = *p++; - if (isspace(c)) - continue; - - if (c == '/') { - char *end; - - printf("/*"); - - do { - putchar(*++p); - } while (*p != '/'); - - putchar('\n'); - - c = *++p; - end = p; - - } - - if (isspace(c)) - continue; - - if (isdigit(c)) { - seq = c - '0'; - while (*p) { - c = *p++; - - if (isspace(c)) - continue; - - if (!isdigit(c)) - break; - - seq = seq * 10 + c - '0'; - } - } - - if (!*p) - break; - - switch (toupper(c)) { - case 'G': - seq = -1; - status = pj_jb_get(&jb, &seq, &thedata); - lastget = seq; - break; - case 'P': - if (seq == 0) - seq = lastseq++; - else - lastseq = seq; - status = pj_jb_put(&jb, seq, data); - if (status == 0) - lastput = seq; - break; - default: - printf("Unknown character '%c'\n", c); - break; - } - -#ifdef REPORT - printf("seq=%d\t%c rc=%d\tsize=%d\tpfch=%d\tlvl=%d\tmxl=%d\tdelay=%d\n", - seq, toupper(c), status, jb.lst.count, jb.prefetch, jb.level, jb.max_level, - (lastget>0 && lastput>0) ? lastput-lastget : -1); -#endif - } - } - -#ifdef REPORT - printf("0\t%c size=%d prefetch=%d level=%d\n", - ' ', jb.lst.count, jb.prefetch, jb.level); -#endif - - if (input != stdin) - fclose(input); - - pj_pool_release(pool); - return 0; -} +/* $Header: /pjproject/pjmedia/src/test/jbuf_test.c 9 6/24/05 11:18p Bennylp $ */ +#include +#include +#include +#include + +#define JB_MIN 1 +#define JB_MAX 8 +#define JB_BUF_SIZE 10 + +#define REPORT +//#define PRINT_COMMENT + +int jbuf_main(pj_pool_factory *pf) +{ + pj_jitter_buffer jb; + FILE *input = fopen("JBTEST.DAT", "rt"); + unsigned lastseq; + void *data = "Hello world"; + char line[1024], *p; + int lastget = 0, lastput = 0; + pj_pool_t *pool; + + pj_init(); + pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL); + + pj_jb_init(&jb, pool, JB_MIN, JB_MAX, JB_BUF_SIZE); + + lastseq = 1; + + while ((p=fgets(line, sizeof(line), input)) != NULL) { + + while (*p && isspace(*p)) + ++p; + + if (!*p) + continue; + + if (*p == '#') { +#ifdef PRINT_COMMENT + printf("\n%s", p); +#endif + continue; + } + + pj_jb_reset(&jb); +#ifdef REPORT + printf( "Initial\t%c size=%d prefetch=%d level=%d\n", + ' ', jb.lst.count, jb.prefetch, jb.level); +#endif + + while (*p) { + int c; + unsigned seq = 0; + void *thedata; + int status = 1234; + + c = *p++; + if (isspace(c)) + continue; + + if (c == '/') { + char *end; + + printf("/*"); + + do { + putchar(*++p); + } while (*p != '/'); + + putchar('\n'); + + c = *++p; + end = p; + + } + + if (isspace(c)) + continue; + + if (isdigit(c)) { + seq = c - '0'; + while (*p) { + c = *p++; + + if (isspace(c)) + continue; + + if (!isdigit(c)) + break; + + seq = seq * 10 + c - '0'; + } + } + + if (!*p) + break; + + switch (toupper(c)) { + case 'G': + seq = -1; + status = pj_jb_get(&jb, &seq, &thedata); + lastget = seq; + break; + case 'P': + if (seq == 0) + seq = lastseq++; + else + lastseq = seq; + status = pj_jb_put(&jb, seq, data); + if (status == 0) + lastput = seq; + break; + default: + printf("Unknown character '%c'\n", c); + break; + } + +#ifdef REPORT + printf("seq=%d\t%c rc=%d\tsize=%d\tpfch=%d\tlvl=%d\tmxl=%d\tdelay=%d\n", + seq, toupper(c), status, jb.lst.count, jb.prefetch, jb.level, jb.max_level, + (lastget>0 && lastput>0) ? lastput-lastget : -1); +#endif + } + } + +#ifdef REPORT + printf("0\t%c size=%d prefetch=%d level=%d\n", + ' ', jb.lst.count, jb.prefetch, jb.level); +#endif + + if (input != stdin) + fclose(input); + + pj_pool_release(pool); + return 0; +} diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c index ad683ffec..aa8b7df58 100644 --- a/pjmedia/src/test/main.c +++ b/pjmedia/src/test/main.c @@ -1,25 +1,25 @@ -/* $Header: /pjproject/pjmedia/src/test/main.c 9 6/24/05 11:18p Bennylp $ */ -#include -#include -#include - -pj_status_t session_test (pj_pool_factory *pf); -pj_status_t rtp_test (pj_pool_factory *pf); -pj_status_t sdp_test(pj_pool_factory *pf); -int jbuf_main(pj_pool_factory *pf); - -int main() -{ - pj_caching_pool caching_pool; - - pj_init(); - pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); - - sdp_test (&caching_pool.factory); - rtp_test(&caching_pool.factory); - session_test (&caching_pool.factory); - //jbuf_main(&caching_pool.factory); - - pj_caching_pool_destroy(&caching_pool); - return 0; -} +/* $Header: /pjproject/pjmedia/src/test/main.c 9 6/24/05 11:18p Bennylp $ */ +#include +#include +#include + +pj_status_t session_test (pj_pool_factory *pf); +pj_status_t rtp_test (pj_pool_factory *pf); +pj_status_t sdp_test(pj_pool_factory *pf); +int jbuf_main(pj_pool_factory *pf); + +int main() +{ + pj_caching_pool caching_pool; + + pj_init(); + pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0); + + sdp_test (&caching_pool.factory); + rtp_test(&caching_pool.factory); + session_test (&caching_pool.factory); + //jbuf_main(&caching_pool.factory); + + pj_caching_pool_destroy(&caching_pool); + return 0; +} diff --git a/pjmedia/src/test/rtp_test.c b/pjmedia/src/test/rtp_test.c index 9b70a894a..abb21c4e5 100644 --- a/pjmedia/src/test/rtp_test.c +++ b/pjmedia/src/test/rtp_test.c @@ -1,20 +1,20 @@ -/* $Header: /pjproject/pjmedia/src/test/rtp_test.c 4 3/12/05 4:21p Bennylp $ */ -#include -#include - -int rtp_test() -{ - pj_rtp_session rtp; - FILE *fhnd = fopen("RTP.DAT", "wb"); - const void *rtphdr; - int hdrlen; - - if (!fhnd) - return -1; - - pj_rtp_session_init (&rtp, 4, 0x12345678); - pj_rtp_encode_rtp (&rtp, 4, 0, 0, 160, &rtphdr, &hdrlen); - fwrite (rtphdr, hdrlen, 1, fhnd); - fclose(fhnd); - return 0; -} +/* $Header: /pjproject/pjmedia/src/test/rtp_test.c 4 3/12/05 4:21p Bennylp $ */ +#include +#include + +int rtp_test() +{ + pj_rtp_session rtp; + FILE *fhnd = fopen("RTP.DAT", "wb"); + const void *rtphdr; + int hdrlen; + + if (!fhnd) + return -1; + + pj_rtp_session_init (&rtp, 4, 0x12345678); + pj_rtp_encode_rtp (&rtp, 4, 0, 0, 160, &rtphdr, &hdrlen); + fwrite (rtphdr, hdrlen, 1, fhnd); + fclose(fhnd); + return 0; +} diff --git a/pjmedia/src/test/sdptest.c b/pjmedia/src/test/sdptest.c index 45a79f670..53a3fe1a4 100644 --- a/pjmedia/src/test/sdptest.c +++ b/pjmedia/src/test/sdptest.c @@ -1,104 +1,104 @@ -/* $Header: /pjproject/pjmedia/src/test/sdptest.c 8 6/12/05 11:36a Bennylp $ */ -#include -#include -#include -#include -#include - -static char *sdp[] = { - /* - "v=0\r\n" - "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n" - "s=SDP Seminar\r\n" - "i=A Seminar on the session description protocol\r\n" - "u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n" - "e=mjh@isi.edu (Mark Handley)\r\n" - "c=IN IP4 224.2.17.12/127\r\n" - "t=2873397496 2873404696\r\n" - "a=recvonly\r\n" - "m=audio 49170 RTP/AVP 0\r\n" - "m=video 51372 RTP/AVP 31\r\n" - "m=application 32416 udp wb\r\n" - "a=orient:portrait\r\n" - "m=audio 49230 RTP/AVP 96 97 98\r\n" - "a=rtpmap:96 L8/8000\r\n" - "a=rtpmap:97 L16/8000\r\n" - "a=rtpmap:98 L16/11025/2\r\n", - */ - "v=0\r\n" - "o=usera 2890844526 2890844527 IN IP4 alice.example.com\r\n" - "s=\r\n" - "c=IN IP4 alice.example.com\r\n" - "t=0 0\r\n" - "m=message 7394 msrp/tcp *\r\n" - "a=accept-types: message/cpim text/plain text/html\r\n" - "a=path:msrp://alice.example.com:7394/2s93i9;tcp\r\n" -}; - -static int sdp_perform_test(pj_pool_factory *pf) -{ - pj_pool_t *pool; - int inputlen, len; - pjsdp_session_desc *ses; - char buf[1500]; - enum { LOOP=1000000 }; - int i; - pj_time_val start, end; - - printf("Parsing and printing %d SDP messages..\n", LOOP); - - pool = pj_pool_create(pf, "", 4096, 0, NULL); - inputlen = strlen(sdp[0]); - pj_gettimeofday(&start); - for (i=0; i +#include +#include +#include +#include + +static char *sdp[] = { + /* + "v=0\r\n" + "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n" + "s=SDP Seminar\r\n" + "i=A Seminar on the session description protocol\r\n" + "u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n" + "e=mjh@isi.edu (Mark Handley)\r\n" + "c=IN IP4 224.2.17.12/127\r\n" + "t=2873397496 2873404696\r\n" + "a=recvonly\r\n" + "m=audio 49170 RTP/AVP 0\r\n" + "m=video 51372 RTP/AVP 31\r\n" + "m=application 32416 udp wb\r\n" + "a=orient:portrait\r\n" + "m=audio 49230 RTP/AVP 96 97 98\r\n" + "a=rtpmap:96 L8/8000\r\n" + "a=rtpmap:97 L16/8000\r\n" + "a=rtpmap:98 L16/11025/2\r\n", + */ + "v=0\r\n" + "o=usera 2890844526 2890844527 IN IP4 alice.example.com\r\n" + "s=\r\n" + "c=IN IP4 alice.example.com\r\n" + "t=0 0\r\n" + "m=message 7394 msrp/tcp *\r\n" + "a=accept-types: message/cpim text/plain text/html\r\n" + "a=path:msrp://alice.example.com:7394/2s93i9;tcp\r\n" +}; + +static int sdp_perform_test(pj_pool_factory *pf) +{ + pj_pool_t *pool; + int inputlen, len; + pjsdp_session_desc *ses; + char buf[1500]; + enum { LOOP=1000000 }; + int i; + pj_time_val start, end; + + printf("Parsing and printing %d SDP messages..\n", LOOP); + + pool = pj_pool_create(pf, "", 4096, 0, NULL); + inputlen = strlen(sdp[0]); + pj_gettimeofday(&start); + for (i=0; i -#include -#include -#include -#include -#include - -pj_status_t session_test (pj_pool_factory *pf) -{ - pj_med_mgr_t *mm; - pj_media_session_t *s1, *s2; - pj_pool_t *pool; - pjsdp_session_desc *sdp; - pj_media_stream_info sd_info; - char buf[1024]; - int len; - pj_media_stream_stat tx_stat, rx_stat; - - pool = pj_pool_create(pf, "test", 4096, 1024, NULL); - - // Init media manager. - mm = pj_med_mgr_create ( pf ); - - // Create caller session. - // THIS WILL DEFINITELY CRASH (NULL as argument)! - s1 = pj_media_session_create (mm, NULL); - - // Set caller's media to send-only. - sd_info.dir = PJ_MEDIA_DIR_ENCODING; - pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info); - - // Create caller SDP. - sdp = pj_media_session_create_sdp (s1, pool, 0); - len = pjsdp_print (sdp, buf, sizeof(buf)); - buf[len] = '\0'; - printf("Caller's initial SDP:\n\n%s\n\n", buf); - - // Parse SDP from caller. - sdp = pjsdp_parse (buf, len, pool); - - // Create callee session based on caller's SDP. - // THIS WILL DEFINITELY CRASH (NULL as argument)! - s2 = pj_media_session_create_from_sdp (mm, sdp, NULL); - - // Create callee SDP - sdp = pj_media_session_create_sdp (s2, pool, 0); - len = pjsdp_print (sdp, buf, sizeof(buf)); - buf[len] = '\0'; - printf("Callee's SDP:\n\n%s\n\n", buf); - - // Parse SDP from callee. - sdp = pjsdp_parse (buf, len, pool); - - // Update caller - pj_media_session_update (s1, sdp); - sdp = pj_media_session_create_sdp (s1, pool, 0); - pjsdp_print (sdp, buf, sizeof(buf)); - printf("Caller's SDP after update:\n\n%s\n\n", buf); - - // Now start media. - pj_media_session_activate (s2); - pj_media_session_activate (s1); - - // Wait - for (;;) { - int has_stat; - - printf("Enter q to exit, 1 or 2 to print statistics.\n"); - fgets (buf, 10, stdin); - has_stat = 0; - - switch (buf[0]) { - case 'q': - case 'Q': - goto done; - break; - case '1': - pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat); - has_stat = 1; - break; - case '2': - pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat); - has_stat = 1; - break; - } - - if (has_stat) { - pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat }; - const char *statname[2] = { "TX", "RX" }; - int i; - - for (i=0; i<2; ++i) { - printf("%s statistics:\n", statname[i]); - printf(" Pkt TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx); - printf(" Octets TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx); - printf(" Jitter %d ms\n", stat[i]->jitter); - printf(" Pkt lost %d\n", stat[i]->pkt_lost); - } - printf("\n"); - } - } - -done: - - // Done. - pj_pool_release (pool); - pj_media_session_destroy (s2); - pj_media_session_destroy (s1); - pj_med_mgr_destroy (mm); - - return 0; -} +/* $Header: /pjproject/pjmedia/src/test/session_test.c 8 6/12/05 11:36a Bennylp $ */ +#include +#include +#include +#include +#include +#include + +pj_status_t session_test (pj_pool_factory *pf) +{ + pj_med_mgr_t *mm; + pj_media_session_t *s1, *s2; + pj_pool_t *pool; + pjsdp_session_desc *sdp; + pj_media_stream_info sd_info; + char buf[1024]; + int len; + pj_media_stream_stat tx_stat, rx_stat; + + pool = pj_pool_create(pf, "test", 4096, 1024, NULL); + + // Init media manager. + mm = pj_med_mgr_create ( pf ); + + // Create caller session. + // THIS WILL DEFINITELY CRASH (NULL as argument)! + s1 = pj_media_session_create (mm, NULL); + + // Set caller's media to send-only. + sd_info.dir = PJ_MEDIA_DIR_ENCODING; + pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info); + + // Create caller SDP. + sdp = pj_media_session_create_sdp (s1, pool, 0); + len = pjsdp_print (sdp, buf, sizeof(buf)); + buf[len] = '\0'; + printf("Caller's initial SDP:\n\n%s\n\n", buf); + + // Parse SDP from caller. + sdp = pjsdp_parse (buf, len, pool); + + // Create callee session based on caller's SDP. + // THIS WILL DEFINITELY CRASH (NULL as argument)! + s2 = pj_media_session_create_from_sdp (mm, sdp, NULL); + + // Create callee SDP + sdp = pj_media_session_create_sdp (s2, pool, 0); + len = pjsdp_print (sdp, buf, sizeof(buf)); + buf[len] = '\0'; + printf("Callee's SDP:\n\n%s\n\n", buf); + + // Parse SDP from callee. + sdp = pjsdp_parse (buf, len, pool); + + // Update caller + pj_media_session_update (s1, sdp); + sdp = pj_media_session_create_sdp (s1, pool, 0); + pjsdp_print (sdp, buf, sizeof(buf)); + printf("Caller's SDP after update:\n\n%s\n\n", buf); + + // Now start media. + pj_media_session_activate (s2); + pj_media_session_activate (s1); + + // Wait + for (;;) { + int has_stat; + + printf("Enter q to exit, 1 or 2 to print statistics.\n"); + fgets (buf, 10, stdin); + has_stat = 0; + + switch (buf[0]) { + case 'q': + case 'Q': + goto done; + break; + case '1': + pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat); + has_stat = 1; + break; + case '2': + pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat); + has_stat = 1; + break; + } + + if (has_stat) { + pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat }; + const char *statname[2] = { "TX", "RX" }; + int i; + + for (i=0; i<2; ++i) { + printf("%s statistics:\n", statname[i]); + printf(" Pkt TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx); + printf(" Octets TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx); + printf(" Jitter %d ms\n", stat[i]->jitter); + printf(" Pkt lost %d\n", stat[i]->pkt_lost); + } + printf("\n"); + } + } + +done: + + // Done. + pj_pool_release (pool); + pj_media_session_destroy (s2); + pj_media_session_destroy (s1); + pj_med_mgr_destroy (mm); + + return 0; +} diff --git a/pjsip/build/Makefile b/pjsip/build/Makefile index 40783e181..930ffd1d8 100644 --- a/pjsip/build/Makefile +++ b/pjsip/build/Makefile @@ -1,73 +1,73 @@ -include make-$(TARGET).inc - -export PJSIP_SRCDIR = ../src/pjsip -export PJSIP_SRCEXT = .c -export PJSIP_SRCS = $(PJSIP_SOURCES) sip_auth.c sip_auth_msg.c sip_auth_parser.c \ - sip_endpoint.c sip_misc.c sip_msg.c sip_parser.c \ - sip_resolve.c sip_transaction.c sip_transport.c sip_uri.c - -export PJSIP_UA_SRCDIR = ../src/pjsip_mod_ua -export PJSIP_UA_SRCEXT = .c -export PJSIP_UA_SRCS = $(PJSIP_UA_SOURCES) sip_dialog.c sip_reg.c sip_ua.c - -export PJSIP_SIMPLE_SRCDIR = ../src/pjsip_simple -export PJSIP_SIMPLE_SRCEXT = .c -export PJSIP_SIMPLE_SRCS = $(PJSIP_SIMPLE_SOURCES) event_notify.c event_notify_msg.c \ - messaging.c pidf.c presence.c xpidf.c - -export PJSUA_SRCDIR = ../src/pjsua -export PJSUA_SRCEXT = .c -export PJSUA_SRCS = $(PJSUA_SOURCES) main.c getopt.c - -export TARGET CCOUT CC AR RANLIB MV RM RMDIR MKDIR OBJEXT LD LDOUT - -all: pjsip pjsip_ua pjsip_simple pjsua - -doc: - cd .. && doxygen docs/doxygen.cfg - -print: - $(MAKE) -f make-rules APP=PJSIP app=pjsip print_lib - $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua print_lib - $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple print_lib - $(MAKE) -f make-rules APP=PJSUA app=pjsua print_bin - -depend: - $(MAKE) -f make-rules APP=PJSIP app=pjsip depend - $(MAKE) -f make-rules APP=PJSUA app=pjsua depend - $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua depend - $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple depend - echo '$(PJSUA_EXE): $(PJSIP_LIB) $(PJSIP_UA_LIB)' >> .pjsua.depend - -dep: depend - -pjsip: - $(MAKE) -f make-rules APP=PJSIP app=pjsip $(PJSIP_LIB) - -pjsua: - $(MAKE) -f make-rules APP=PJSUA app=pjsua $(PJSUA_EXE) - -pjsip_ua: - $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua $(PJSIP_UA_LIB) - -pjsip_simple: - $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple $(PJSIP_SIMPLE_LIB) - -samples: - gcc $(_CFLAGS) -o ../bin/simpleua ../src/samples/simpleua.c $(_LDFLAGS) - -clean: - $(MAKE) -f make-rules APP=PJSIP app=pjsip clean - $(MAKE) -f make-rules APP=PJSUA app=pjsua clean - $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua clean - $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple clean - -realclean: - $(MAKE) -f make-rules APP=PJSIP app=pjsip realclean - $(MAKE) -f make-rules APP=PJSUA app=pjsua realclean - $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua realclean - $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple realclean - -distclean: realclean - - +include make-$(TARGET).inc + +export PJSIP_SRCDIR = ../src/pjsip +export PJSIP_SRCEXT = .c +export PJSIP_SRCS = $(PJSIP_SOURCES) sip_auth.c sip_auth_msg.c sip_auth_parser.c \ + sip_endpoint.c sip_misc.c sip_msg.c sip_parser.c \ + sip_resolve.c sip_transaction.c sip_transport.c sip_uri.c + +export PJSIP_UA_SRCDIR = ../src/pjsip_mod_ua +export PJSIP_UA_SRCEXT = .c +export PJSIP_UA_SRCS = $(PJSIP_UA_SOURCES) sip_dialog.c sip_reg.c sip_ua.c + +export PJSIP_SIMPLE_SRCDIR = ../src/pjsip_simple +export PJSIP_SIMPLE_SRCEXT = .c +export PJSIP_SIMPLE_SRCS = $(PJSIP_SIMPLE_SOURCES) event_notify.c event_notify_msg.c \ + messaging.c pidf.c presence.c xpidf.c + +export PJSUA_SRCDIR = ../src/pjsua +export PJSUA_SRCEXT = .c +export PJSUA_SRCS = $(PJSUA_SOURCES) main.c getopt.c + +export TARGET CCOUT CC AR RANLIB MV RM RMDIR MKDIR OBJEXT LD LDOUT + +all: pjsip pjsip_ua pjsip_simple pjsua + +doc: + cd .. && doxygen docs/doxygen.cfg + +print: + $(MAKE) -f make-rules APP=PJSIP app=pjsip print_lib + $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua print_lib + $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple print_lib + $(MAKE) -f make-rules APP=PJSUA app=pjsua print_bin + +depend: + $(MAKE) -f make-rules APP=PJSIP app=pjsip depend + $(MAKE) -f make-rules APP=PJSUA app=pjsua depend + $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua depend + $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple depend + echo '$(PJSUA_EXE): $(PJSIP_LIB) $(PJSIP_UA_LIB)' >> .pjsua.depend + +dep: depend + +pjsip: + $(MAKE) -f make-rules APP=PJSIP app=pjsip $(PJSIP_LIB) + +pjsua: + $(MAKE) -f make-rules APP=PJSUA app=pjsua $(PJSUA_EXE) + +pjsip_ua: + $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua $(PJSIP_UA_LIB) + +pjsip_simple: + $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple $(PJSIP_SIMPLE_LIB) + +samples: + gcc $(_CFLAGS) -o ../bin/simpleua ../src/samples/simpleua.c $(_LDFLAGS) + +clean: + $(MAKE) -f make-rules APP=PJSIP app=pjsip clean + $(MAKE) -f make-rules APP=PJSUA app=pjsua clean + $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua clean + $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple clean + +realclean: + $(MAKE) -f make-rules APP=PJSIP app=pjsip realclean + $(MAKE) -f make-rules APP=PJSUA app=pjsua realclean + $(MAKE) -f make-rules APP=PJSIP_UA app=pjsip_ua realclean + $(MAKE) -f make-rules APP=PJSIP_SIMPLE app=pjsip_simple realclean + +distclean: realclean + + diff --git a/pjsip/build/make-linux-i386.inc b/pjsip/build/make-linux-i386.inc index 62534942b..015876ab6 100644 --- a/pjsip/build/make-linux-i386.inc +++ b/pjsip/build/make-linux-i386.inc @@ -1,58 +1,58 @@ -# -# make-linux-i386.inc: Platform specific rules for Linux i386 compile. -# - -# -# Include PJLIB settings. -# -include ../../pjlib/build/make-$(TARGET).inc - -# -# make-optimize.inc declares PJSIP_OPTIMIZE variable -# -include make-optimize.inc - - - -_CFLAGS := $(_CFLAGS) -I../../pjlib/src -I../../pjmedia/src \ - -I../src $(PJSIP_OPTIMIZE) -_LDFLAGS := $(_LDFLAGS) -L../lib -L../../pjlib/lib \ - -L../../pjmedia/lib -lpjsip_core -lpjsip_ua -lpjsip_simple \ - -lpjmedia -lpj -lpthread - -# -# libpjsip_core.a -# -export PJSIP_SOURCES = -export PJSIP_CFLAGS = $(_CFLAGS) -export PJSIP_LIB = ../lib/libpjsip_core.a -export PJSIP_EXTRA_DEP := - -# -# libpjsip_ua.a -# -export PJSIP_UA_SOURCES = -export PJSIP_UA_CFLAGS = $(_CFLAGS) -export PJSIP_UA_LIB = ../lib/libpjsip_ua.a -export PJSIP_UA_EXTRA_DEP := - -# -# libpjsip_simple.a -# -export PJSIP_SIMPLE_SOURCES := -export PJSIP_SIMPLE_CFLAGS := $(_CFLAGS) -export PJSIP_SIMPLE_LIB := ../lib/libpjsip_simple.a -export PJSIP_SIMPLE_EXTRA_LIB := - -# -# pjsua.exe -# -export PJSUA_SOURCES = -export PJSUA_CFLAGS = $(_CFLAGS) -export PJSUA_LDFLAGS = $(_LDFLAGS) -export PJSUA_EXE = ../bin/pjsua -export PJSUA_EXTRA_DEP := ../lib/libpjsip_core.a ../lib/libpjsip_ua.a \ - ../lib/libpjsip_simple.a ../../pjlib/lib/libpj.a \ - ../../pjmedia/lib/libpjmedia.a - - +# +# make-linux-i386.inc: Platform specific rules for Linux i386 compile. +# + +# +# Include PJLIB settings. +# +include ../../pjlib/build/make-$(TARGET).inc + +# +# make-optimize.inc declares PJSIP_OPTIMIZE variable +# +include make-optimize.inc + + + +_CFLAGS := $(_CFLAGS) -I../../pjlib/src -I../../pjmedia/src \ + -I../src $(PJSIP_OPTIMIZE) +_LDFLAGS := $(_LDFLAGS) -L../lib -L../../pjlib/lib \ + -L../../pjmedia/lib -lpjsip_core -lpjsip_ua -lpjsip_simple \ + -lpjmedia -lpj -lpthread + +# +# libpjsip_core.a +# +export PJSIP_SOURCES = +export PJSIP_CFLAGS = $(_CFLAGS) +export PJSIP_LIB = ../lib/libpjsip_core.a +export PJSIP_EXTRA_DEP := + +# +# libpjsip_ua.a +# +export PJSIP_UA_SOURCES = +export PJSIP_UA_CFLAGS = $(_CFLAGS) +export PJSIP_UA_LIB = ../lib/libpjsip_ua.a +export PJSIP_UA_EXTRA_DEP := + +# +# libpjsip_simple.a +# +export PJSIP_SIMPLE_SOURCES := +export PJSIP_SIMPLE_CFLAGS := $(_CFLAGS) +export PJSIP_SIMPLE_LIB := ../lib/libpjsip_simple.a +export PJSIP_SIMPLE_EXTRA_LIB := + +# +# pjsua.exe +# +export PJSUA_SOURCES = +export PJSUA_CFLAGS = $(_CFLAGS) +export PJSUA_LDFLAGS = $(_LDFLAGS) +export PJSUA_EXE = ../bin/pjsua +export PJSUA_EXTRA_DEP := ../lib/libpjsip_core.a ../lib/libpjsip_ua.a \ + ../lib/libpjsip_simple.a ../../pjlib/lib/libpj.a \ + ../../pjmedia/lib/libpjmedia.a + + diff --git a/pjsip/build/make-mingw.inc b/pjsip/build/make-mingw.inc index 897a2b3de..96d40152b 100644 --- a/pjsip/build/make-mingw.inc +++ b/pjsip/build/make-mingw.inc @@ -1,56 +1,56 @@ -# -# Platform specific flags -# - -# -# Include PJLIB settings. -# -include ../../pjlib/build/make-$(TARGET).inc - - -# -# make-optimize.inc declares PJSIP_OPTIMIZE variable -# -include make-optimize.inc - - -_CFLAGS := $(_CFLAGS) -I../../pjlib/src -I../../pjmedia/src \ - $(PJSIP_OPTIMIZE) -_LDFLAGS := -L../../pjlib/lib -L../../pjmedia/lib \ - -lpjsip_ua -lpjsip_simple -lpjsip_core -lpjmedia $(_LDFLAGS) - -# -# libpjsip_core.a -# -export PJSIP_SOURCES := -export PJSIP_CFLAGS := $(_CFLAGS) -export PJSIP_LIB := ../lib/libpjsip_core.a -export PJSIP_EXTRA_DEP := - -# -# libpjsip_ua.a -# -export PJSIP_UA_SOURCES := -export PJSIP_UA_CFLAGS := $(_CFLAGS) -export PJSIP_UA_LIB := ../lib/libpjsip_ua.a -export PJSIP_UA_EXTRA_DEP := - -# -# libpjsip_simple.a -# -export PJSIP_SIMPLE_SOURCES := -export PJSIP_SIMPLE_CFLAGS := $(_CFLAGS) -export PJSIP_SIMPLE_LIB := ../lib/libpjsip_simple.a -export PJSIP_SIMPLE_EXTRA_LIB := - - -# -# pjsua.exe -# -export PJSUA_SOURCES := -export PJSUA_CFLAGS := $(_CFLAGS) -export PJSUA_LDFLAGS = $(_LDFLAGS) -export PJSUA_EXE = ../bin/pjsua_mingw.exe -export PJSUA_EXTRA_DEP := ../lib/libpjsip_core.a ../lib/libpjsip_ua.a \ - ../lib/libpjsip_simple.a ../../pjlib/lib/libpj.a \ - ../../pjmedia/lib/libpjmedia.a +# +# Platform specific flags +# + +# +# Include PJLIB settings. +# +include ../../pjlib/build/make-$(TARGET).inc + + +# +# make-optimize.inc declares PJSIP_OPTIMIZE variable +# +include make-optimize.inc + + +_CFLAGS := $(_CFLAGS) -I../../pjlib/src -I../../pjmedia/src \ + $(PJSIP_OPTIMIZE) +_LDFLAGS := -L../../pjlib/lib -L../../pjmedia/lib \ + -lpjsip_ua -lpjsip_simple -lpjsip_core -lpjmedia $(_LDFLAGS) + +# +# libpjsip_core.a +# +export PJSIP_SOURCES := +export PJSIP_CFLAGS := $(_CFLAGS) +export PJSIP_LIB := ../lib/libpjsip_core.a +export PJSIP_EXTRA_DEP := + +# +# libpjsip_ua.a +# +export PJSIP_UA_SOURCES := +export PJSIP_UA_CFLAGS := $(_CFLAGS) +export PJSIP_UA_LIB := ../lib/libpjsip_ua.a +export PJSIP_UA_EXTRA_DEP := + +# +# libpjsip_simple.a +# +export PJSIP_SIMPLE_SOURCES := +export PJSIP_SIMPLE_CFLAGS := $(_CFLAGS) +export PJSIP_SIMPLE_LIB := ../lib/libpjsip_simple.a +export PJSIP_SIMPLE_EXTRA_LIB := + + +# +# pjsua.exe +# +export PJSUA_SOURCES := +export PJSUA_CFLAGS := $(_CFLAGS) +export PJSUA_LDFLAGS = $(_LDFLAGS) +export PJSUA_EXE = ../bin/pjsua_mingw.exe +export PJSUA_EXTRA_DEP := ../lib/libpjsip_core.a ../lib/libpjsip_ua.a \ + ../lib/libpjsip_simple.a ../../pjlib/lib/libpj.a \ + ../../pjmedia/lib/libpjmedia.a diff --git a/pjsip/build/make-optimize.inc b/pjsip/build/make-optimize.inc index 76a065caa..ebf9b668b 100644 --- a/pjsip/build/make-optimize.inc +++ b/pjsip/build/make-optimize.inc @@ -1,10 +1,10 @@ - -# -# If MINSIZE is defined, optimize for code size. -# -ifdef MINSIZE -PJSIP_OPTIMIZE := $(PJSIP_OPTIMIZE) -DPJSIP_HAS_DUMP=0 -else -PJSIP_OPTIMIZE := $(PJSIP_OPTIMIZE) -endif - + +# +# If MINSIZE is defined, optimize for code size. +# +ifdef MINSIZE +PJSIP_OPTIMIZE := $(PJSIP_OPTIMIZE) -DPJSIP_HAS_DUMP=0 +else +PJSIP_OPTIMIZE := $(PJSIP_OPTIMIZE) +endif + diff --git a/pjsip/build/make-rules b/pjsip/build/make-rules index 5bc71e99f..d33f5835d 100644 --- a/pjsip/build/make-rules +++ b/pjsip/build/make-rules @@ -1,119 +1,119 @@ -LIBDIR = ../lib -BINDIR = ../bin - -# -# The full path of output lib file (e.g. ../lib/libapp.a). -# -LIB = $($(APP)_LIB) - -# -# The full path of output executable file (e.g. ../bin/app.exe). -# -EXE = $($(APP)_EXE) - -# -# Source directory -# -SRCDIR = $($(APP)_SRCDIR) - -# -# SRCEXT is .c -# SRCS is file.c -# FULL_SRCS is ../src/app/file.c -# -SRCEXT = $($(APP)_SRCEXT) -SRCS = $($(APP)_SRCS) -FULL_SRCS = $(foreach file, $(SRCS), $(SRCDIR)/$(file)) - - -# -# Output directory for object files (i.e. output/target) -# -OBJDIR = ./output/$(app)-$(TARGET) - -# -# OBJS1 is ./output/target/file.c -# OBJS is ./output/target/file.o -# -OBJS1 = $(foreach file, $(SRCS), $(OBJDIR)/$(file)) -OBJS = $(OBJS1:%$(SRCEXT)=%$(OBJEXT)) -OBJDIRS := $(sort $(foreach file, $(SRCS), $(dir $(OBJDIR)/$(file)))) - - -# -# When generating dependency (gcc -MM), ideally we use only either -# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work. -# -DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS) - -print_common: - @echo "###" - @echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):" - @echo "###" - @echo APP=$(APP) - @echo SRCEXT=$(SRCEXT) - @echo OBJDIR=$(OBJDIR) - @echo OBJS=$(OBJS) - @echo SRCDIR=$(SRCDIR) - @echo FULL_SRCS=$(FULL_SRCS) - @echo $(APP)_CFLAGS=$($(APP)_CFLAGS) - @echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS) - @echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS) - @echo DEPFLAGS=$(DEPFLAGS) - -print_bin: print_common - @echo EXE=$(EXE) - @echo BINDIR=$(BINDIR) - -print_lib: print_common - @echo LIB=$(LIB) - @echo LIBDIR=$(LIBDIR) - -$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) - $(AR) $(LIB) $(OBJS) - $(RANLIB) $(LIB) - -$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) - $(LD) $(LDOUT) $(EXE) $(OBJS) $($(APP)_LDFLAGS) - -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c - $(CC) $($(APP)_CFLAGS) $< $(CCOUT) $@ - -$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp - $(CC) $($(APP)_CXXFLAGS) $< $(CCOUT) $@ - -#$(OBJDIR): -# $(MKDIR) $(OBJDIR) -$(OBJDIRS): - $(MKDIR) $@ - -$(LIBDIR): - $(MKDIR) $(LIBDIR) - -$(BINDIR): - $(MKDIR) $(BINDIR) - -clean: - $(RM) -r $(OBJDIR)/* - $(RMDIR) $(OBJDIR) - -realclean: clean - $(RM) $(LIB) $(EXE) - $(RM) .$(app).depend - -depend: - $(RM) .$(app).depend - for F in $(FULL_SRCS); do \ - echo -n $(OBJDIR)/ >> .$(app).depend; \ - if gcc -MM $(DEPFLAGS) $$F >> .$(app).depend; then \ - true; \ - else \ - echo 'err:' >> .$(app).depend; \ - exit 1; \ - fi; \ - done - -dep: depend - --include .$(app).depend - +LIBDIR = ../lib +BINDIR = ../bin + +# +# The full path of output lib file (e.g. ../lib/libapp.a). +# +LIB = $($(APP)_LIB) + +# +# The full path of output executable file (e.g. ../bin/app.exe). +# +EXE = $($(APP)_EXE) + +# +# Source directory +# +SRCDIR = $($(APP)_SRCDIR) + +# +# SRCEXT is .c +# SRCS is file.c +# FULL_SRCS is ../src/app/file.c +# +SRCEXT = $($(APP)_SRCEXT) +SRCS = $($(APP)_SRCS) +FULL_SRCS = $(foreach file, $(SRCS), $(SRCDIR)/$(file)) + + +# +# Output directory for object files (i.e. output/target) +# +OBJDIR = ./output/$(app)-$(TARGET) + +# +# OBJS1 is ./output/target/file.c +# OBJS is ./output/target/file.o +# +OBJS1 = $(foreach file, $(SRCS), $(OBJDIR)/$(file)) +OBJS = $(OBJS1:%$(SRCEXT)=%$(OBJEXT)) +OBJDIRS := $(sort $(foreach file, $(SRCS), $(dir $(OBJDIR)/$(file)))) + + +# +# When generating dependency (gcc -MM), ideally we use only either +# CFLAGS or CXXFLAGS (not both). But I just couldn't make if/ifeq to work. +# +DEPFLAGS = $($(APP)_CXXFLAGS) $($(APP)_CFLAGS) + +print_common: + @echo "###" + @echo "### DUMPING MAKE VARIABLES (I WON'T DO ANYTHING ELSE):" + @echo "###" + @echo APP=$(APP) + @echo SRCEXT=$(SRCEXT) + @echo OBJDIR=$(OBJDIR) + @echo OBJS=$(OBJS) + @echo SRCDIR=$(SRCDIR) + @echo FULL_SRCS=$(FULL_SRCS) + @echo $(APP)_CFLAGS=$($(APP)_CFLAGS) + @echo $(APP)_CXXFLAGS=$($(APP)_CXXFLAGS) + @echo $(APP)_LDFLAGS=$($(APP)_LDFLAGS) + @echo DEPFLAGS=$(DEPFLAGS) + +print_bin: print_common + @echo EXE=$(EXE) + @echo BINDIR=$(BINDIR) + +print_lib: print_common + @echo LIB=$(LIB) + @echo LIBDIR=$(LIBDIR) + +$(LIB): $(LIBDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) + $(AR) $(LIB) $(OBJS) + $(RANLIB) $(LIB) + +$(EXE): $(BINDIR) $(OBJDIRS) $(OBJS) $($(APP)_EXTRA_DEP) + $(LD) $(LDOUT) $(EXE) $(OBJS) $($(APP)_LDFLAGS) + +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.c + $(CC) $($(APP)_CFLAGS) $< $(CCOUT) $@ + +$(OBJDIR)/%$(OBJEXT): $(SRCDIR)/%.cpp + $(CC) $($(APP)_CXXFLAGS) $< $(CCOUT) $@ + +#$(OBJDIR): +# $(MKDIR) $(OBJDIR) +$(OBJDIRS): + $(MKDIR) $@ + +$(LIBDIR): + $(MKDIR) $(LIBDIR) + +$(BINDIR): + $(MKDIR) $(BINDIR) + +clean: + $(RM) -r $(OBJDIR)/* + $(RMDIR) $(OBJDIR) + +realclean: clean + $(RM) $(LIB) $(EXE) + $(RM) .$(app).depend + +depend: + $(RM) .$(app).depend + for F in $(FULL_SRCS); do \ + echo -n $(OBJDIR)/ >> .$(app).depend; \ + if gcc -MM $(DEPFLAGS) $$F >> .$(app).depend; then \ + true; \ + else \ + echo 'err:' >> .$(app).depend; \ + exit 1; \ + fi; \ + done + +dep: depend + +-include .$(app).depend + diff --git a/pjsip/docs/PJSIP-Testing.doc b/pjsip/docs/PJSIP-Testing.doc index 59822a9f1..d0d4e4202 100644 Binary files a/pjsip/docs/PJSIP-Testing.doc and b/pjsip/docs/PJSIP-Testing.doc differ diff --git a/pjsip/src/pjsip/print.h b/pjsip/src/pjsip/print.h index 98e7195a6..83afa2f02 100644 --- a/pjsip/src/pjsip/print.h +++ b/pjsip/src/pjsip/print.h @@ -1,98 +1,98 @@ -/* $Header: /pjproject/pjsip/src/pjsip/print.h 9 6/22/05 12:27a Bennylp $ */ -#ifndef __PJSIP_PRINT_H__ -#define __PJSIP_PRINT_H__ - -#define copy_advance_check(buf,str) \ - do { \ - if ((str).slen+10 >= (endbuf-buf)) return -1; \ - pj_memcpy(buf, (str).ptr, (str).slen); \ - buf += (str).slen; \ - } while (0) - -/* -static char *imp_copy_advance_pair(char *buf, char *endbuf, const char *str1, int len1, const pj_str_t *str2) -{ - if (str2->slen) { - int printed = len1+str2->slen; - if (printed+10 >= (endbuf-buf)) return NULL; - pj_memcpy(buf,str1,len1); - pj_memcpy(buf+len1, str2->ptr, str2->slen); - return buf + printed; - } else - return buf; -} -*/ - -#define copy_advance_pair_check(buf,str1,len1,str2) \ - do { \ - if (str2.slen) { \ - printed = len1+str2.slen; \ - if (printed+10 >= (endbuf-buf)) return -1; \ - pj_memcpy(buf,str1,len1); \ - pj_memcpy(buf+len1, str2.ptr, str2.slen); \ - buf += printed; \ - } \ - } while (0) -/* -#define copy_advance_pair(buf,str1,len1,str2) \ - do { \ - buf = imp_copy_advance_pair(buf, endbuf, str1, len1, &str2); \ - if (buf == NULL) return -1; \ - } while (0) -*/ - -#define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \ - do { \ - if (str2.slen) { \ - printed = len1+str2.slen+2; \ - if (printed+10 >= (endbuf-buf)) return -1; \ - pj_memcpy(buf,str1,len1); \ - *(buf+len1)=quotebegin; \ - pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \ - *(buf+printed-1) = quoteend; \ - buf += printed; \ - } \ - } while (0) - -#define copy_advance_no_check(buf,str) \ - pj_memcpy(buf, (str).ptr, (str).slen); \ - buf += (str).slen; - -#define copy_advance_pair_no_check(buf,str1,len1,str2) \ - if (str2.slen) { \ - pj_memcpy(buf,str1,len1); \ - pj_memcpy(buf+len1, str2.ptr, str2.slen); \ - buf += len1+str2.slen; \ - } - -#define copy_advance copy_advance_check -#define copy_advance_pair copy_advance_pair_check -#define copy_advance_pair_quote copy_advance_pair_quote_check - -#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \ - do { \ - if (str2.slen && *str2.ptr!=quotebegin) \ - copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \ - else \ - copy_advance_pair(buf,str1,len1,str2); \ - } while (0) - -/* - * Internal type declarations. - */ -typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*); -typedef int (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len); - -extern const pj_str_t pjsip_hdr_names[]; - -PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr) -{ - pjsip_hdr *hdr = hptr; - hdr->type = htype; - hdr->name = hdr->sname = pjsip_hdr_names[htype]; - hdr->vptr = vptr; - pj_list_init(hdr); -} - -#endif /* __PJSIP_PRINT_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/print.h 9 6/22/05 12:27a Bennylp $ */ +#ifndef __PJSIP_PRINT_H__ +#define __PJSIP_PRINT_H__ + +#define copy_advance_check(buf,str) \ + do { \ + if ((str).slen+10 >= (endbuf-buf)) return -1; \ + pj_memcpy(buf, (str).ptr, (str).slen); \ + buf += (str).slen; \ + } while (0) + +/* +static char *imp_copy_advance_pair(char *buf, char *endbuf, const char *str1, int len1, const pj_str_t *str2) +{ + if (str2->slen) { + int printed = len1+str2->slen; + if (printed+10 >= (endbuf-buf)) return NULL; + pj_memcpy(buf,str1,len1); + pj_memcpy(buf+len1, str2->ptr, str2->slen); + return buf + printed; + } else + return buf; +} +*/ + +#define copy_advance_pair_check(buf,str1,len1,str2) \ + do { \ + if (str2.slen) { \ + printed = len1+str2.slen; \ + if (printed+10 >= (endbuf-buf)) return -1; \ + pj_memcpy(buf,str1,len1); \ + pj_memcpy(buf+len1, str2.ptr, str2.slen); \ + buf += printed; \ + } \ + } while (0) +/* +#define copy_advance_pair(buf,str1,len1,str2) \ + do { \ + buf = imp_copy_advance_pair(buf, endbuf, str1, len1, &str2); \ + if (buf == NULL) return -1; \ + } while (0) +*/ + +#define copy_advance_pair_quote_check(buf,str1,len1,str2,quotebegin,quoteend) \ + do { \ + if (str2.slen) { \ + printed = len1+str2.slen+2; \ + if (printed+10 >= (endbuf-buf)) return -1; \ + pj_memcpy(buf,str1,len1); \ + *(buf+len1)=quotebegin; \ + pj_memcpy(buf+len1+1, str2.ptr, str2.slen); \ + *(buf+printed-1) = quoteend; \ + buf += printed; \ + } \ + } while (0) + +#define copy_advance_no_check(buf,str) \ + pj_memcpy(buf, (str).ptr, (str).slen); \ + buf += (str).slen; + +#define copy_advance_pair_no_check(buf,str1,len1,str2) \ + if (str2.slen) { \ + pj_memcpy(buf,str1,len1); \ + pj_memcpy(buf+len1, str2.ptr, str2.slen); \ + buf += len1+str2.slen; \ + } + +#define copy_advance copy_advance_check +#define copy_advance_pair copy_advance_pair_check +#define copy_advance_pair_quote copy_advance_pair_quote_check + +#define copy_advance_pair_quote_cond(buf,str1,len1,str2,quotebegin,quoteend) \ + do { \ + if (str2.slen && *str2.ptr!=quotebegin) \ + copy_advance_pair_quote(buf,str1,len1,str2,quotebegin,quoteend); \ + else \ + copy_advance_pair(buf,str1,len1,str2); \ + } while (0) + +/* + * Internal type declarations. + */ +typedef void* (*pjsip_hdr_clone_fptr)(pj_pool_t *, const void*); +typedef int (*pjsip_hdr_print_fptr)(void *hdr, char *buf, pj_size_t len); + +extern const pj_str_t pjsip_hdr_names[]; + +PJ_INLINE(void) init_hdr(void *hptr, pjsip_hdr_e htype, void *vptr) +{ + pjsip_hdr *hdr = hptr; + hdr->type = htype; + hdr->name = hdr->sname = pjsip_hdr_names[htype]; + hdr->vptr = vptr; + pj_list_init(hdr); +} + +#endif /* __PJSIP_PRINT_H__ */ + diff --git a/pjsip/src/pjsip/sip_auth.c b/pjsip/src/pjsip/sip_auth.c index 697005f35..8f581a615 100644 --- a/pjsip/src/pjsip/sip_auth.c +++ b/pjsip/src/pjsip/sip_auth.c @@ -1,785 +1,785 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.c 12 9/11/05 9:08a Bennylp $ */ -#include -#include /* just to get pjsip_DIGEST_STR */ -#include -#include -#include -#include -#include -#include -#include - -/* Length of digest string. */ -#define MD5STRLEN 32 - -/* Maximum stack size we use for storing username+realm+password etc. */ -#define MAX_TEMP 128 - -/* A macro just to get rid of type mismatch between char and unsigned char */ -#define MD5_APPEND(pms,buf,len) md5_append(pms, (const unsigned char*)buf, len) - -/* Logging. */ -#define THIS_FILE "sip_auth.c" -#if 0 -# define AUTH_TRACE_(expr) PJ_LOG(3, expr) -#else -# define AUTH_TRACE_(expr) -#endif - -static const char hex[] = "0123456789abcdef"; - -/* Transform digest to string. - * output must be at least MD5STRLEN+1 bytes. - * - * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! - */ -static void digest2str(const unsigned char digest[], char *output) -{ - char *p = output; - int i; - - for (i = 0; i<16; ++i) { - int val = digest[i]; - *p++ = hex[val >> 4]; - *p++ = hex[val & 0x0F]; - } -} - -/* - * Create response digest based on the parameters and store the - * digest ASCII in 'result'. - */ -static void create_digest( pj_str_t *result, - const pj_str_t *nonce, - const pj_str_t *nc, - const pj_str_t *cnonce, - const pj_str_t *qop, - const pj_str_t *uri, - const pjsip_cred_info *cred_info, - const pj_str_t *method) -{ - char ha1[MD5STRLEN]; - char ha2[MD5STRLEN]; - unsigned char digest[16]; - md5_state_t pms; - - pj_assert(result->slen >= MD5STRLEN); - - AUTH_TRACE_((THIS_FILE, "Begin creating digest")); - - if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) { - /*** - *** ha1 = MD5(username ":" realm ":" password) - ***/ - md5_init(&pms); - MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen); - md5_finish(&pms, digest); - - digest2str(digest, ha1); - - } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) { - pj_assert(cred_info->data.slen == 32); - pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); - } - - AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1)); - - /*** - *** ha2 = MD5(method ":" req_uri) - ***/ - md5_init(&pms); - MD5_APPEND( &pms, method->ptr, method->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, uri->ptr, uri->slen); - md5_finish(&pms, digest); - digest2str(digest, ha2); - - AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2)); - - /*** - *** When qop is not used: - *** response = MD5(ha1 ":" nonce ":" ha2) - *** - *** When qop=auth is used: - *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) - ***/ - md5_init(&pms); - MD5_APPEND( &pms, ha1, MD5STRLEN); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, nonce->ptr, nonce->slen); - if (qop && qop->slen != 0) { - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, nc->ptr, nc->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, qop->ptr, qop->slen); - } - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, ha2, MD5STRLEN); - - /* This is the final response digest. */ - md5_finish(&pms, digest); - - /* Convert digest to string and store in chal->response. */ - result->slen = MD5STRLEN; - digest2str(digest, result->ptr); - - AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); - AUTH_TRACE_((THIS_FILE, "Digest created")); -} - -/* - * Finds out if qop offer contains "auth" token. - */ -static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer) -{ - pj_str_t qop; - char *p; - - pj_strdup_with_null( pool, &qop, qop_offer); - p = qop.ptr; - while (*p) { - *p = (char)tolower(*p); - ++p; - } - - p = qop.ptr; - while (*p) { - if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') { - int e = *(p+4); - if (e=='"' || e==',' || e==0) - return PJ_TRUE; - else - p += 4; - } else { - ++p; - } - } - - return PJ_FALSE; -} - -/* - * Generate response digest. - * Most of the parameters to generate the digest (i.e. username, realm, uri, - * and nonce) are expected to be in the credential. Additional parameters (i.e. - * password and method param) should be supplied in the argument. - * - * The resulting digest will be stored in cred->response. - * The pool is used to allocate 32 bytes to store the digest in cred->response. - */ -static pj_status_t respond_digest( pj_pool_t *pool, - pjsip_digest_credential *cred, - const pjsip_digest_challenge *chal, - const pj_str_t *uri, - const pjsip_cred_info *cred_info, - const pj_str_t *cnonce, - pj_uint32_t nc, - const pj_str_t *method) -{ - /* Check algorithm is supported. We only support MD5. */ - if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR)) - { - PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"", - chal->algorithm.slen, chal->algorithm.ptr)); - return -1; - } - - /* Build digest credential from arguments. */ - pj_strdup(pool, &cred->username, &cred_info->username); - pj_strdup(pool, &cred->realm, &chal->realm); - pj_strdup(pool, &cred->nonce, &chal->nonce); - pj_strdup(pool, &cred->uri, uri); - cred->algorithm = pjsip_MD5_STR; - pj_strdup(pool, &cred->opaque, &chal->opaque); - - /* Allocate memory. */ - cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN); - cred->response.slen = MD5STRLEN; - - if (chal->qop.slen == 0) { - /* Server doesn't require quality of protection. */ - - /* Convert digest to string and store in chal->response. */ - create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL, - uri, cred_info, method); - - } else if (has_auth_qop(pool, &chal->qop)) { - /* Server requires quality of protection. - * We respond with selecting "qop=auth" protection. - */ - cred->qop = pjsip_AUTH_STR; - cred->nc.ptr = pj_pool_alloc(pool, 16); - sprintf(cred->nc.ptr, "%06u", nc); - - if (cnonce && cnonce->slen) { - pj_strdup(pool, &cred->cnonce, cnonce); - } else { - pj_str_t dummy_cnonce = { "b39971", 6}; - pj_strdup(pool, &cred->cnonce, &dummy_cnonce); - } - - create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce, - &pjsip_AUTH_STR, uri, cred_info, method ); - - } else { - /* Server requires quality protection that we don't support. */ - PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", - chal->qop.slen, chal->qop.ptr)); - return -1; - } - - return 0; -} - -#if PJSIP_AUTH_QOP_SUPPORT -/* - * Update authentication session with a challenge. - */ -static void update_digest_session( pj_pool_t *ses_pool, - pjsip_auth_session *auth_sess, - const pjsip_www_authenticate_hdr *hdr ) -{ - if (hdr->challenge.digest.qop.slen == 0) - return; - - /* Initialize cnonce and qop if not present. */ - if (auth_sess->cnonce.slen == 0) { - /* Save the whole challenge */ - auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr); - - /* Create cnonce */ - pj_create_unique_string( ses_pool, &auth_sess->cnonce ); - - /* Initialize nonce-count */ - auth_sess->nc = 1; - - /* Save realm. */ - pj_assert(auth_sess->realm.slen != 0); - if (auth_sess->realm.slen == 0) { - pj_strdup(ses_pool, &auth_sess->realm, - &hdr->challenge.digest.realm); - } - - } else { - /* Update last_nonce and nonce-count */ - if (!pj_strcmp(&hdr->challenge.digest.nonce, - &auth_sess->last_chal->challenge.digest.nonce)) - { - /* Same nonce, increment nonce-count */ - ++auth_sess->nc; - } else { - /* Server gives new nonce. */ - pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce, - &hdr->challenge.digest.nonce); - /* Has the opaque changed? */ - if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque, - &hdr->challenge.digest.opaque)) - { - pj_strdup(ses_pool, - &auth_sess->last_chal->challenge.digest.opaque, - &hdr->challenge.digest.opaque); - } - auth_sess->nc = 1; - } - } -} -#endif /* PJSIP_AUTH_QOP_SUPPORT */ - - -/* Find authentication session in the list. */ -static pjsip_auth_session *find_session( pjsip_auth_session *sess_list, - const pj_str_t *realm ) -{ - pjsip_auth_session *sess = sess_list->next; - while (sess != sess_list) { - if (pj_stricmp(&sess->realm, realm) == 0) - return sess; - sess = sess->next; - } - - return NULL; -} - -/* - * Create Authorization/Proxy-Authorization response header based on the challege - * in WWW-Authenticate/Proxy-Authenticate header. - */ -PJ_DEF(pjsip_authorization_hdr*) -pjsip_auth_respond( pj_pool_t *req_pool, - const pjsip_www_authenticate_hdr *hdr, - const pjsip_uri *uri, - const pjsip_cred_info *cred_info, - const pjsip_method *method, - pj_pool_t *sess_pool, - pjsip_auth_session *auth_sess) -{ - pjsip_authorization_hdr *auth; - char tmp[PJSIP_MAX_URL_SIZE]; - pj_str_t uri_str; - pj_pool_t *pool; - - pj_assert(hdr != NULL); - pj_assert(uri != NULL); - pj_assert(cred_info != NULL); - pj_assert(method != NULL); - - /* Print URL in the original request. */ - uri_str.ptr = tmp; - uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp)); - if (uri_str.slen < 1) { - pj_assert(!"URL is too long!"); - PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!")); - return NULL; - } - -# if (PJSIP_AUTH_HEADER_CACHING) - { - pool = sess_pool; - PJ_UNUSED_ARG(req_pool); - } -# else - { - pool = req_pool; - PJ_UNUSED_ARG(sess_pool); - } -# endif - - if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) - auth = pjsip_authorization_hdr_create(pool); - else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) - auth = pjsip_proxy_authorization_hdr_create(pool); - else { - pj_assert(0); - return NULL; - } - - /* Only support digest scheme at the moment. */ - if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { - pj_status_t rc; - pj_str_t *cnonce = NULL; - pj_uint32_t nc = 1; - - /* Update the session (nonce-count etc) if required. */ -# if PJSIP_AUTH_QOP_SUPPORT - { - if (auth_sess) { - update_digest_session( sess_pool, auth_sess, hdr ); - - cnonce = &auth_sess->cnonce; - nc = auth_sess->nc; - } - } -# endif /* PJSIP_AUTH_QOP_SUPPORT */ - - auth->scheme = pjsip_DIGEST_STR; - rc = respond_digest( pool, &auth->credential.digest, - &hdr->challenge.digest, &uri_str, cred_info, - cnonce, nc, &method->name); - if (rc != 0) - return NULL; - - /* Set qop type in auth session the first time only. */ - if (hdr->challenge.digest.qop.slen != 0 && auth_sess) { - if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { - pj_str_t *qop_val = &auth->credential.digest.qop; - if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { - auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH; - } else { - auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN; - } - } - } - } else { - auth = NULL; - } - - /* Keep the new authorization header in the cache, only - * if no qop is not present. - */ -# if PJSIP_AUTH_HEADER_CACHING - { - if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { - pjsip_cached_auth_hdr *cached_hdr; - - /* Delete old header with the same method. */ - cached_hdr = auth_sess->cached_hdr.next; - while (cached_hdr != &auth_sess->cached_hdr) { - if (pjsip_method_cmp(method, &cached_hdr->method)==0) - break; - cached_hdr = cached_hdr->next; - } - - /* Save the header to the list. */ - if (cached_hdr != &auth_sess->cached_hdr) { - cached_hdr->hdr = auth; - } else { - cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); - pjsip_method_copy( pool, &cached_hdr->method, method); - cached_hdr->hdr = auth; - pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr ); - } - } - } -# endif - - return auth; - -} - -/* Verify incoming Authorization/Proxy-Authorization header against existing - * credentials. Will return TRUE if the authorization request matches any of - * the credential. - */ -PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr, - const pj_str_t *method, - const pjsip_cred_info *cred_info ) -{ - if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { - char digest_buf[MD5STRLEN]; - pj_str_t digest; - const pjsip_digest_credential *dig = &hdr->credential.digest; - - /* Check that username match. */ - if (pj_strcmp(&dig->username, &cred_info->username) != 0) - return PJ_FALSE; - - /* Check that realm match. */ - if (pj_strcmp(&dig->realm, &cred_info->realm) != 0) - return PJ_FALSE; - - /* Prepare for our digest calculation. */ - digest.ptr = digest_buf; - digest.slen = MD5STRLEN; - - /* Create digest for comparison. */ - create_digest( &digest, - &hdr->credential.digest.nonce, - &hdr->credential.digest.nc, - &hdr->credential.digest.cnonce, - &hdr->credential.digest.qop, - &hdr->credential.digest.uri, - cred_info, - method ); - - return pj_stricmp(&digest, &hdr->credential.digest.response) == 0; - - } else { - pj_assert(0); - return PJ_FALSE; - } -} - -/* Find credential to use for the specified realm and scheme. */ -PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count, - const pjsip_cred_info cred[], - const pj_str_t *realm, - const pj_str_t *scheme) -{ - unsigned i; - PJ_UNUSED_ARG(scheme) - for (i=0; ilast_chal != NULL); - - cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm, - &sess->last_chal->scheme ); - if (!cred) - return; - - - hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, - tdata->msg->line.req.uri, - cred, &tdata->msg->line.req.method, - sess_pool, sess); - if (hauth) { - pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth); - } -} -#endif - -/* - * Initialize new request message with authorization headers. - * This function will put Authorization/Proxy-Authorization headers to the - * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) - * and the session has previously sent Authorization/Proxy-Authorization header - * with the same method, then the same Authorization/Proxy-Authorization header - * will be resent from the cache only if qop is not present. If the stack is - * configured to automatically generate next Authorization/Proxy-Authorization - * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- - * Authorization headers are calculated and generated when they are not present - * in the case or if authorization session has qop. - * - * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag - * are not set, this function will do nothing. The stack then will only send - * Authorization/Proxy-Authorization to respond 401/407 response. - */ -PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, - pjsip_tx_data *tdata, - pjsip_auth_session *sess_list, - int cred_count, - const pjsip_cred_info cred_info[]) -{ - pjsip_auth_session *sess; - pjsip_method *method = &tdata->msg->line.req.method; - - pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); - - if (!sess_list) - return 0; - - sess = sess_list->next; - while (sess != sess_list) { - if (sess->qop_value == PJSIP_AUTH_QOP_NONE) { -# if (PJSIP_AUTH_HEADER_CACHING) - { - pjsip_cached_auth_hdr *entry = sess->cached_hdr.next; - while (entry != &sess->cached_hdr) { - if (pjsip_method_cmp(&entry->method, method)==0) { - pjsip_authorization_hdr *hauth; - hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); - } else { -# if (PJSIP_AUTH_AUTO_SEND_NEXT) - { - new_auth_for_req( tdata, sess_pool, sess, - cred_count, cred_info); - } -# else - { - PJ_UNUSED_ARG(sess_pool); - PJ_UNUSED_ARG(cred_count); - PJ_UNUSED_ARG(cred_info); - } -# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */ - } - entry = entry->next; - } - } -# elif (PJSIP_AUTH_AUTO_SEND_NEXT) - { - new_auth_for_req( tdata, sess_pool, sess, - cred_count, cred_info); - } -# else - { - PJ_UNUSED_ARG(sess_pool); - PJ_UNUSED_ARG(cred_count); - PJ_UNUSED_ARG(cred_info); - } -# endif /* PJSIP_AUTH_HEADER_CACHING */ - - } -# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) - else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) { - /* For qop="auth", we have to re-create the authorization header. - */ - const pjsip_cred_info *cred; - pjsip_authorization_hdr *hauth; - - cred = pjsip_auth_find_cred( cred_count, cred_info, - &sess->realm, - &sess->last_chal->scheme); - if (!cred) { - sess = sess->next; - continue; - } - - hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, - tdata->msg->line.req.uri, - cred, - &tdata->msg->line.req.method, - sess_pool, sess ); - if (hauth) { - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); - } - } -# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ - - sess = sess->next; - } - return 0; -} - -/* Process authorization challenge */ -static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool, - const pjsip_www_authenticate_hdr *hchal, - const pjsip_uri *uri, - pjsip_tx_data *tdata, - int cred_count, - const pjsip_cred_info cred_info[], - pj_pool_t *ses_pool, - pjsip_auth_session *auth_sess) -{ - const pjsip_cred_info *cred; - pjsip_authorization_hdr *sent_auth = NULL, *hauth; - pjsip_hdr *hdr; - - /* See if we have sent authorization header for this realm */ - hdr = tdata->msg->hdr.next; - while (hdr != &tdata->msg->hdr) { - if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE && - hdr->type == PJSIP_H_AUTHORIZATION) || - (hchal->type == PJSIP_H_PROXY_AUTHENTICATE && - hdr->type == PJSIP_H_PROXY_AUTHORIZATION)) - { - sent_auth = (pjsip_authorization_hdr*) hdr; - if (pj_stricmp(&hchal->challenge.common.realm, - &sent_auth->credential.common.realm )==0) - { - break; - } - } - hdr = hdr->next; - } - - /* If we have sent, see if server rejected because of stale nonce or - * other causes. - */ - if (hdr != &tdata->msg->hdr) { - if (hchal->challenge.digest.stale == 0) { - /* Our credential is rejected. No point in trying to re-supply - * the same credential. - */ - PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s", - sent_auth->credential.digest.username.slen, - sent_auth->credential.digest.username.ptr, - sent_auth->credential.digest.realm.slen, - sent_auth->credential.digest.realm.ptr)); - return NULL; - } - - /* Otherwise remove old, stale authorization header from the mesasge. - * We will supply a new one. - */ - pj_list_erase(sent_auth); - } - - /* Find credential to be used for the challenge. */ - cred = pjsip_auth_find_cred( cred_count, cred_info, - &hchal->challenge.common.realm, &hchal->scheme); - if (!cred) { - const pj_str_t *realm = &hchal->challenge.common.realm; - PJ_LOG(4,(THIS_FILE, - "Unable to set auth for %s: can not find credential for %.*s/%.*s", - tdata->obj_name, - realm->slen, realm->ptr, - hchal->scheme.slen, hchal->scheme.ptr)); - return NULL; - } - - /* Respond to authorization challenge. */ - hauth = pjsip_auth_respond( req_pool, hchal, uri, cred, - &tdata->msg->line.req.method, - ses_pool, auth_sess); - return hauth; -} - - -/* Reinitialize outgoing request after 401/407 response is received. - * The purpose of this function is: - * - to add a Authorization/Proxy-Authorization header. - * - to put the newly created Authorization/Proxy-Authorization header - * in cached_list. - */ -PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt, - pj_pool_t *ses_pool, - pjsip_auth_session *sess_list, - int cred_count, - const pjsip_cred_info cred_info[], - pjsip_tx_data *tdata, - const pjsip_rx_data *rdata) -{ - const pjsip_hdr *hdr; - pjsip_via_hdr *via; - - PJ_UNUSED_ARG(endpt) - - pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG); - pj_assert(rdata->msg->line.status.code == 401 || - rdata->msg->line.status.code == 407 ); - - /* - * Respond to each authentication challenge. - */ - hdr = rdata->msg->hdr.next; - while (hdr != &rdata->msg->hdr) { - pjsip_auth_session *sess; - const pjsip_www_authenticate_hdr *hchal; - pjsip_authorization_hdr *hauth; - - /* Find WWW-Authenticate or Proxy-Authenticate header. */ - while (hdr->type != PJSIP_H_WWW_AUTHENTICATE && - hdr->type != PJSIP_H_PROXY_AUTHENTICATE && - hdr != &rdata->msg->hdr) - { - hdr = hdr->next; - } - if (hdr == &rdata->msg->hdr) - break; - - hchal = (const pjsip_www_authenticate_hdr*) hdr; - - /* Find authentication session for this realm, create a new one - * if not present. - */ - sess = find_session(sess_list, &hchal->challenge.common.realm ); - if (!sess) { - sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess)); - pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm); - sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); -# if (PJSIP_AUTH_HEADER_CACHING) - { - pj_list_init(&sess->cached_hdr); - } -# endif - pj_list_insert_before( sess_list, sess ); - } - - /* Create authorization header for this challenge, and update - * authorization session. - */ - hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, - tdata, cred_count, cred_info, ses_pool, sess ); - if (!hauth) - return NULL; - - /* Add to the message. */ - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); - - /* Process next header. */ - hdr = hdr->next; - } - - - /* Remove branch param in Via header. */ - via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param.slen = 0; - - /* Increment reference counter. */ - pjsip_tx_data_add_ref(tdata); - - /* Done. */ - return tdata; -} - +/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.c 12 9/11/05 9:08a Bennylp $ */ +#include +#include /* just to get pjsip_DIGEST_STR */ +#include +#include +#include +#include +#include +#include +#include + +/* Length of digest string. */ +#define MD5STRLEN 32 + +/* Maximum stack size we use for storing username+realm+password etc. */ +#define MAX_TEMP 128 + +/* A macro just to get rid of type mismatch between char and unsigned char */ +#define MD5_APPEND(pms,buf,len) md5_append(pms, (const unsigned char*)buf, len) + +/* Logging. */ +#define THIS_FILE "sip_auth.c" +#if 0 +# define AUTH_TRACE_(expr) PJ_LOG(3, expr) +#else +# define AUTH_TRACE_(expr) +#endif + +static const char hex[] = "0123456789abcdef"; + +/* Transform digest to string. + * output must be at least MD5STRLEN+1 bytes. + * + * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! + */ +static void digest2str(const unsigned char digest[], char *output) +{ + char *p = output; + int i; + + for (i = 0; i<16; ++i) { + int val = digest[i]; + *p++ = hex[val >> 4]; + *p++ = hex[val & 0x0F]; + } +} + +/* + * Create response digest based on the parameters and store the + * digest ASCII in 'result'. + */ +static void create_digest( pj_str_t *result, + const pj_str_t *nonce, + const pj_str_t *nc, + const pj_str_t *cnonce, + const pj_str_t *qop, + const pj_str_t *uri, + const pjsip_cred_info *cred_info, + const pj_str_t *method) +{ + char ha1[MD5STRLEN]; + char ha2[MD5STRLEN]; + unsigned char digest[16]; + md5_state_t pms; + + pj_assert(result->slen >= MD5STRLEN); + + AUTH_TRACE_((THIS_FILE, "Begin creating digest")); + + if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) { + /*** + *** ha1 = MD5(username ":" realm ":" password) + ***/ + md5_init(&pms); + MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen); + md5_finish(&pms, digest); + + digest2str(digest, ha1); + + } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) { + pj_assert(cred_info->data.slen == 32); + pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); + } + + AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1)); + + /*** + *** ha2 = MD5(method ":" req_uri) + ***/ + md5_init(&pms); + MD5_APPEND( &pms, method->ptr, method->slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, uri->ptr, uri->slen); + md5_finish(&pms, digest); + digest2str(digest, ha2); + + AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2)); + + /*** + *** When qop is not used: + *** response = MD5(ha1 ":" nonce ":" ha2) + *** + *** When qop=auth is used: + *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) + ***/ + md5_init(&pms); + MD5_APPEND( &pms, ha1, MD5STRLEN); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, nonce->ptr, nonce->slen); + if (qop && qop->slen != 0) { + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, nc->ptr, nc->slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, qop->ptr, qop->slen); + } + MD5_APPEND( &pms, ":", 1); + MD5_APPEND( &pms, ha2, MD5STRLEN); + + /* This is the final response digest. */ + md5_finish(&pms, digest); + + /* Convert digest to string and store in chal->response. */ + result->slen = MD5STRLEN; + digest2str(digest, result->ptr); + + AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); + AUTH_TRACE_((THIS_FILE, "Digest created")); +} + +/* + * Finds out if qop offer contains "auth" token. + */ +static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer) +{ + pj_str_t qop; + char *p; + + pj_strdup_with_null( pool, &qop, qop_offer); + p = qop.ptr; + while (*p) { + *p = (char)tolower(*p); + ++p; + } + + p = qop.ptr; + while (*p) { + if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') { + int e = *(p+4); + if (e=='"' || e==',' || e==0) + return PJ_TRUE; + else + p += 4; + } else { + ++p; + } + } + + return PJ_FALSE; +} + +/* + * Generate response digest. + * Most of the parameters to generate the digest (i.e. username, realm, uri, + * and nonce) are expected to be in the credential. Additional parameters (i.e. + * password and method param) should be supplied in the argument. + * + * The resulting digest will be stored in cred->response. + * The pool is used to allocate 32 bytes to store the digest in cred->response. + */ +static pj_status_t respond_digest( pj_pool_t *pool, + pjsip_digest_credential *cred, + const pjsip_digest_challenge *chal, + const pj_str_t *uri, + const pjsip_cred_info *cred_info, + const pj_str_t *cnonce, + pj_uint32_t nc, + const pj_str_t *method) +{ + /* Check algorithm is supported. We only support MD5. */ + if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR)) + { + PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"", + chal->algorithm.slen, chal->algorithm.ptr)); + return -1; + } + + /* Build digest credential from arguments. */ + pj_strdup(pool, &cred->username, &cred_info->username); + pj_strdup(pool, &cred->realm, &chal->realm); + pj_strdup(pool, &cred->nonce, &chal->nonce); + pj_strdup(pool, &cred->uri, uri); + cred->algorithm = pjsip_MD5_STR; + pj_strdup(pool, &cred->opaque, &chal->opaque); + + /* Allocate memory. */ + cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN); + cred->response.slen = MD5STRLEN; + + if (chal->qop.slen == 0) { + /* Server doesn't require quality of protection. */ + + /* Convert digest to string and store in chal->response. */ + create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL, + uri, cred_info, method); + + } else if (has_auth_qop(pool, &chal->qop)) { + /* Server requires quality of protection. + * We respond with selecting "qop=auth" protection. + */ + cred->qop = pjsip_AUTH_STR; + cred->nc.ptr = pj_pool_alloc(pool, 16); + sprintf(cred->nc.ptr, "%06u", nc); + + if (cnonce && cnonce->slen) { + pj_strdup(pool, &cred->cnonce, cnonce); + } else { + pj_str_t dummy_cnonce = { "b39971", 6}; + pj_strdup(pool, &cred->cnonce, &dummy_cnonce); + } + + create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce, + &pjsip_AUTH_STR, uri, cred_info, method ); + + } else { + /* Server requires quality protection that we don't support. */ + PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", + chal->qop.slen, chal->qop.ptr)); + return -1; + } + + return 0; +} + +#if PJSIP_AUTH_QOP_SUPPORT +/* + * Update authentication session with a challenge. + */ +static void update_digest_session( pj_pool_t *ses_pool, + pjsip_auth_session *auth_sess, + const pjsip_www_authenticate_hdr *hdr ) +{ + if (hdr->challenge.digest.qop.slen == 0) + return; + + /* Initialize cnonce and qop if not present. */ + if (auth_sess->cnonce.slen == 0) { + /* Save the whole challenge */ + auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr); + + /* Create cnonce */ + pj_create_unique_string( ses_pool, &auth_sess->cnonce ); + + /* Initialize nonce-count */ + auth_sess->nc = 1; + + /* Save realm. */ + pj_assert(auth_sess->realm.slen != 0); + if (auth_sess->realm.slen == 0) { + pj_strdup(ses_pool, &auth_sess->realm, + &hdr->challenge.digest.realm); + } + + } else { + /* Update last_nonce and nonce-count */ + if (!pj_strcmp(&hdr->challenge.digest.nonce, + &auth_sess->last_chal->challenge.digest.nonce)) + { + /* Same nonce, increment nonce-count */ + ++auth_sess->nc; + } else { + /* Server gives new nonce. */ + pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce, + &hdr->challenge.digest.nonce); + /* Has the opaque changed? */ + if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque, + &hdr->challenge.digest.opaque)) + { + pj_strdup(ses_pool, + &auth_sess->last_chal->challenge.digest.opaque, + &hdr->challenge.digest.opaque); + } + auth_sess->nc = 1; + } + } +} +#endif /* PJSIP_AUTH_QOP_SUPPORT */ + + +/* Find authentication session in the list. */ +static pjsip_auth_session *find_session( pjsip_auth_session *sess_list, + const pj_str_t *realm ) +{ + pjsip_auth_session *sess = sess_list->next; + while (sess != sess_list) { + if (pj_stricmp(&sess->realm, realm) == 0) + return sess; + sess = sess->next; + } + + return NULL; +} + +/* + * Create Authorization/Proxy-Authorization response header based on the challege + * in WWW-Authenticate/Proxy-Authenticate header. + */ +PJ_DEF(pjsip_authorization_hdr*) +pjsip_auth_respond( pj_pool_t *req_pool, + const pjsip_www_authenticate_hdr *hdr, + const pjsip_uri *uri, + const pjsip_cred_info *cred_info, + const pjsip_method *method, + pj_pool_t *sess_pool, + pjsip_auth_session *auth_sess) +{ + pjsip_authorization_hdr *auth; + char tmp[PJSIP_MAX_URL_SIZE]; + pj_str_t uri_str; + pj_pool_t *pool; + + pj_assert(hdr != NULL); + pj_assert(uri != NULL); + pj_assert(cred_info != NULL); + pj_assert(method != NULL); + + /* Print URL in the original request. */ + uri_str.ptr = tmp; + uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp)); + if (uri_str.slen < 1) { + pj_assert(!"URL is too long!"); + PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!")); + return NULL; + } + +# if (PJSIP_AUTH_HEADER_CACHING) + { + pool = sess_pool; + PJ_UNUSED_ARG(req_pool); + } +# else + { + pool = req_pool; + PJ_UNUSED_ARG(sess_pool); + } +# endif + + if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) + auth = pjsip_authorization_hdr_create(pool); + else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) + auth = pjsip_proxy_authorization_hdr_create(pool); + else { + pj_assert(0); + return NULL; + } + + /* Only support digest scheme at the moment. */ + if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { + pj_status_t rc; + pj_str_t *cnonce = NULL; + pj_uint32_t nc = 1; + + /* Update the session (nonce-count etc) if required. */ +# if PJSIP_AUTH_QOP_SUPPORT + { + if (auth_sess) { + update_digest_session( sess_pool, auth_sess, hdr ); + + cnonce = &auth_sess->cnonce; + nc = auth_sess->nc; + } + } +# endif /* PJSIP_AUTH_QOP_SUPPORT */ + + auth->scheme = pjsip_DIGEST_STR; + rc = respond_digest( pool, &auth->credential.digest, + &hdr->challenge.digest, &uri_str, cred_info, + cnonce, nc, &method->name); + if (rc != 0) + return NULL; + + /* Set qop type in auth session the first time only. */ + if (hdr->challenge.digest.qop.slen != 0 && auth_sess) { + if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { + pj_str_t *qop_val = &auth->credential.digest.qop; + if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { + auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH; + } else { + auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN; + } + } + } + } else { + auth = NULL; + } + + /* Keep the new authorization header in the cache, only + * if no qop is not present. + */ +# if PJSIP_AUTH_HEADER_CACHING + { + if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { + pjsip_cached_auth_hdr *cached_hdr; + + /* Delete old header with the same method. */ + cached_hdr = auth_sess->cached_hdr.next; + while (cached_hdr != &auth_sess->cached_hdr) { + if (pjsip_method_cmp(method, &cached_hdr->method)==0) + break; + cached_hdr = cached_hdr->next; + } + + /* Save the header to the list. */ + if (cached_hdr != &auth_sess->cached_hdr) { + cached_hdr->hdr = auth; + } else { + cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); + pjsip_method_copy( pool, &cached_hdr->method, method); + cached_hdr->hdr = auth; + pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr ); + } + } + } +# endif + + return auth; + +} + +/* Verify incoming Authorization/Proxy-Authorization header against existing + * credentials. Will return TRUE if the authorization request matches any of + * the credential. + */ +PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr, + const pj_str_t *method, + const pjsip_cred_info *cred_info ) +{ + if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { + char digest_buf[MD5STRLEN]; + pj_str_t digest; + const pjsip_digest_credential *dig = &hdr->credential.digest; + + /* Check that username match. */ + if (pj_strcmp(&dig->username, &cred_info->username) != 0) + return PJ_FALSE; + + /* Check that realm match. */ + if (pj_strcmp(&dig->realm, &cred_info->realm) != 0) + return PJ_FALSE; + + /* Prepare for our digest calculation. */ + digest.ptr = digest_buf; + digest.slen = MD5STRLEN; + + /* Create digest for comparison. */ + create_digest( &digest, + &hdr->credential.digest.nonce, + &hdr->credential.digest.nc, + &hdr->credential.digest.cnonce, + &hdr->credential.digest.qop, + &hdr->credential.digest.uri, + cred_info, + method ); + + return pj_stricmp(&digest, &hdr->credential.digest.response) == 0; + + } else { + pj_assert(0); + return PJ_FALSE; + } +} + +/* Find credential to use for the specified realm and scheme. */ +PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count, + const pjsip_cred_info cred[], + const pj_str_t *realm, + const pj_str_t *scheme) +{ + unsigned i; + PJ_UNUSED_ARG(scheme) + for (i=0; ilast_chal != NULL); + + cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm, + &sess->last_chal->scheme ); + if (!cred) + return; + + + hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, + tdata->msg->line.req.uri, + cred, &tdata->msg->line.req.method, + sess_pool, sess); + if (hauth) { + pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth); + } +} +#endif + +/* + * Initialize new request message with authorization headers. + * This function will put Authorization/Proxy-Authorization headers to the + * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) + * and the session has previously sent Authorization/Proxy-Authorization header + * with the same method, then the same Authorization/Proxy-Authorization header + * will be resent from the cache only if qop is not present. If the stack is + * configured to automatically generate next Authorization/Proxy-Authorization + * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- + * Authorization headers are calculated and generated when they are not present + * in the case or if authorization session has qop. + * + * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag + * are not set, this function will do nothing. The stack then will only send + * Authorization/Proxy-Authorization to respond 401/407 response. + */ +PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, + pjsip_tx_data *tdata, + pjsip_auth_session *sess_list, + int cred_count, + const pjsip_cred_info cred_info[]) +{ + pjsip_auth_session *sess; + pjsip_method *method = &tdata->msg->line.req.method; + + pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); + + if (!sess_list) + return 0; + + sess = sess_list->next; + while (sess != sess_list) { + if (sess->qop_value == PJSIP_AUTH_QOP_NONE) { +# if (PJSIP_AUTH_HEADER_CACHING) + { + pjsip_cached_auth_hdr *entry = sess->cached_hdr.next; + while (entry != &sess->cached_hdr) { + if (pjsip_method_cmp(&entry->method, method)==0) { + pjsip_authorization_hdr *hauth; + hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); + } else { +# if (PJSIP_AUTH_AUTO_SEND_NEXT) + { + new_auth_for_req( tdata, sess_pool, sess, + cred_count, cred_info); + } +# else + { + PJ_UNUSED_ARG(sess_pool); + PJ_UNUSED_ARG(cred_count); + PJ_UNUSED_ARG(cred_info); + } +# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */ + } + entry = entry->next; + } + } +# elif (PJSIP_AUTH_AUTO_SEND_NEXT) + { + new_auth_for_req( tdata, sess_pool, sess, + cred_count, cred_info); + } +# else + { + PJ_UNUSED_ARG(sess_pool); + PJ_UNUSED_ARG(cred_count); + PJ_UNUSED_ARG(cred_info); + } +# endif /* PJSIP_AUTH_HEADER_CACHING */ + + } +# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) + else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) { + /* For qop="auth", we have to re-create the authorization header. + */ + const pjsip_cred_info *cred; + pjsip_authorization_hdr *hauth; + + cred = pjsip_auth_find_cred( cred_count, cred_info, + &sess->realm, + &sess->last_chal->scheme); + if (!cred) { + sess = sess->next; + continue; + } + + hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, + tdata->msg->line.req.uri, + cred, + &tdata->msg->line.req.method, + sess_pool, sess ); + if (hauth) { + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); + } + } +# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ + + sess = sess->next; + } + return 0; +} + +/* Process authorization challenge */ +static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool, + const pjsip_www_authenticate_hdr *hchal, + const pjsip_uri *uri, + pjsip_tx_data *tdata, + int cred_count, + const pjsip_cred_info cred_info[], + pj_pool_t *ses_pool, + pjsip_auth_session *auth_sess) +{ + const pjsip_cred_info *cred; + pjsip_authorization_hdr *sent_auth = NULL, *hauth; + pjsip_hdr *hdr; + + /* See if we have sent authorization header for this realm */ + hdr = tdata->msg->hdr.next; + while (hdr != &tdata->msg->hdr) { + if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE && + hdr->type == PJSIP_H_AUTHORIZATION) || + (hchal->type == PJSIP_H_PROXY_AUTHENTICATE && + hdr->type == PJSIP_H_PROXY_AUTHORIZATION)) + { + sent_auth = (pjsip_authorization_hdr*) hdr; + if (pj_stricmp(&hchal->challenge.common.realm, + &sent_auth->credential.common.realm )==0) + { + break; + } + } + hdr = hdr->next; + } + + /* If we have sent, see if server rejected because of stale nonce or + * other causes. + */ + if (hdr != &tdata->msg->hdr) { + if (hchal->challenge.digest.stale == 0) { + /* Our credential is rejected. No point in trying to re-supply + * the same credential. + */ + PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s", + sent_auth->credential.digest.username.slen, + sent_auth->credential.digest.username.ptr, + sent_auth->credential.digest.realm.slen, + sent_auth->credential.digest.realm.ptr)); + return NULL; + } + + /* Otherwise remove old, stale authorization header from the mesasge. + * We will supply a new one. + */ + pj_list_erase(sent_auth); + } + + /* Find credential to be used for the challenge. */ + cred = pjsip_auth_find_cred( cred_count, cred_info, + &hchal->challenge.common.realm, &hchal->scheme); + if (!cred) { + const pj_str_t *realm = &hchal->challenge.common.realm; + PJ_LOG(4,(THIS_FILE, + "Unable to set auth for %s: can not find credential for %.*s/%.*s", + tdata->obj_name, + realm->slen, realm->ptr, + hchal->scheme.slen, hchal->scheme.ptr)); + return NULL; + } + + /* Respond to authorization challenge. */ + hauth = pjsip_auth_respond( req_pool, hchal, uri, cred, + &tdata->msg->line.req.method, + ses_pool, auth_sess); + return hauth; +} + + +/* Reinitialize outgoing request after 401/407 response is received. + * The purpose of this function is: + * - to add a Authorization/Proxy-Authorization header. + * - to put the newly created Authorization/Proxy-Authorization header + * in cached_list. + */ +PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt, + pj_pool_t *ses_pool, + pjsip_auth_session *sess_list, + int cred_count, + const pjsip_cred_info cred_info[], + pjsip_tx_data *tdata, + const pjsip_rx_data *rdata) +{ + const pjsip_hdr *hdr; + pjsip_via_hdr *via; + + PJ_UNUSED_ARG(endpt) + + pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG); + pj_assert(rdata->msg->line.status.code == 401 || + rdata->msg->line.status.code == 407 ); + + /* + * Respond to each authentication challenge. + */ + hdr = rdata->msg->hdr.next; + while (hdr != &rdata->msg->hdr) { + pjsip_auth_session *sess; + const pjsip_www_authenticate_hdr *hchal; + pjsip_authorization_hdr *hauth; + + /* Find WWW-Authenticate or Proxy-Authenticate header. */ + while (hdr->type != PJSIP_H_WWW_AUTHENTICATE && + hdr->type != PJSIP_H_PROXY_AUTHENTICATE && + hdr != &rdata->msg->hdr) + { + hdr = hdr->next; + } + if (hdr == &rdata->msg->hdr) + break; + + hchal = (const pjsip_www_authenticate_hdr*) hdr; + + /* Find authentication session for this realm, create a new one + * if not present. + */ + sess = find_session(sess_list, &hchal->challenge.common.realm ); + if (!sess) { + sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess)); + pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm); + sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); +# if (PJSIP_AUTH_HEADER_CACHING) + { + pj_list_init(&sess->cached_hdr); + } +# endif + pj_list_insert_before( sess_list, sess ); + } + + /* Create authorization header for this challenge, and update + * authorization session. + */ + hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, + tdata, cred_count, cred_info, ses_pool, sess ); + if (!hauth) + return NULL; + + /* Add to the message. */ + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); + + /* Process next header. */ + hdr = hdr->next; + } + + + /* Remove branch param in Via header. */ + via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); + via->branch_param.slen = 0; + + /* Increment reference counter. */ + pjsip_tx_data_add_ref(tdata); + + /* Done. */ + return tdata; +} + diff --git a/pjsip/src/pjsip/sip_auth.h b/pjsip/src/pjsip/sip_auth.h index 1aad8cbbf..a527896fc 100644 --- a/pjsip/src/pjsip/sip_auth.h +++ b/pjsip/src/pjsip/sip_auth.h @@ -1,230 +1,230 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.h 11 8/31/05 9:05p Bennylp $ */ -#ifndef __PJSIP_AUTH_SIP_AUTH_H__ -#define __PJSIP_AUTH_SIP_AUTH_H__ - -/** - * @file pjsip_auth.h - * @brief SIP Authorization Module. - */ - -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_AUTH_API Authorization API's - * @ingroup PJSIP_AUTH - * @{ - */ - - /** Type of data in the credential information. */ -typedef enum pjsip_cred_data_type -{ - PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */ - PJSIP_CRED_DATA_DIGEST, /**< Hashed digest. */ -} pjsip_cred_data_type; - -/** Authentication's quality of protection (qop) type. */ -typedef enum pjsip_auth_qop_type -{ - PJSIP_AUTH_QOP_NONE, /**< No quality of protection. */ - PJSIP_AUTH_QOP_AUTH, /**< Authentication. */ - PJSIP_AUTH_QOP_AUTH_INT, /**< Authentication with integrity protection. */ - PJSIP_AUTH_QOP_UNKNOWN, /**< Unknown protection. */ -} pjsip_auth_qop_type; - - -/** - * This structure describes credential information. - * A credential information is a static, persistent information that identifies - * username and password required to authorize to a specific realm. - */ -struct pjsip_cred_info -{ - pj_str_t realm; /**< Realm. */ - pj_str_t scheme; /**< Scheme. */ - pj_str_t username; /**< User name. */ - int data_type; /**< Type of data. */ - pj_str_t data; /**< The data, which can be a plaintext - password or a hashed digest. */ -}; - -/** - * This structure describes cached value of previously sent Authorization - * or Proxy-Authorization header. The authentication framework keeps a list - * of this structure and will resend the same header to the same server - * as long as the method, uri, and nonce stays the same. - */ -typedef struct pjsip_cached_auth_hdr -{ - PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr) - - pjsip_method method; - pjsip_authorization_hdr *hdr; - -} pjsip_cached_auth_hdr; - - -/** - * This structure describes authentication information for the specified - * realm. Each instance of this structure describes authentication "session" - * between this endpoint and remote server. This "session" information is - * usefull to keep information that persists for more than one challenge, - * such as nonce-count and cnonce value. - * - * Other than that, this structure also keeps the last authorization headers - * that have been sent in the cache list. - */ -typedef struct pjsip_auth_session -{ - PJ_DECL_LIST_MEMBER(struct pjsip_auth_session) - - pj_str_t realm; - pj_bool_t is_proxy; - pjsip_auth_qop_type qop_value; -#if PJSIP_AUTH_QOP_SUPPORT - pj_uint32_t nc; - pj_str_t cnonce; -#endif -#if PJSIP_AUTH_AUTO_SEND_NEXT - pjsip_www_authenticate_hdr *last_chal; -#endif -#if PJSIP_AUTH_HEADER_CACHING - pjsip_cached_auth_hdr cached_hdr; -#endif - -} pjsip_auth_session; - - -/** - * Create authorization header for the specified credential. - * Application calls this function to create Authorization or Proxy-Authorization - * header after receiving WWW-Authenticate or Proxy-Authenticate challenge - * (normally in 401/407 response). - * If authorization session argument is specified, this function will update - * the session with the updated information if required (e.g. to update - * nonce-count when qop is "auth" or "auth-int"). This function will also - * save the authorization header in the session's cached header list. - * - * @param req_pool Pool to allocate new header for the request. - * @param hdr The WWW-Authenticate or Proxy-Authenticate found in - * the response. - * @param uri The URI for which authorization is targeted to. - * @param cred_info The credential to be used for authentication. - * @param method The method. - * @param sess_pool Session pool to update session or to allocate message - * in the cache. May be NULL if auth_sess is NULL. - * @param auth_sess If not NULL, this specifies the specific authentication - * session to be used or updated. - * - * @return The Authorization header, which can be typecasted to - * Proxy-Authorization. - */ -PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond( - pj_pool_t *req_pool, - const pjsip_www_authenticate_hdr *hdr, - const pjsip_uri *uri, - const pjsip_cred_info *cred_info, - const pjsip_method *method, - pj_pool_t *sess_pool, - pjsip_auth_session *auth_sess); - -/** - * Verify digest in the authorization request. - * - * @param hdr The incoming Authorization/Proxy-Authorization header. - * @param method The method. - * @param password The plaintext password to verify. - * - * @return Non-zero if authorization succeed. - */ -PJ_DECL(pj_bool_t) pjsip_auth_verify( const pjsip_authorization_hdr *hdr, - const pj_str_t *method, - const pjsip_cred_info *cred_info ); - - -/** - * This function can be used to find credential information which matches - * the specified realm. - * - * @param count Number of credentials in the parameter. - * @param cred The array of credentials. - * @param realm Realm to search. - * @param scheme Authentication scheme. - * - * @return The credential which matches the specified realm. - */ -PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count, - const pjsip_cred_info cred[], - const pj_str_t *realm, - const pj_str_t *scheme ); - - -/** - * Initialize new request message with authorization headers. - * This function will put Authorization/Proxy-Authorization headers to the - * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) - * and the session has previously sent Authorization/Proxy-Authorization header - * with the same method, then the same Authorization/Proxy-Authorization header - * will be resent from the cache only if qop is not present. If the stack is - * configured to automatically generate next Authorization/Proxy-Authorization - * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- - * Authorization headers are calculated and generated when they are not present - * in the case or if authorization session has qop. - * - * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag - * are not set, this function will do nothing. The stack then will only send - * Authorization/Proxy-Authorization to respond 401/407 response. - * - * @param sess_pool Session level pool, where memory will be allocated from - * for data that persists across requests (e.g. caching). - * @param tdata The request message to be initialized. - * @param sess_list List of authorization sessions that have been recorded. - * @param cred_count Number of credentials. - * @param cred_info Array of credentials. - * - * @return Zero if successfull. - */ -PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, - pjsip_tx_data *tdata, - pjsip_auth_session *sess_list, - int cred_count, - const pjsip_cred_info cred_info[]); - -/** - * Call this function when a transaction failed with 401 or 407 response. - * This function will reinitialize the original request message with the - * authentication challenge found in the response message, and add the - * new authorization header in the authorization cache. - * - * Note that upon return the reference counter of the transmit data - * will be incremented. - * - * @param endpt Endpoint. - * @param pool The pool to allocate memory for new cred_info. - * @param cached_list Cached authorization headers. - * @param cred_count Number of credentials. - * @param cred_info Array of credentials to use. - * @param tdata The original request message, which normally can be - * retrieved from tsx->last_tx. - * @param rdata The response message containing 401/407 status. - * - * @return New transmit data buffer, or NULL if the dialog - * can not respond to the authorization challenge. - */ -PJ_DECL(pjsip_tx_data*) -pjsip_auth_reinit_req( pjsip_endpoint *endpt, - pj_pool_t *ses_pool, - pjsip_auth_session *sess_list, - int cred_count, const pjsip_cred_info cred_info[], - pjsip_tx_data *tdata, const pjsip_rx_data *rdata); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_AUTH_SIP_AUTH_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_auth.h 11 8/31/05 9:05p Bennylp $ */ +#ifndef __PJSIP_AUTH_SIP_AUTH_H__ +#define __PJSIP_AUTH_SIP_AUTH_H__ + +/** + * @file pjsip_auth.h + * @brief SIP Authorization Module. + */ + +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_AUTH_API Authorization API's + * @ingroup PJSIP_AUTH + * @{ + */ + + /** Type of data in the credential information. */ +typedef enum pjsip_cred_data_type +{ + PJSIP_CRED_DATA_PLAIN_PASSWD, /**< Plain text password. */ + PJSIP_CRED_DATA_DIGEST, /**< Hashed digest. */ +} pjsip_cred_data_type; + +/** Authentication's quality of protection (qop) type. */ +typedef enum pjsip_auth_qop_type +{ + PJSIP_AUTH_QOP_NONE, /**< No quality of protection. */ + PJSIP_AUTH_QOP_AUTH, /**< Authentication. */ + PJSIP_AUTH_QOP_AUTH_INT, /**< Authentication with integrity protection. */ + PJSIP_AUTH_QOP_UNKNOWN, /**< Unknown protection. */ +} pjsip_auth_qop_type; + + +/** + * This structure describes credential information. + * A credential information is a static, persistent information that identifies + * username and password required to authorize to a specific realm. + */ +struct pjsip_cred_info +{ + pj_str_t realm; /**< Realm. */ + pj_str_t scheme; /**< Scheme. */ + pj_str_t username; /**< User name. */ + int data_type; /**< Type of data. */ + pj_str_t data; /**< The data, which can be a plaintext + password or a hashed digest. */ +}; + +/** + * This structure describes cached value of previously sent Authorization + * or Proxy-Authorization header. The authentication framework keeps a list + * of this structure and will resend the same header to the same server + * as long as the method, uri, and nonce stays the same. + */ +typedef struct pjsip_cached_auth_hdr +{ + PJ_DECL_LIST_MEMBER(struct pjsip_cached_auth_hdr) + + pjsip_method method; + pjsip_authorization_hdr *hdr; + +} pjsip_cached_auth_hdr; + + +/** + * This structure describes authentication information for the specified + * realm. Each instance of this structure describes authentication "session" + * between this endpoint and remote server. This "session" information is + * usefull to keep information that persists for more than one challenge, + * such as nonce-count and cnonce value. + * + * Other than that, this structure also keeps the last authorization headers + * that have been sent in the cache list. + */ +typedef struct pjsip_auth_session +{ + PJ_DECL_LIST_MEMBER(struct pjsip_auth_session) + + pj_str_t realm; + pj_bool_t is_proxy; + pjsip_auth_qop_type qop_value; +#if PJSIP_AUTH_QOP_SUPPORT + pj_uint32_t nc; + pj_str_t cnonce; +#endif +#if PJSIP_AUTH_AUTO_SEND_NEXT + pjsip_www_authenticate_hdr *last_chal; +#endif +#if PJSIP_AUTH_HEADER_CACHING + pjsip_cached_auth_hdr cached_hdr; +#endif + +} pjsip_auth_session; + + +/** + * Create authorization header for the specified credential. + * Application calls this function to create Authorization or Proxy-Authorization + * header after receiving WWW-Authenticate or Proxy-Authenticate challenge + * (normally in 401/407 response). + * If authorization session argument is specified, this function will update + * the session with the updated information if required (e.g. to update + * nonce-count when qop is "auth" or "auth-int"). This function will also + * save the authorization header in the session's cached header list. + * + * @param req_pool Pool to allocate new header for the request. + * @param hdr The WWW-Authenticate or Proxy-Authenticate found in + * the response. + * @param uri The URI for which authorization is targeted to. + * @param cred_info The credential to be used for authentication. + * @param method The method. + * @param sess_pool Session pool to update session or to allocate message + * in the cache. May be NULL if auth_sess is NULL. + * @param auth_sess If not NULL, this specifies the specific authentication + * session to be used or updated. + * + * @return The Authorization header, which can be typecasted to + * Proxy-Authorization. + */ +PJ_DECL(pjsip_authorization_hdr*) pjsip_auth_respond( + pj_pool_t *req_pool, + const pjsip_www_authenticate_hdr *hdr, + const pjsip_uri *uri, + const pjsip_cred_info *cred_info, + const pjsip_method *method, + pj_pool_t *sess_pool, + pjsip_auth_session *auth_sess); + +/** + * Verify digest in the authorization request. + * + * @param hdr The incoming Authorization/Proxy-Authorization header. + * @param method The method. + * @param password The plaintext password to verify. + * + * @return Non-zero if authorization succeed. + */ +PJ_DECL(pj_bool_t) pjsip_auth_verify( const pjsip_authorization_hdr *hdr, + const pj_str_t *method, + const pjsip_cred_info *cred_info ); + + +/** + * This function can be used to find credential information which matches + * the specified realm. + * + * @param count Number of credentials in the parameter. + * @param cred The array of credentials. + * @param realm Realm to search. + * @param scheme Authentication scheme. + * + * @return The credential which matches the specified realm. + */ +PJ_DECL(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count, + const pjsip_cred_info cred[], + const pj_str_t *realm, + const pj_str_t *scheme ); + + +/** + * Initialize new request message with authorization headers. + * This function will put Authorization/Proxy-Authorization headers to the + * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) + * and the session has previously sent Authorization/Proxy-Authorization header + * with the same method, then the same Authorization/Proxy-Authorization header + * will be resent from the cache only if qop is not present. If the stack is + * configured to automatically generate next Authorization/Proxy-Authorization + * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- + * Authorization headers are calculated and generated when they are not present + * in the case or if authorization session has qop. + * + * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag + * are not set, this function will do nothing. The stack then will only send + * Authorization/Proxy-Authorization to respond 401/407 response. + * + * @param sess_pool Session level pool, where memory will be allocated from + * for data that persists across requests (e.g. caching). + * @param tdata The request message to be initialized. + * @param sess_list List of authorization sessions that have been recorded. + * @param cred_count Number of credentials. + * @param cred_info Array of credentials. + * + * @return Zero if successfull. + */ +PJ_DECL(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, + pjsip_tx_data *tdata, + pjsip_auth_session *sess_list, + int cred_count, + const pjsip_cred_info cred_info[]); + +/** + * Call this function when a transaction failed with 401 or 407 response. + * This function will reinitialize the original request message with the + * authentication challenge found in the response message, and add the + * new authorization header in the authorization cache. + * + * Note that upon return the reference counter of the transmit data + * will be incremented. + * + * @param endpt Endpoint. + * @param pool The pool to allocate memory for new cred_info. + * @param cached_list Cached authorization headers. + * @param cred_count Number of credentials. + * @param cred_info Array of credentials to use. + * @param tdata The original request message, which normally can be + * retrieved from tsx->last_tx. + * @param rdata The response message containing 401/407 status. + * + * @return New transmit data buffer, or NULL if the dialog + * can not respond to the authorization challenge. + */ +PJ_DECL(pjsip_tx_data*) +pjsip_auth_reinit_req( pjsip_endpoint *endpt, + pj_pool_t *ses_pool, + pjsip_auth_session *sess_list, + int cred_count, const pjsip_cred_info cred_info[], + pjsip_tx_data *tdata, const pjsip_rx_data *rdata); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_AUTH_SIP_AUTH_H__ */ + diff --git a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c index a3efa92aa..6e8b87808 100644 --- a/pjsip/src/pjsip/sip_auth_msg.c +++ b/pjsip/src/pjsip/sip_auth_msg.c @@ -1,292 +1,292 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.c 9 8/31/05 9:05p Bennylp $ */ -#include -#include -#include -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// -/* - * Authorization and Proxy-Authorization header. - */ -static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *hdr); -static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *hdr); -static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, - char *buf, pj_size_t size); - -static pjsip_hdr_vptr authorization_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print, -}; - - -PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool) -{ - pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr); - return hdr; -} - -PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool) -{ - pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr); - return hdr; -} - -static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"'); - copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"'); - copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"'); - copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"'); - copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"'); - copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm); - copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"'); - copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"'); - //Note: there's no dbl-quote in qop in Authorization header - // (unlike WWW-Authenticate) - //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"'); - copy_advance_pair(buf, ", qop=", 6, cred->qop); - copy_advance_pair(buf, ", nc=", 5, cred->nc); - copy_advance(buf, cred->other_param); - - return (int) (buf-startbuf); -} - -static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size) -{ - PJ_UNUSED_ARG(cred) - PJ_UNUSED_ARG(buf) - PJ_UNUSED_ARG(size) - return -1; -} - -static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - copy_advance(buf, hdr->scheme); - *buf++ = ' '; - - if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) - { - printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf); - } - else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0) - { - printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf); - } - else { - pj_assert(0); - return -1; - } - - if (printed == -1) - return -1; - - buf += printed; - *buf = '\0'; - return (int)(buf-startbuf); -} - -static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *rhs) -{ - /* This function also serves Proxy-Authorization header. */ - pjsip_authorization_hdr *hdr; - if (rhs->type == PJSIP_H_AUTHORIZATION) - hdr = pjsip_authorization_hdr_create(pool); - else - hdr = pjsip_proxy_authorization_hdr_create(pool); - - pj_strdup(pool, &hdr->scheme, &rhs->scheme); - - if (pj_stricmp2(&hdr->scheme, "digest") == 0) { - pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username); - pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm); - pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce); - pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri); - pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response); - pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm); - pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce); - pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque); - pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop); - pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc); - pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param); - } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { - pj_assert(0); - return NULL; - } else { - pj_assert(0); - return NULL; - } - - return hdr; -} - -static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *rhs) -{ - /* This function also serves Proxy-Authorization header. */ - pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Proxy-Authenticate and WWW-Authenticate header. - */ -static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *hdr); -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *hdr); - -static pjsip_hdr_vptr www_authenticate_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print, -}; - - -PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool) -{ - pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr); - return hdr; -} - - -PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool) -{ - pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr); - return hdr; -} - -static int print_digest_challenge( pjsip_digest_challenge *chal, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"'); - copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"'); - copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"'); - copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"'); - if (chal->stale) { - pj_str_t true_str = { "true", 4 }; - copy_advance_pair(buf, ",stale=", 7, true_str); - } - copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm); - copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"'); - copy_advance(buf, chal->other_param); - - return (int)(buf-startbuf); -} - -static int print_pgp_challenge( pjsip_pgp_challenge *chal, - char *buf, pj_size_t size) -{ - PJ_UNUSED_ARG(chal) - PJ_UNUSED_ARG(buf) - PJ_UNUSED_ARG(size) - return -1; -} - -static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - copy_advance(buf, hdr->scheme); - *buf++ = ' '; - - if (pj_stricmp2(&hdr->scheme, "digest") == 0) - printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf); - else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) - printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf); - else { - pj_assert(0); - return -1; - } - - if (printed == -1) - return -1; - - buf += printed; - *buf = '\0'; - return (int)(buf-startbuf); -} - -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *rhs) -{ - /* This function also serves Proxy-Authenticate header. */ - pjsip_www_authenticate_hdr *hdr; - if (rhs->type == PJSIP_H_WWW_AUTHENTICATE) - hdr = pjsip_www_authenticate_hdr_create(pool); - else - hdr = pjsip_proxy_authenticate_hdr_create(pool); - - pj_strdup(pool, &hdr->scheme, &rhs->scheme); - - if (pj_stricmp2(&hdr->scheme, "digest") == 0) { - pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm); - pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain); - pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce); - pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque); - hdr->challenge.digest.stale = rhs->challenge.digest.stale; - pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm); - pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop); - pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param); - } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { - pj_assert(0); - return NULL; - } else { - pj_assert(0); - return NULL; - } - - return hdr; - -} - -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *rhs) -{ - /* This function also serves Proxy-Authenticate header. */ - pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - +/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.c 9 8/31/05 9:05p Bennylp $ */ +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +/* + * Authorization and Proxy-Authorization header. + */ +static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *hdr); +static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *hdr); +static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, + char *buf, pj_size_t size); + +static pjsip_hdr_vptr authorization_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print, +}; + + +PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool) +{ + pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr); + return hdr; +} + +PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool) +{ + pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr); + return hdr; +} + +static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"'); + copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"'); + copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"'); + copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"'); + copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"'); + copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm); + copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"'); + copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"'); + //Note: there's no dbl-quote in qop in Authorization header + // (unlike WWW-Authenticate) + //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"'); + copy_advance_pair(buf, ", qop=", 6, cred->qop); + copy_advance_pair(buf, ", nc=", 5, cred->nc); + copy_advance(buf, cred->other_param); + + return (int) (buf-startbuf); +} + +static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size) +{ + PJ_UNUSED_ARG(cred) + PJ_UNUSED_ARG(buf) + PJ_UNUSED_ARG(size) + return -1; +} + +static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + copy_advance(buf, hdr->scheme); + *buf++ = ' '; + + if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) + { + printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf); + } + else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0) + { + printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf); + } + else { + pj_assert(0); + return -1; + } + + if (printed == -1) + return -1; + + buf += printed; + *buf = '\0'; + return (int)(buf-startbuf); +} + +static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *rhs) +{ + /* This function also serves Proxy-Authorization header. */ + pjsip_authorization_hdr *hdr; + if (rhs->type == PJSIP_H_AUTHORIZATION) + hdr = pjsip_authorization_hdr_create(pool); + else + hdr = pjsip_proxy_authorization_hdr_create(pool); + + pj_strdup(pool, &hdr->scheme, &rhs->scheme); + + if (pj_stricmp2(&hdr->scheme, "digest") == 0) { + pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username); + pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm); + pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce); + pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri); + pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response); + pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm); + pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce); + pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque); + pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop); + pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc); + pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param); + } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { + pj_assert(0); + return NULL; + } else { + pj_assert(0); + return NULL; + } + + return hdr; +} + +static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_authorization_hdr *rhs) +{ + /* This function also serves Proxy-Authorization header. */ + pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Proxy-Authenticate and WWW-Authenticate header. + */ +static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *hdr); +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *hdr); + +static pjsip_hdr_vptr www_authenticate_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print, +}; + + +PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool) +{ + pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr); + return hdr; +} + + +PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool) +{ + pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr); + return hdr; +} + +static int print_digest_challenge( pjsip_digest_challenge *chal, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"'); + copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"'); + copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"'); + copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"'); + if (chal->stale) { + pj_str_t true_str = { "true", 4 }; + copy_advance_pair(buf, ",stale=", 7, true_str); + } + copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm); + copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"'); + copy_advance(buf, chal->other_param); + + return (int)(buf-startbuf); +} + +static int print_pgp_challenge( pjsip_pgp_challenge *chal, + char *buf, pj_size_t size) +{ + PJ_UNUSED_ARG(chal) + PJ_UNUSED_ARG(buf) + PJ_UNUSED_ARG(size) + return -1; +} + +static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + copy_advance(buf, hdr->scheme); + *buf++ = ' '; + + if (pj_stricmp2(&hdr->scheme, "digest") == 0) + printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf); + else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) + printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf); + else { + pj_assert(0); + return -1; + } + + if (printed == -1) + return -1; + + buf += printed; + *buf = '\0'; + return (int)(buf-startbuf); +} + +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *rhs) +{ + /* This function also serves Proxy-Authenticate header. */ + pjsip_www_authenticate_hdr *hdr; + if (rhs->type == PJSIP_H_WWW_AUTHENTICATE) + hdr = pjsip_www_authenticate_hdr_create(pool); + else + hdr = pjsip_proxy_authenticate_hdr_create(pool); + + pj_strdup(pool, &hdr->scheme, &rhs->scheme); + + if (pj_stricmp2(&hdr->scheme, "digest") == 0) { + pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm); + pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain); + pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce); + pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque); + hdr->challenge.digest.stale = rhs->challenge.digest.stale; + pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm); + pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop); + pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param); + } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { + pj_assert(0); + return NULL; + } else { + pj_assert(0); + return NULL; + } + + return hdr; + +} + +static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_www_authenticate_hdr *rhs) +{ + /* This function also serves Proxy-Authenticate header. */ + pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + diff --git a/pjsip/src/pjsip/sip_auth_msg.h b/pjsip/src/pjsip/sip_auth_msg.h index 77df41ddc..ab4c04af4 100644 --- a/pjsip/src/pjsip/sip_auth_msg.h +++ b/pjsip/src/pjsip/sip_auth_msg.h @@ -1,192 +1,192 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.h 7 6/19/05 6:12p Bennylp $ */ -#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__ -#define __PJSIP_AUTH_SIP_AUTH_MSG_H__ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization - * @brief Authorization and Proxy-Authorization header field. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * Common credential. - */ -struct pjsip_common_credential -{ - pj_str_t realm; -}; - -typedef struct pjsip_common_credential pjsip_common_credential; - - -/** - * This structure describe credential used in Authorization and - * Proxy-Authorization header for digest authentication scheme. - */ -struct pjsip_digest_credential -{ - pj_str_t realm; - pj_str_t username; - pj_str_t nonce; - pj_str_t uri; - pj_str_t response; - pj_str_t algorithm; - pj_str_t cnonce; - pj_str_t opaque; - pj_str_t qop; - pj_str_t nc; - pj_str_t other_param; -}; - -typedef struct pjsip_digest_credential pjsip_digest_credential; - -/** - * This structure describe credential used in Authorization and - * Proxy-Authorization header for PGP authentication scheme. - */ -struct pjsip_pgp_credential -{ - pj_str_t realm; - pj_str_t version; - pj_str_t signature; - pj_str_t signed_by; - pj_str_t nonce; -}; - -typedef struct pjsip_pgp_credential pjsip_pgp_credential; - -/** - * This structure describes SIP Authorization header (and also SIP - * Proxy-Authorization header). - */ -struct pjsip_authorization_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr) - pj_str_t scheme; - union - { - pjsip_common_credential common; - pjsip_digest_credential digest; - pjsip_pgp_credential pgp; - } credential; -}; - -typedef struct pjsip_authorization_hdr pjsip_authorization_hdr; - -/** SIP Proxy-Authorization header shares the same structure as SIP - Authorization header. - */ -typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr; - -/** - * Create SIP Authorization header. - * @param pool Pool where memory will be allocated from. - * @return SIP Authorization header. - */ -PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool); - -/** - * Create SIP Proxy-Authorization header. - * @param pool Pool where memory will be allocated from. - * @return SIP Proxy-Authorization header. - */ -PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - -/** - * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate - * @brief Proxy-Authenticate and WWW-Authenticate. - * @ingroup PJSIP_MSG - * @{ - */ - -struct pjsip_common_challenge -{ - pj_str_t realm; -}; - -typedef struct pjsip_common_challenge pjsip_common_challenge; - -/** - * This structure describes authentication challenge used in Proxy-Authenticate - * or WWW-Authenticate for digest authentication scheme. - */ -struct pjsip_digest_challenge -{ - pj_str_t realm; - pj_str_t domain; - pj_str_t nonce; - pj_str_t opaque; - int stale; - pj_str_t algorithm; - pj_str_t qop; - pj_str_t other_param; -}; - -typedef struct pjsip_digest_challenge pjsip_digest_challenge; - -/** - * This structure describes authentication challenge used in Proxy-Authenticate - * or WWW-Authenticate for PGP authentication scheme. - */ -struct pjsip_pgp_challenge -{ - pj_str_t realm; - pj_str_t version; - pj_str_t micalgorithm; - pj_str_t pubalgorithm; - pj_str_t nonce; -}; - -typedef struct pjsip_pgp_challenge pjsip_pgp_challenge; - -/** - * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate - * header also uses the same structure). - */ -struct pjsip_www_authenticate_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr) - pj_str_t scheme; - union - { - pjsip_common_challenge common; - pjsip_digest_challenge digest; - pjsip_pgp_challenge pgp; - } challenge; -}; - -typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr; -typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr; - - -/** - * Create SIP WWW-Authenticate header. - * @param pool Pool where memory will be allocated from. - * @return SIP WWW-Authenticate header. - */ -PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool); - -/** - * Create SIP Proxy-Authenticate header. - * @param pool Pool where memory will be allocated from. - * @return SIP Proxy-Authenticate header. - */ -PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_msg.h 7 6/19/05 6:12p Bennylp $ */ +#ifndef __PJSIP_AUTH_SIP_AUTH_MSG_H__ +#define __PJSIP_AUTH_SIP_AUTH_MSG_H__ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MSG_AUTHORIZATION Header Field: Authorization and Proxy-Authorization + * @brief Authorization and Proxy-Authorization header field. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Common credential. + */ +struct pjsip_common_credential +{ + pj_str_t realm; +}; + +typedef struct pjsip_common_credential pjsip_common_credential; + + +/** + * This structure describe credential used in Authorization and + * Proxy-Authorization header for digest authentication scheme. + */ +struct pjsip_digest_credential +{ + pj_str_t realm; + pj_str_t username; + pj_str_t nonce; + pj_str_t uri; + pj_str_t response; + pj_str_t algorithm; + pj_str_t cnonce; + pj_str_t opaque; + pj_str_t qop; + pj_str_t nc; + pj_str_t other_param; +}; + +typedef struct pjsip_digest_credential pjsip_digest_credential; + +/** + * This structure describe credential used in Authorization and + * Proxy-Authorization header for PGP authentication scheme. + */ +struct pjsip_pgp_credential +{ + pj_str_t realm; + pj_str_t version; + pj_str_t signature; + pj_str_t signed_by; + pj_str_t nonce; +}; + +typedef struct pjsip_pgp_credential pjsip_pgp_credential; + +/** + * This structure describes SIP Authorization header (and also SIP + * Proxy-Authorization header). + */ +struct pjsip_authorization_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_authorization_hdr) + pj_str_t scheme; + union + { + pjsip_common_credential common; + pjsip_digest_credential digest; + pjsip_pgp_credential pgp; + } credential; +}; + +typedef struct pjsip_authorization_hdr pjsip_authorization_hdr; + +/** SIP Proxy-Authorization header shares the same structure as SIP + Authorization header. + */ +typedef struct pjsip_authorization_hdr pjsip_proxy_authorization_hdr; + +/** + * Create SIP Authorization header. + * @param pool Pool where memory will be allocated from. + * @return SIP Authorization header. + */ +PJ_DECL(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool); + +/** + * Create SIP Proxy-Authorization header. + * @param pool Pool where memory will be allocated from. + * @return SIP Proxy-Authorization header. + */ +PJ_DECL(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/** + * @defgroup PJSIP_WWW_AUTH Header Field: Proxy-Authenticate and WWW-Authenticate + * @brief Proxy-Authenticate and WWW-Authenticate. + * @ingroup PJSIP_MSG + * @{ + */ + +struct pjsip_common_challenge +{ + pj_str_t realm; +}; + +typedef struct pjsip_common_challenge pjsip_common_challenge; + +/** + * This structure describes authentication challenge used in Proxy-Authenticate + * or WWW-Authenticate for digest authentication scheme. + */ +struct pjsip_digest_challenge +{ + pj_str_t realm; + pj_str_t domain; + pj_str_t nonce; + pj_str_t opaque; + int stale; + pj_str_t algorithm; + pj_str_t qop; + pj_str_t other_param; +}; + +typedef struct pjsip_digest_challenge pjsip_digest_challenge; + +/** + * This structure describes authentication challenge used in Proxy-Authenticate + * or WWW-Authenticate for PGP authentication scheme. + */ +struct pjsip_pgp_challenge +{ + pj_str_t realm; + pj_str_t version; + pj_str_t micalgorithm; + pj_str_t pubalgorithm; + pj_str_t nonce; +}; + +typedef struct pjsip_pgp_challenge pjsip_pgp_challenge; + +/** + * This structure describe SIP WWW-Authenticate header (Proxy-Authenticate + * header also uses the same structure). + */ +struct pjsip_www_authenticate_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_www_authenticate_hdr) + pj_str_t scheme; + union + { + pjsip_common_challenge common; + pjsip_digest_challenge digest; + pjsip_pgp_challenge pgp; + } challenge; +}; + +typedef struct pjsip_www_authenticate_hdr pjsip_www_authenticate_hdr; +typedef struct pjsip_www_authenticate_hdr pjsip_proxy_authenticate_hdr; + + +/** + * Create SIP WWW-Authenticate header. + * @param pool Pool where memory will be allocated from. + * @return SIP WWW-Authenticate header. + */ +PJ_DECL(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool); + +/** + * Create SIP Proxy-Authenticate header. + * @param pool Pool where memory will be allocated from. + * @return SIP Proxy-Authenticate header. + */ +PJ_DECL(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_AUTH_SIP_AUTH_MSG_H__ */ diff --git a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c index c67f79140..7043e8a23 100644 --- a/pjsip/src/pjsip/sip_auth_parser.c +++ b/pjsip/src/pjsip/sip_auth_parser.c @@ -1,246 +1,246 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.c 7 8/31/05 9:05p Bennylp $ */ -#include -#include -#include -#include -#include - -static pjsip_authorization_hdr* parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool); -static pjsip_proxy_authorization_hdr* parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool); -static pjsip_www_authenticate_hdr* parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool); -static pjsip_proxy_authenticate_hdr* parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool); - -static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred); -static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred); -static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal); -static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal); - -const pj_str_t pjsip_USERNAME_STR = { "username", 8 }, - pjsip_REALM_STR = { "realm", 5}, - pjsip_NONCE_STR = { "nonce", 5}, - pjsip_URI_STR = { "uri", 3 }, - pjsip_RESPONSE_STR = { "response", 8 }, - pjsip_ALGORITHM_STR = { "algorithm", 9 }, - pjsip_DOMAIN_STR = { "domain", 6 }, - pjsip_STALE_STR = { "stale", 5}, - pjsip_QOP_STR = { "qop", 3}, - pjsip_CNONCE_STR = { "cnonce", 6}, - pjsip_OPAQUE_STR = { "opaque", 6}, - pjsip_NC_STR = { "nc", 2}, - pjsip_TRUE_STR = { "true", 4}, - pjsip_QUOTED_TRUE_STR = { "\"true\"", 6}, - pjsip_FALSE_STR = { "false", 5}, - pjsip_QUOTED_FALSE_STR = { "\"false\"", 7}, - pjsip_DIGEST_STR = { "Digest", 6}, - pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8}, - pjsip_PGP_STR = { "PGP", 3 }, - pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 }, - pjsip_MD5_STR = { "md5", 3 }, - pjsip_QUOTED_MD5_STR = { "\"md5\"", 5}, - pjsip_AUTH_STR = { "auth", 4}, - pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 }; - - -static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred) -{ - for (;;) { - pj_str_t name, value; - - pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE); - - if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) { - cred->username = value; - - } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) { - cred->realm = value; - - } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { - cred->nonce = value; - - } else if (!pj_stricmp(&name, &pjsip_URI_STR)) { - cred->uri = value; - - } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) { - cred->response = value; - - } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { - cred->algorithm = value; - - } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) { - cred->cnonce = value; - - } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { - cred->opaque = value; - - } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { - cred->qop = value; - - } else if (!pj_stricmp(&name, &pjsip_NC_STR)) { - cred->nc = value; - - } else { - pjsip_concat_param_imp(&cred->other_param, pool, &name, &value, ','); - } - - /* Eat comma */ - if (!pj_scan_is_eof(scanner) && *scanner->current == ',') - pj_scan_get_char(scanner); - else - break; - } -} - -static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred) -{ - PJ_UNUSED_ARG(scanner) - PJ_UNUSED_ARG(pool) - PJ_UNUSED_ARG(cred) - - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); -} - -static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal) -{ - for (;;) { - pj_str_t name, value; - - pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE); - - if (!pj_stricmp(&name, &pjsip_REALM_STR)) { - chal->realm = value; - - } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) { - chal->domain = value; - - } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { - chal->nonce = value; - - } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { - chal->opaque = value; - - } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) { - if (!pj_stricmp(&value, &pjsip_TRUE_STR) || !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR)) - chal->stale = 1; - - } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { - chal->algorithm = value; - - - } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { - chal->qop = value; - - } else { - pjsip_concat_param_imp(&chal->other_param, pool, &name, &value, ','); - } - - /* Eat comma */ - if (!pj_scan_is_eof(scanner) && *scanner->current == ',') - pj_scan_get_char(scanner); - else - break; - } -} - -static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal) -{ - PJ_UNUSED_ARG(scanner) - PJ_UNUSED_ARG(pool) - PJ_UNUSED_ARG(chal) - - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); -} - -static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool, - pjsip_authorization_hdr *hdr) -{ - if (*scanner->current == '"') { - pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); - hdr->scheme.ptr++; - hdr->scheme.slen -= 2; - } else { - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme); - } - - if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { - - parse_digest_credential(scanner, pool, &hdr->credential.digest); - - } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { - - parse_pgp_credential( scanner, pool, &hdr->credential.pgp); - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - pjsip_parse_end_hdr_imp( scanner ); -} - -static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool, - pjsip_www_authenticate_hdr *hdr) -{ - if (*scanner->current == '"') { - pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); - hdr->scheme.ptr++; - hdr->scheme.slen -= 2; - } else { - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme); - } - - if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { - - parse_digest_challenge(scanner, pool, &hdr->challenge.digest); - - } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { - - parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp); - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - pjsip_parse_end_hdr_imp( scanner ); -} - - -static pjsip_authorization_hdr *parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool) -{ - pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(pool); - int_parse_hdr_authorization(scanner, pool, hdr); - return hdr; -} - -static pjsip_proxy_authorization_hdr *parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool) -{ - pjsip_proxy_authorization_hdr *hdr = pjsip_proxy_authorization_hdr_create(pool); - int_parse_hdr_authorization(scanner, pool, hdr); - return hdr; -} - -static pjsip_www_authenticate_hdr *parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool) -{ - pjsip_www_authenticate_hdr *hdr = pjsip_www_authenticate_hdr_create(pool); - int_parse_hdr_authenticate(scanner, pool, hdr); - return hdr; -} - -static pjsip_proxy_authenticate_hdr *parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool) -{ - pjsip_proxy_authenticate_hdr *hdr = pjsip_proxy_authenticate_hdr_create(pool); - int_parse_hdr_authenticate(scanner, pool, hdr); - return hdr; -} - - -PJ_DEF(void) pjsip_auth_init_parser() -{ - pjsip_register_hdr_parser( "Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_authorization); - pjsip_register_hdr_parser( "Proxy-Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authorization); - pjsip_register_hdr_parser( "WWW-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_www_authenticate); - pjsip_register_hdr_parser( "Proxy-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authenticate); -} - -PJ_DEF(void) pjsip_auth_deinit_parser() -{ -} - +/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.c 7 8/31/05 9:05p Bennylp $ */ +#include +#include +#include +#include +#include + +static pjsip_authorization_hdr* parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool); +static pjsip_proxy_authorization_hdr* parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool); +static pjsip_www_authenticate_hdr* parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool); +static pjsip_proxy_authenticate_hdr* parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool); + +static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred); +static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred); +static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal); +static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal); + +const pj_str_t pjsip_USERNAME_STR = { "username", 8 }, + pjsip_REALM_STR = { "realm", 5}, + pjsip_NONCE_STR = { "nonce", 5}, + pjsip_URI_STR = { "uri", 3 }, + pjsip_RESPONSE_STR = { "response", 8 }, + pjsip_ALGORITHM_STR = { "algorithm", 9 }, + pjsip_DOMAIN_STR = { "domain", 6 }, + pjsip_STALE_STR = { "stale", 5}, + pjsip_QOP_STR = { "qop", 3}, + pjsip_CNONCE_STR = { "cnonce", 6}, + pjsip_OPAQUE_STR = { "opaque", 6}, + pjsip_NC_STR = { "nc", 2}, + pjsip_TRUE_STR = { "true", 4}, + pjsip_QUOTED_TRUE_STR = { "\"true\"", 6}, + pjsip_FALSE_STR = { "false", 5}, + pjsip_QUOTED_FALSE_STR = { "\"false\"", 7}, + pjsip_DIGEST_STR = { "Digest", 6}, + pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8}, + pjsip_PGP_STR = { "PGP", 3 }, + pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 }, + pjsip_MD5_STR = { "md5", 3 }, + pjsip_QUOTED_MD5_STR = { "\"md5\"", 5}, + pjsip_AUTH_STR = { "auth", 4}, + pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 }; + + +static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_credential *cred) +{ + for (;;) { + pj_str_t name, value; + + pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE); + + if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) { + cred->username = value; + + } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) { + cred->realm = value; + + } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { + cred->nonce = value; + + } else if (!pj_stricmp(&name, &pjsip_URI_STR)) { + cred->uri = value; + + } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) { + cred->response = value; + + } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { + cred->algorithm = value; + + } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) { + cred->cnonce = value; + + } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { + cred->opaque = value; + + } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { + cred->qop = value; + + } else if (!pj_stricmp(&name, &pjsip_NC_STR)) { + cred->nc = value; + + } else { + pjsip_concat_param_imp(&cred->other_param, pool, &name, &value, ','); + } + + /* Eat comma */ + if (!pj_scan_is_eof(scanner) && *scanner->current == ',') + pj_scan_get_char(scanner); + else + break; + } +} + +static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_credential *cred) +{ + PJ_UNUSED_ARG(scanner) + PJ_UNUSED_ARG(pool) + PJ_UNUSED_ARG(cred) + + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); +} + +static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_digest_challenge *chal) +{ + for (;;) { + pj_str_t name, value; + + pjsip_parse_param_imp(scanner, &name, &value, PJSIP_PARSE_REMOVE_QUOTE); + + if (!pj_stricmp(&name, &pjsip_REALM_STR)) { + chal->realm = value; + + } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) { + chal->domain = value; + + } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { + chal->nonce = value; + + } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { + chal->opaque = value; + + } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) { + if (!pj_stricmp(&value, &pjsip_TRUE_STR) || !pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR)) + chal->stale = 1; + + } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { + chal->algorithm = value; + + + } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { + chal->qop = value; + + } else { + pjsip_concat_param_imp(&chal->other_param, pool, &name, &value, ','); + } + + /* Eat comma */ + if (!pj_scan_is_eof(scanner) && *scanner->current == ',') + pj_scan_get_char(scanner); + else + break; + } +} + +static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool, pjsip_pgp_challenge *chal) +{ + PJ_UNUSED_ARG(scanner) + PJ_UNUSED_ARG(pool) + PJ_UNUSED_ARG(chal) + + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); +} + +static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool, + pjsip_authorization_hdr *hdr) +{ + if (*scanner->current == '"') { + pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); + hdr->scheme.ptr++; + hdr->scheme.slen -= 2; + } else { + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme); + } + + if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { + + parse_digest_credential(scanner, pool, &hdr->credential.digest); + + } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { + + parse_pgp_credential( scanner, pool, &hdr->credential.pgp); + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + pjsip_parse_end_hdr_imp( scanner ); +} + +static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool, + pjsip_www_authenticate_hdr *hdr) +{ + if (*scanner->current == '"') { + pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); + hdr->scheme.ptr++; + hdr->scheme.slen -= 2; + } else { + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->scheme); + } + + if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { + + parse_digest_challenge(scanner, pool, &hdr->challenge.digest); + + } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { + + parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp); + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + pjsip_parse_end_hdr_imp( scanner ); +} + + +static pjsip_authorization_hdr *parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool) +{ + pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(pool); + int_parse_hdr_authorization(scanner, pool, hdr); + return hdr; +} + +static pjsip_proxy_authorization_hdr *parse_hdr_proxy_authorization( pj_scanner *scanner, pj_pool_t *pool) +{ + pjsip_proxy_authorization_hdr *hdr = pjsip_proxy_authorization_hdr_create(pool); + int_parse_hdr_authorization(scanner, pool, hdr); + return hdr; +} + +static pjsip_www_authenticate_hdr *parse_hdr_www_authenticate( pj_scanner *scanner, pj_pool_t *pool) +{ + pjsip_www_authenticate_hdr *hdr = pjsip_www_authenticate_hdr_create(pool); + int_parse_hdr_authenticate(scanner, pool, hdr); + return hdr; +} + +static pjsip_proxy_authenticate_hdr *parse_hdr_proxy_authenticate( pj_scanner *scanner, pj_pool_t *pool) +{ + pjsip_proxy_authenticate_hdr *hdr = pjsip_proxy_authenticate_hdr_create(pool); + int_parse_hdr_authenticate(scanner, pool, hdr); + return hdr; +} + + +PJ_DEF(void) pjsip_auth_init_parser() +{ + pjsip_register_hdr_parser( "Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_authorization); + pjsip_register_hdr_parser( "Proxy-Authorization", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authorization); + pjsip_register_hdr_parser( "WWW-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_www_authenticate); + pjsip_register_hdr_parser( "Proxy-Authenticate", NULL, (pjsip_parse_hdr_func*) &parse_hdr_proxy_authenticate); +} + +PJ_DEF(void) pjsip_auth_deinit_parser() +{ +} + diff --git a/pjsip/src/pjsip/sip_auth_parser.h b/pjsip/src/pjsip/sip_auth_parser.h index bd8b77e1b..8e6d20a0f 100644 --- a/pjsip/src/pjsip/sip_auth_parser.h +++ b/pjsip/src/pjsip/sip_auth_parser.h @@ -1,68 +1,68 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.h 7 8/31/05 9:05p Bennylp $ */ -#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__ -#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__ - -/** - * @file pjsip_auth_parser.h - * @brief SIP Authorization Parser Module. - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module - * @ingroup PJSIP_AUTH - * @{ - */ - -/** - * Initialize and register authorization parser module. - * This will register parser handler for various Authorization related headers - * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and - * Proxy-Authenticate headers. - */ -PJ_DECL(void) pjsip_auth_init_parser(); - -/** - * DeInitialize authorization parser module. - */ -PJ_DECL(void) pjsip_auth_deinit_parser(); - - -extern const pj_str_t pjsip_USERNAME_STR, - pjsip_REALM_STR, - pjsip_NONCE_STR, - pjsip_URI_STR, - pjsip_RESPONSE_STR, - pjsip_ALGORITHM_STR, - pjsip_DOMAIN_STR, - pjsip_STALE_STR, - pjsip_QOP_STR, - pjsip_CNONCE_STR, - pjsip_OPAQUE_STR, - pjsip_NC_STR, - pjsip_TRUE_STR, - pjsip_FALSE_STR, - pjsip_DIGEST_STR, - pjsip_PGP_STR, - pjsip_MD5_STR, - pjsip_AUTH_STR; -/* -extern const pj_str_t pjsip_QUOTED_TRUE_STR, - pjsip_QUOTED_FALSE_STR, - pjsip_QUOTED_DIGEST_STR, - pjsip_QUOTED_PGP_STR, - pjsip_QUOTED_MD5_STR, - pjsip_QUOTED_AUTH_STR; -*/ - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_auth_parser.h 7 8/31/05 9:05p Bennylp $ */ +#ifndef __PJSIP_AUTH_SIP_AUTH_PARSER_H__ +#define __PJSIP_AUTH_SIP_AUTH_PARSER_H__ + +/** + * @file pjsip_auth_parser.h + * @brief SIP Authorization Parser Module. + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_AUTH_PARSER_MODULE Authorization Parser Module + * @ingroup PJSIP_AUTH + * @{ + */ + +/** + * Initialize and register authorization parser module. + * This will register parser handler for various Authorization related headers + * such as Authorization, WWW-Authenticate, Proxy-Authorizization, and + * Proxy-Authenticate headers. + */ +PJ_DECL(void) pjsip_auth_init_parser(); + +/** + * DeInitialize authorization parser module. + */ +PJ_DECL(void) pjsip_auth_deinit_parser(); + + +extern const pj_str_t pjsip_USERNAME_STR, + pjsip_REALM_STR, + pjsip_NONCE_STR, + pjsip_URI_STR, + pjsip_RESPONSE_STR, + pjsip_ALGORITHM_STR, + pjsip_DOMAIN_STR, + pjsip_STALE_STR, + pjsip_QOP_STR, + pjsip_CNONCE_STR, + pjsip_OPAQUE_STR, + pjsip_NC_STR, + pjsip_TRUE_STR, + pjsip_FALSE_STR, + pjsip_DIGEST_STR, + pjsip_PGP_STR, + pjsip_MD5_STR, + pjsip_AUTH_STR; +/* +extern const pj_str_t pjsip_QUOTED_TRUE_STR, + pjsip_QUOTED_FALSE_STR, + pjsip_QUOTED_DIGEST_STR, + pjsip_QUOTED_PGP_STR, + pjsip_QUOTED_MD5_STR, + pjsip_QUOTED_AUTH_STR; +*/ + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_AUTH_SIP_AUTH_PARSER_H__ */ + diff --git a/pjsip/src/pjsip/sip_config.h b/pjsip/src/pjsip/sip_config.h index 6793c7ec6..010a4a3ab 100644 --- a/pjsip/src/pjsip/sip_config.h +++ b/pjsip/src/pjsip/sip_config.h @@ -1,138 +1,138 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_config.h 10 10/14/05 12:23a Bennylp $ */ -#ifndef __PJSIP_SIP_CONFIG_H__ -#define __PJSIP_SIP_CONFIG_H__ - -#include - -/* Endpoint. */ -#define PJSIP_MAX_TIMER_COUNT (2*PJSIP_MAX_TSX_COUNT + 2*PJSIP_MAX_DIALOG_COUNT) -#define PJSIP_POOL_LEN_ENDPT (2048+64*PJSIP_MAX_TSX_COUNT) -#define PJSIP_POOL_INC_ENDPT (1024) - -/* Transport related constants. */ -#define PJSIP_MAX_TRANSPORTS (PJ_IOQUEUE_MAX_HANDLES) -#define PJSIP_MAX_PKT_LEN 1500 -#define PJSIP_POOL_LEN_RDATA 2500 -#define PJSIP_POOL_INC_RDATA 512 -#define PJSIP_POOL_LEN_TRANSPORT 512 -#define PJSIP_POOL_INC_TRANSPORT 512 -#define PJSIP_POOL_LEN_TDATA 2500 -#define PJSIP_POOL_INC_TDATA 512 -#define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT) -#define PJSIP_POOL_INC_UA 0 -#define PJSIP_TRANSPORT_CLOSE_TIMEOUT 30 -#define PJSIP_MAX_TRANSPORT_USAGE 16 - -#define PJSIP_MAX_FORWARDS_VALUE 70 - -#define PJSIP_RFC3261_BRANCH_ID "z9hG4bK" -#define PJSIP_RFC3261_BRANCH_LEN 7 - -/* Message/URL related constants. */ -#define PJSIP_MAX_CALL_ID_LEN PJ_GUID_STRING_LENGTH -#define PJSIP_MAX_TAG_LEN PJ_GUID_STRING_LENGTH -#define PJSIP_MAX_BRANCH_LEN (PJSIP_RFC3261_BRANCH_LEN + PJ_GUID_STRING_LENGTH) -#define PJSIP_MAX_URL_SIZE 256 -#define PJSIP_MAX_HNAME_LEN 64 - -/* Transction related constants. */ -#define PJSIP_MAX_TSX_COUNT (16*1024) -#define PJSIP_POOL_LEN_TSX 1536 //768 -#define PJSIP_POOL_INC_TSX 256 -#define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2) - -/* Dialog related constants. */ -#define PJSIP_MAX_DIALOG_COUNT (16*1024) -#define PJSIP_POOL_LEN_DIALOG 1200 -#define PJSIP_POOL_INC_DIALOG 512 - -/* Module related constants. */ -#define PJSIP_MAX_MODULE 8 - -/***************************************************************************** - * Default timeout settings, in miliseconds. - */ - -//#define PJSIP_T1_TIMEOUT 15000 -//#define PJSIP_T2_TIMEOUT 60000 - -/* T1 timeout value. */ -#if !defined(PJSIP_T1_TIMEOUT) -# define PJSIP_T1_TIMEOUT 500 -#endif - -/* T2 timeout value. */ -#if !defined(PJSIP_T2_TIMEOUT) -# define PJSIP_T2_TIMEOUT 4000 -#endif - -/* Completed timer for non-INVITE */ -#if !defined(PJSIP_T4_TIMEOUT) -# define PJSIP_T4_TIMEOUT 5000 -#endif - -/* Completed timer for INVITE */ -#if !defined(PJSIP_TD_TIMEOUT) -# define PJSIP_TD_TIMEOUT 32000 -#endif - - -/***************************************************************************** - * Authorization - */ - -/* - * If this flag is set, the stack will keep the Authorization/Proxy-Authorization - * headers that are sent in a cache. Future requests with the same realm and - * the same method will use the headers in the cache (as long as no qop is - * required by server). - * - * Turning on this flag will make authorization process goes faster, but - * will grow the memory usage undefinitely until the dialog/registration - * session is terminated. - * - * Default: 1 - */ -#if !defined(PJSIP_AUTH_HEADER_CACHING) -# define PJSIP_AUTH_HEADER_CACHING 1 -#endif - -/* - * If this flag is set, the stack will proactively send Authorization/Proxy- - * Authorization header for next requests. If next request has the same method - * with any of previous requests, then the last header which is saved in - * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh - * header will be recalculated. If a particular server has requested qop, then - * a fresh header will always be calculated. - * - * If this flag is NOT set, then the stack will only send Authorization/Proxy- - * Authorization headers when it receives 401/407 response from server. - * - * Turning ON this flag will grow memory usage of a dialog/registration pool - * indefinitely until it is terminated, because the stack needs to keep the - * last WWW-Authenticate/Proxy-Authenticate challenge. - * - * Default: 1 - */ -#if !defined(PJSIP_AUTH_AUTO_SEND_NEXT) -# define PJSIP_AUTH_AUTO_SEND_NEXT 1 -#endif - -/* - * Support qop="auth" directive. - * This option also requires client to cache the last challenge offered by - * server. - * - * Default: 1 - */ -#if !defined(PJSIP_AUTH_QOP_SUPPORT) -# define PJSIP_AUTH_QOP_SUPPORT 1 -#endif - - -#include -#include - - -#endif /* __PJSIP_SIP_CONFIG_H__ */ - +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_config.h 10 10/14/05 12:23a Bennylp $ */ +#ifndef __PJSIP_SIP_CONFIG_H__ +#define __PJSIP_SIP_CONFIG_H__ + +#include + +/* Endpoint. */ +#define PJSIP_MAX_TIMER_COUNT (2*PJSIP_MAX_TSX_COUNT + 2*PJSIP_MAX_DIALOG_COUNT) +#define PJSIP_POOL_LEN_ENDPT (2048+64*PJSIP_MAX_TSX_COUNT) +#define PJSIP_POOL_INC_ENDPT (1024) + +/* Transport related constants. */ +#define PJSIP_MAX_TRANSPORTS (PJ_IOQUEUE_MAX_HANDLES) +#define PJSIP_MAX_PKT_LEN 1500 +#define PJSIP_POOL_LEN_RDATA 2500 +#define PJSIP_POOL_INC_RDATA 512 +#define PJSIP_POOL_LEN_TRANSPORT 512 +#define PJSIP_POOL_INC_TRANSPORT 512 +#define PJSIP_POOL_LEN_TDATA 2500 +#define PJSIP_POOL_INC_TDATA 512 +#define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT) +#define PJSIP_POOL_INC_UA 0 +#define PJSIP_TRANSPORT_CLOSE_TIMEOUT 30 +#define PJSIP_MAX_TRANSPORT_USAGE 16 + +#define PJSIP_MAX_FORWARDS_VALUE 70 + +#define PJSIP_RFC3261_BRANCH_ID "z9hG4bK" +#define PJSIP_RFC3261_BRANCH_LEN 7 + +/* Message/URL related constants. */ +#define PJSIP_MAX_CALL_ID_LEN PJ_GUID_STRING_LENGTH +#define PJSIP_MAX_TAG_LEN PJ_GUID_STRING_LENGTH +#define PJSIP_MAX_BRANCH_LEN (PJSIP_RFC3261_BRANCH_LEN + PJ_GUID_STRING_LENGTH) +#define PJSIP_MAX_URL_SIZE 256 +#define PJSIP_MAX_HNAME_LEN 64 + +/* Transction related constants. */ +#define PJSIP_MAX_TSX_COUNT (16*1024) +#define PJSIP_POOL_LEN_TSX 1536 //768 +#define PJSIP_POOL_INC_TSX 256 +#define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2) + +/* Dialog related constants. */ +#define PJSIP_MAX_DIALOG_COUNT (16*1024) +#define PJSIP_POOL_LEN_DIALOG 1200 +#define PJSIP_POOL_INC_DIALOG 512 + +/* Module related constants. */ +#define PJSIP_MAX_MODULE 8 + +/***************************************************************************** + * Default timeout settings, in miliseconds. + */ + +//#define PJSIP_T1_TIMEOUT 15000 +//#define PJSIP_T2_TIMEOUT 60000 + +/* T1 timeout value. */ +#if !defined(PJSIP_T1_TIMEOUT) +# define PJSIP_T1_TIMEOUT 500 +#endif + +/* T2 timeout value. */ +#if !defined(PJSIP_T2_TIMEOUT) +# define PJSIP_T2_TIMEOUT 4000 +#endif + +/* Completed timer for non-INVITE */ +#if !defined(PJSIP_T4_TIMEOUT) +# define PJSIP_T4_TIMEOUT 5000 +#endif + +/* Completed timer for INVITE */ +#if !defined(PJSIP_TD_TIMEOUT) +# define PJSIP_TD_TIMEOUT 32000 +#endif + + +/***************************************************************************** + * Authorization + */ + +/* + * If this flag is set, the stack will keep the Authorization/Proxy-Authorization + * headers that are sent in a cache. Future requests with the same realm and + * the same method will use the headers in the cache (as long as no qop is + * required by server). + * + * Turning on this flag will make authorization process goes faster, but + * will grow the memory usage undefinitely until the dialog/registration + * session is terminated. + * + * Default: 1 + */ +#if !defined(PJSIP_AUTH_HEADER_CACHING) +# define PJSIP_AUTH_HEADER_CACHING 1 +#endif + +/* + * If this flag is set, the stack will proactively send Authorization/Proxy- + * Authorization header for next requests. If next request has the same method + * with any of previous requests, then the last header which is saved in + * the cache will be used (if PJSIP_AUTH_CACHING is set). Otherwise a fresh + * header will be recalculated. If a particular server has requested qop, then + * a fresh header will always be calculated. + * + * If this flag is NOT set, then the stack will only send Authorization/Proxy- + * Authorization headers when it receives 401/407 response from server. + * + * Turning ON this flag will grow memory usage of a dialog/registration pool + * indefinitely until it is terminated, because the stack needs to keep the + * last WWW-Authenticate/Proxy-Authenticate challenge. + * + * Default: 1 + */ +#if !defined(PJSIP_AUTH_AUTO_SEND_NEXT) +# define PJSIP_AUTH_AUTO_SEND_NEXT 1 +#endif + +/* + * Support qop="auth" directive. + * This option also requires client to cache the last challenge offered by + * server. + * + * Default: 1 + */ +#if !defined(PJSIP_AUTH_QOP_SUPPORT) +# define PJSIP_AUTH_QOP_SUPPORT 1 +#endif + + +#include +#include + + +#endif /* __PJSIP_SIP_CONFIG_H__ */ + diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index 7c744f42e..3727cc869 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -1,1030 +1,1030 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_endpoint.c 26 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION -#define LOG_THIS "endpoint..." - -#define MAX_METHODS 32 - -/** - * The SIP endpoint. - */ -struct pjsip_endpoint -{ - /** Pool to allocate memory for the endpoint. */ - pj_pool_t *pool; - - /** Mutex for the pool, hash table, and event list/queue. */ - pj_mutex_t *mutex; - - /** Pool factory. */ - pj_pool_factory *pf; - - /** Transaction table. */ - pj_hash_table_t *tsx_table; - - /** Mutex for transaction table. */ - pj_mutex_t *tsx_table_mutex; - - /** Timer heap. */ - pj_timer_heap_t *timer_heap; - - /** Transport manager. */ - pjsip_transport_mgr *transport_mgr; - - /** DNS Resolver. */ - pjsip_resolver_t *resolver; - - /** Number of modules registered. */ - pj_uint32_t mod_count; - - /** Modules. */ - pjsip_module *modules[PJSIP_MAX_MODULE]; - - /** Number of supported methods. */ - unsigned method_cnt; - - /** Array of supported methods. */ - const pjsip_method *methods[MAX_METHODS]; - - /** Allow header. */ - pjsip_allow_hdr *allow_hdr; - - /** Route header list. */ - pjsip_route_hdr route_hdr_list; - - /** Additional request headers. */ - pjsip_hdr req_hdr; -}; - - - -/* - * Prototypes. - */ -static void endpt_transport_callback( pjsip_endpoint *, pjsip_rx_data *rdata ); - - -/* - * Create transaction. - * Defined in sip_transaction.c - */ -pjsip_transaction * pjsip_tsx_create( pj_pool_t *pool, pjsip_endpoint *endpt); - -/* - * This is the global handler for memory allocation failure, for pools that - * are created by the endpoint (by default, all pools ARE allocated by - * endpoint). The error is handled by throwing exception, and hopefully, - * the exception will be handled by the application (or this library). - */ -static void pool_callback( pj_pool_t *pool, pj_size_t size ) -{ - PJ_UNUSED_ARG(pool) - PJ_UNUSED_ARG(size) - - PJ_THROW(PJSIP_EX_NO_MEMORY); -} - - -/* - * Initialize modules. - */ -static pj_status_t init_modules( pjsip_endpoint *endpt ) -{ - pj_status_t status; - unsigned i; - //pj_str_t str_COMMA = { ", ", 2 }; - extern pjsip_module aux_tsx_module; - - PJ_LOG(5, (LOG_THIS, "init_modules()")); - - /* Load static modules. */ - endpt->mod_count = PJSIP_MAX_MODULE; - status = register_static_modules( &endpt->mod_count, endpt->modules ); - if (status != 0) { - return status; - } - - /* Add mini aux module. */ - endpt->modules[endpt->mod_count++] = &aux_tsx_module; - - /* Load dynamic modules. */ - // Not supported yet! - - /* Sort modules on the priority. */ - for (i=endpt->mod_count-1; i>0; --i) { - pj_uint32_t max = 0; - unsigned j; - for (j=1; j<=i; ++j) { - if (endpt->modules[j]->priority > endpt->modules[max]->priority) - max = j; - } - if (max != i) { - pjsip_module *temp = endpt->modules[max]; - endpt->modules[max] = endpt->modules[i]; - endpt->modules[i] = temp; - } - } - - /* Initialize each module. */ - for (i=0; i < endpt->mod_count; ++i) { - int j; - - pjsip_module *mod = endpt->modules[i]; - if (mod->init_module) { - status = mod->init_module(endpt, mod, i); - if (status != 0) { - return status; - } - } - - /* Collect all supported methods from modules. */ - for (j=0; jmethod_cnt; ++j) { - unsigned k; - for (k=0; kmethod_cnt; ++k) { - if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0) - break; - } - if (k == endpt->method_cnt) { - if (endpt->method_cnt < MAX_METHODS) { - endpt->methods[endpt->method_cnt++] = mod->methods[j]; - } else { - PJ_LOG(1,(LOG_THIS, "Too many methods")); - return -1; - } - } - } - } - - /* Create Allow header. */ - endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool ); - endpt->allow_hdr->count = endpt->method_cnt; - for (i=0; imethod_cnt; ++i) { - endpt->allow_hdr->values[i] = endpt->methods[i]->name; - } - - /* Start each module. */ - for (i=0; i < endpt->mod_count; ++i) { - pjsip_module *mod = endpt->modules[i]; - if (mod->start_module) { - status = mod->start_module(mod); - if (status != 0) { - return status; - } - } - } - - /* Done. */ - return 0; -} - -/* - * Unregister the transaction from the hash table, and destroy the resources - * from the transaction. - */ -PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name)); - - pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED); - - /* No need to lock transaction. - * This function typically is called from the transaction callback, which - * means that transaction mutex is being held. - */ - pj_assert( pj_mutex_is_locked(tsx->mutex) ); - - /* Lock endpoint. */ - pj_mutex_lock( endpt->tsx_table_mutex ); - - /* Unregister from the hash table. */ - pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr, - tsx->transaction_key.slen, NULL); - - /* Unlock endpoint mutex. */ - pj_mutex_unlock( endpt->tsx_table_mutex ); - - /* Destroy transaction mutex. */ - pj_mutex_destroy( tsx->mutex ); - - /* Release the pool for the transaction. */ - pj_pool_release(tsx->pool); - - PJ_LOG(4, (LOG_THIS, "tsx%p destroyed", tsx)); -} - - -/* - * Receive transaction events from transactions and dispatch them to the - * modules. - */ -static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt) -{ - unsigned i; - - /* Dispatch event to modules. */ - for (i=0; imod_count; ++i) { - pjsip_module *mod = endpt->modules[i]; - if (mod && mod->tsx_handler) { - mod->tsx_handler( mod, evt ); - } - } - - /* Destroy transaction if it is terminated. */ - if (evt->type == PJSIP_EVENT_TSX_STATE_CHANGED && - evt->obj.tsx->state == PJSIP_TSX_STATE_DESTROYED) - { - /* No need to lock mutex. Mutex is locked inside the destroy function */ - pjsip_endpt_destroy_tsx( endpt, evt->obj.tsx ); - } -} - -/* - * Receive transaction events from transactions and put in the event queue - * to be processed later. - */ -void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ) -{ - endpt_do_event(endpt, evt); -} - -/* - * Get "Allow" header. - */ -PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ) -{ - return endpt->allow_hdr; -} - -/* - * Get additional headers to be put in outgoing request message. - */ -PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt) -{ - return &endpt->req_hdr; -} - -PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, - int url_cnt, const pj_str_t url[]) -{ - int i; - pjsip_route_hdr *hdr; - pj_str_t str_ROUTE = { "Route", 5 }; - - /* Lock endpoint mutex. */ - pj_mutex_lock(endpt->mutex); - - pj_list_init(&endpt->route_hdr_list); - - for (i=0; ipool, len + 1); - pj_memcpy(dup, url[i].ptr, len); - dup[len] = '\0'; - - hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL); - if (!hdr) { - pj_mutex_unlock(endpt->mutex); - PJ_LOG(4,(LOG_THIS, "Invalid URL %s in proxy URL", dup)); - return -1; - } - - pj_assert(hdr->type == PJSIP_H_ROUTE); - pj_list_insert_before(&endpt->route_hdr_list, hdr); - } - - /* Unlock endpoint mutex. */ - pj_mutex_unlock(endpt->mutex); - - return 0; -} - -/* - * Get "Route" header list. - */ -PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ) -{ - return &endpt->route_hdr_list; -} - - -/* - * Initialize endpoint. - */ -PJ_DEF(pjsip_endpoint*) pjsip_endpt_create(pj_pool_factory *pf) -{ - pj_status_t status; - pj_pool_t *pool; - pjsip_endpoint *endpt; - pjsip_max_forwards_hdr *mf_hdr; - - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create()")); - - /* Create pool */ - pool = pj_pool_create(pf, "pept%p", - PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT, - &pool_callback); - if (!pool) - return NULL; - - /* Create endpoint. */ - endpt = pj_pool_calloc(pool, 1, sizeof(*endpt)); - endpt->pool = pool; - endpt->pf = pf; - - /* Create mutex for the events, etc. */ - endpt->mutex = pj_mutex_create( endpt->pool, "ept%p", 0 ); - if (!endpt->mutex) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating endpoint mutex")); - goto on_error; - } - - /* Create mutex for the transaction table. */ - endpt->tsx_table_mutex = pj_mutex_create( endpt->pool, "mtbl%p", 0); - if (!endpt->tsx_table_mutex) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating endpoint mutex(2)")); - goto on_error; - } - - /* Create hash table for transaction. */ - endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT ); - if (!endpt->tsx_table) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating tsx hash table")); - goto on_error; - } - - /* Create timer heap to manage all timers within this endpoint. */ - endpt->timer_heap = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, 0); - if (!endpt->timer_heap) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating timer heap")); - goto on_error; - } - - /* Create transport manager. */ - endpt->transport_mgr = pjsip_transport_mgr_create( endpt->pool, - endpt, - &endpt_transport_callback); - if (!endpt->transport_mgr) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating transport mgr")); - goto on_error; - } - - /* Create asynchronous DNS resolver. */ - endpt->resolver = pjsip_resolver_create(endpt->pool); - if (!endpt->resolver) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating resolver")); - goto on_error; - } - - /* Initialize TLS ID for transaction lock. */ - pjsip_tsx_lock_tls_id = pj_thread_local_alloc(); - if (pjsip_tsx_lock_tls_id == -1) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error allocating TLS")); - goto on_error; - } - pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL); - - /* Initialize request headers. */ - pj_list_init(&endpt->req_hdr); - - /* Initialist "Route" header list. */ - pj_list_init(&endpt->route_hdr_list); - - /* Add "Max-Forwards" for request header. */ - mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool); - mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE; - pj_list_insert_before( &endpt->req_hdr, mf_hdr); - - /* Load and init modules. */ - status = init_modules(endpt); - if (status != PJ_SUCCESS) { - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error in init_modules()")); - return NULL; - } - - /* Done. */ - return endpt; - -on_error: - if (endpt->transport_mgr) { - pjsip_transport_mgr_destroy(endpt->transport_mgr); - endpt->transport_mgr = NULL; - } - if (endpt->mutex) { - pj_mutex_destroy(endpt->mutex); - endpt->mutex = NULL; - } - if (endpt->tsx_table_mutex) { - pj_mutex_destroy(endpt->tsx_table_mutex); - endpt->tsx_table_mutex = NULL; - } - pj_pool_release( endpt->pool ); - - PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init() failed")); - return NULL; -} - -/* - * Destroy endpoint. - */ -PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy()")); - - /* Shutdown and destroy all transports. */ - pjsip_transport_mgr_destroy(endpt->transport_mgr); - - /* Delete endpoint mutex. */ - pj_mutex_destroy(endpt->mutex); - - /* Delete transaction table mutex. */ - pj_mutex_destroy(endpt->tsx_table_mutex); - - /* Finally destroy pool. */ - pj_pool_release(endpt->pool); -} - -/* - * Create new pool. - */ -PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, - const char *pool_name, - pj_size_t initial, - pj_size_t increment ) -{ - pj_pool_t *pool; - - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_pool()")); - - /* Lock endpoint mutex. */ - pj_mutex_lock(endpt->mutex); - - /* Create pool */ - pool = pj_pool_create( endpt->pf, pool_name, - initial, increment, &pool_callback); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->mutex); - - if (pool) { - PJ_LOG(5, (LOG_THIS, " pool %s created", pj_pool_getobjname(pool))); - } else { - PJ_LOG(4, (LOG_THIS, "Unable to create pool %s!", pool_name)); - } - - return pool; -} - -/* - * Return back pool to endpoint's pool manager to be either destroyed or - * recycled. - */ -PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool))); - - pj_mutex_lock(endpt->mutex); - pj_pool_release( pool ); - pj_mutex_unlock(endpt->mutex); -} - -/* - * Handle events. - */ -PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, - const pj_time_val *max_timeout) -{ - pj_time_val timeout; - int i; - - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_handle_events()")); - - /* Poll the timer. The timer heap has its own mutex for better - * granularity, so we don't need to lock end endpoint. We also keep - * polling the timer while we have events. - */ - timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */ - for (i=0; i<10; ++i) { - if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1) - break; - } - - /* If caller specifies maximum time to wait, then compare the value with - * the timeout to wait from timer, and use the minimum value. - */ - if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { - timeout = *max_timeout; - } - - /* Poll events in the transport manager. */ - pjsip_transport_mgr_handle_events( endpt->transport_mgr, &timeout); -} - -/* - * Schedule timer. - */ -PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, - pj_timer_entry *entry, - const pj_time_val *delay ) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", - entry, delay->sec, delay->msec)); - return pj_timer_heap_schedule( endpt->timer_heap, entry, delay ); -} - -/* - * Cancel the previously registered timer. - */ -PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, - pj_timer_entry *entry ) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_cancel_timer(entry=%p)", entry)); - pj_timer_heap_cancel( endpt->timer_heap, entry ); -} - -/* - * Create a new transaction. - * Endpoint must then initialize the new transaction as either UAS or UAC, and - * register it to the hash table. - */ -PJ_DEF(pjsip_transaction*) pjsip_endpt_create_tsx(pjsip_endpoint *endpt) -{ - pj_pool_t *pool; - pjsip_transaction *tsx; - - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_tsx()")); - - /* Request one pool for the transaction. Mutex is locked there. */ - pool = pjsip_endpt_create_pool(endpt, "ptsx%p", - PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX); - if (pool == NULL) { - PJ_LOG(2, (LOG_THIS, "failed to create transaction (no pool)")); - return NULL; - } - - /* Create the transaction. */ - tsx = pjsip_tsx_create(pool, endpt); - - /* Return */ - return tsx; -} - -/* - * Register the transaction to the endpoint. - * This will put the transaction to the transaction hash table. Before calling - * this function, the transaction must be INITIALIZED as either UAS or UAC, so - * that the transaction key is built. - */ -PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_register_tsx(%s)", tsx->obj_name)); - - pj_assert(tsx->transaction_key.slen != 0); - //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL); - - /* Lock hash table mutex. */ - pj_mutex_lock(endpt->tsx_table_mutex); - - /* Register the transaction to the hash table. */ - pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr, - tsx->transaction_key.slen, tsx); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->tsx_table_mutex); -} - -/* - * Find transaction by the key. - */ -PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, - const pj_str_t *key ) -{ - pjsip_transaction *tsx; - - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_find_tsx()")); - - /* Start lock mutex in the endpoint. */ - pj_mutex_lock(endpt->tsx_table_mutex); - - /* Find the transaction in the hash table. */ - tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen ); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->tsx_table_mutex); - - return tsx; -} - -/* - * Create key. - */ -static void rdata_create_key( pjsip_rx_data *rdata) -{ - pjsip_role_e role; - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - role = PJSIP_ROLE_UAS; - } else { - role = PJSIP_ROLE_UAC; - } - pjsip_tsx_create_key(rdata->pool, &rdata->key, role, - &rdata->cseq->method, rdata); -} - -/* - * This is the callback that is called by the transport manager when it - * receives a message from the network. - */ -static void endpt_transport_callback( pjsip_endpoint *endpt, - pjsip_rx_data *rdata ) -{ - pjsip_msg *msg = rdata->msg; - pjsip_transaction *tsx; - pj_bool_t a_new_transaction_just_been_created = PJ_FALSE; - - PJ_LOG(5, (LOG_THIS, "endpt_transport_callback(rdata=%p)", rdata)); - - /* For response, check that the value in Via sent-by match the transport. - * If not matched, silently drop the response. - * Ref: RFC3261 Section 18.1.2 Receiving Response - */ - if (msg->type == PJSIP_RESPONSE_MSG) { - const pj_sockaddr_in *addr; - const char *addr_addr; - int port = rdata->via->sent_by.port; - pj_bool_t mismatch = PJ_FALSE; - if (port == 0) { - int type; - type = pjsip_transport_get_type(rdata->transport); - port = pjsip_transport_get_default_port_for_type(type); - } - addr = pjsip_transport_get_addr_name(rdata->transport); - addr_addr = pj_sockaddr_get_str_addr(addr); - if (pj_strcmp2(&rdata->via->sent_by.host, addr_addr) != 0) - mismatch = PJ_TRUE; - else if (port != pj_sockaddr_get_port(addr)) { - /* Port or address mismatch, we should discard response */ - /* But we saw one implementation (we don't want to name it to - * protect the innocence) which put wrong sent-by port although - * the "rport" parameter is correct. - * So we discard the response only if the port doesn't match - * both the port in sent-by and rport. We try to be lenient here! - */ - if (rdata->via->rport_param != pj_sockaddr_get_port(addr)) - mismatch = PJ_TRUE; - else { - PJ_LOG(4,(LOG_THIS, "Response %p has mismatch port in sent-by" - " but the rport parameter is correct", - rdata)); - } - } - - if (mismatch) { - pjsip_event e; - - PJ_LOG(3, (LOG_THIS, "Response %p discarded: sent-by mismatch", - rdata)); - - e.type = PJSIP_EVENT_DISCARD_MSG; - e.src_type = PJSIP_EVENT_RX_MSG; - e.src.rdata = rdata; - e.obj.ptr = NULL; - endpt_do_event( endpt, &e ); - return; - } - } - - /* Create key for transaction lookup. */ - rdata_create_key( rdata); - - /* Find the transaction for the received message. */ - PJ_LOG(5, (LOG_THIS, "finding tsx with key=%.*s", - rdata->key.slen, rdata->key.ptr)); - - /* Start lock mutex in the endpoint. */ - pj_mutex_lock(endpt->tsx_table_mutex); - - /* Find the transaction in the hash table. */ - tsx = pj_hash_get( endpt->tsx_table, rdata->key.ptr, rdata->key.slen ); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->tsx_table_mutex); - - /* If the transaction is not found... */ - if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { - - /* - * For response message, discard the message, except if the response is - * an 2xx class response to INVITE, which in this case it must be - * passed to TU to be acked. - */ - if (msg->type == PJSIP_RESPONSE_MSG) { - - /* Inform TU about the 200 message, only if it's INVITE. */ - if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) && - rdata->cseq->method.id == PJSIP_INVITE_METHOD) - { - pjsip_event e; - - /* Should not happen for UA. Tsx theoritically lives until - * all responses are absorbed. - */ - pj_assert(0); - - e.type = PJSIP_EVENT_RX_200_RESPONSE; - e.src_type = PJSIP_EVENT_RX_MSG; - e.src.rdata = rdata; - e.obj.ptr = NULL; - endpt_do_event( endpt, &e ); - - } else { - /* Just discard the response, inform TU. */ - pjsip_event e; - - PJ_LOG(3, (LOG_THIS, "Response %p discarded: transaction not found", - rdata)); - - e.type = PJSIP_EVENT_DISCARD_MSG; - e.src_type = PJSIP_EVENT_RX_MSG; - e.src.rdata = rdata; - e.obj.ptr = NULL; - endpt_do_event( endpt, &e ); - } - - /* - * For non-ACK request message, create a new transaction. - */ - } else if (rdata->msg->line.req.method.id != PJSIP_ACK_METHOD) { - /* Create transaction, mutex is locked there. */ - tsx = pjsip_endpt_create_tsx(endpt); - if (!tsx) - return; - - /* Initialize transaction as UAS. */ - pjsip_tsx_init_uas( tsx, rdata ); - - /* Register transaction, mutex is locked there. */ - pjsip_endpt_register_tsx( endpt, tsx ); - - a_new_transaction_just_been_created = PJ_TRUE; - } - } - - /* If transaction is found (or newly created), pass the message. - * Otherwise if it's an ACK request, pass directly to TU. - */ - if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) { - /* Dispatch message to transaction. */ - pjsip_tsx_on_rx_msg( tsx, rdata ); - - } else if (rdata->msg->line.req.method.id == PJSIP_ACK_METHOD) { - /* - * This is an ACK message, but the INVITE transaction could not - * be found (possibly because the branch parameter in Via in ACK msg - * is different than the branch in original INVITE). This happens with - * SER! - */ - pjsip_event event; - - event.type = PJSIP_EVENT_RX_ACK_MSG; - event.src_type = PJSIP_EVENT_RX_MSG; - event.src.rdata = rdata; - event.obj.ptr = NULL; - endpt_do_event( endpt, &event ); - } - - /* - * If a new request message has just been receieved, but no modules - * seem to be able to handle the request message, then terminate the - * transaction. - * - * Ideally for cases like "unsupported method", we should be able to - * answer the request statelessly. But we can not do that since the - * endpoint shoule be able to be used as both user agent and proxy stack, - * and a proxy stack should be able to handle arbitrary methods. - */ - if (a_new_transaction_just_been_created && tsx->status_code < 100) { - /* Certainly no modules has sent any response message. - * Check that any modules has attached a module data. - */ - int i; - for (i=0; imodule_data[i] != NULL) { - break; - } - } - if (i == PJSIP_MAX_MODULE) { - /* No modules have attached itself to the transaction. - * Terminate the transaction with 501/Not Implemented. - */ - pjsip_tx_data *tdata; - - if (tsx->method.id == PJSIP_OPTIONS_METHOD) { - tdata = pjsip_endpt_create_response(endpt, rdata, 200); - } else { - tdata = pjsip_endpt_create_response(endpt, rdata, - PJSIP_SC_METHOD_NOT_ALLOWED); - } - if (endpt->allow_hdr) { - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr)); - } - pjsip_tsx_on_tx_msg( tsx, tdata ); - - } else { - /* - * If a module has registered itself in the transaction but it - * hasn't responded the request, chances are the module wouldn't - * respond to the request at all. We terminate the request here - * with 500/Internal Server Error, to be safe. - */ - pjsip_tx_data *tdata; - tdata = pjsip_endpt_create_response(endpt, rdata, 500); - pjsip_tsx_on_tx_msg(tsx, tdata); - } - } -} - -/* - * Create transmit data buffer. - */ -PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_tdata( pjsip_endpoint *endpt ) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_tdata()")); - return pjsip_tx_data_create(endpt->transport_mgr); -} - -/* - * Resolve - */ -PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, - pj_pool_t *pool, - pjsip_host_port *target, - void *token, - pjsip_resolver_callback *cb) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_resolve()")); - pjsip_resolve( endpt->resolver, pool, target, token, cb); -} - -/* - * Find/create transport. - */ -PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt, - pj_pool_t *pool, - pjsip_transport_type_e type, - const pj_sockaddr_in *remote, - void *token, - pjsip_transport_completion_callback *cb) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_get_transport()")); - pjsip_transport_get( endpt->transport_mgr, pool, type, - remote, token, cb); -} - - -PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt, - pjsip_transport_type_e type, - pj_sockaddr_in *addr, - const pj_sockaddr_in *addr_name) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_listener()")); - return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name ); -} - -PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt, - pj_sock_t sock, - const pj_sockaddr_in *addr_name) -{ - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_udp_listener()")); - return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name ); -} - -PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) -{ -#if PJ_LOG_MAX_LEVEL >= 3 - unsigned count; - pj_hash_iterator_t itr_val; - pj_hash_iterator_t *itr; - - PJ_LOG(5, (LOG_THIS, "pjsip_endpt_dump()")); - - /* Lock mutex. */ - pj_mutex_lock(endpt->mutex); - - PJ_LOG(3, (LOG_THIS, "Dumping endpoint %p:", endpt)); - - /* Dumping pool factory. */ - (*endpt->pf->dump_status)(endpt->pf, detail); - - /* Pool health. */ - PJ_LOG(3, (LOG_THIS," Endpoint pool capacity=%u, used_size=%u", - pj_pool_get_capacity(endpt->pool), - pj_pool_get_used_size(endpt->pool))); - - /* Transaction tables. */ - count = pj_hash_count(endpt->tsx_table); - PJ_LOG(3, (LOG_THIS, " Number of transactions: %u", count)); - - if (count && detail) { - pj_hash_iterator_t it_val; - pj_hash_iterator_t *it; - pj_time_val now; - - PJ_LOG(3, (LOG_THIS, " Dumping transaction tables:")); - - pj_gettimeofday(&now); - it = pj_hash_first(endpt->tsx_table, &it_val); - - while (it != NULL) { - int timeout_diff; - - /* Get the transaction. No need to lock transaction's mutex - * since we already hold endpoint mutex, so that no transactions - * will be deleted. - */ - pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it); - - const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC"); - - if (tsx->timeout_timer._timer_id != -1) { - if (tsx->timeout_timer._timer_value.sec > now.sec) { - timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec; - } else { - timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec; - timeout_diff = 0 - timeout_diff; - } - } else { - timeout_diff = -1; - } - - PJ_LOG(3, (LOG_THIS, " %s %s %10.*s %.9u %s t=%ds", - tsx->obj_name, role, - tsx->method.name.slen, tsx->method.name.ptr, - tsx->cseq, - pjsip_tsx_state_str(tsx->state), - timeout_diff)); - - it = pj_hash_next(endpt->tsx_table, it); - } - } - - /* Transports. - * Note: transport is not properly locked in this function. - * See pjsip_transport_first, pjsip_transport_next. - */ - itr = pjsip_transport_first( endpt->transport_mgr, &itr_val ); - if (itr) { - PJ_LOG(3, (LOG_THIS, " Dumping transports:")); - - do { - char src_addr[128], dst_addr[128]; - int src_port, dst_port; - const pj_sockaddr_in *addr; - pjsip_transport_t *t; - - t = pjsip_transport_this(endpt->transport_mgr, itr); - addr = pjsip_transport_get_local_addr(t); - strcpy(src_addr, pj_sockaddr_get_str_addr(addr)); - src_port = pj_sockaddr_get_port(addr); - - addr = pjsip_transport_get_remote_addr(t); - strcpy(dst_addr, pj_sockaddr_get_str_addr(addr)); - dst_port = pj_sockaddr_get_port(addr); - - PJ_LOG(3, (LOG_THIS, " %s %s %s:%d --> %s:%d (refcnt=%d)", - pjsip_transport_get_type_name(t), - pjsip_transport_get_obj_name(t), - src_addr, src_port, - dst_addr, dst_port, - pjsip_transport_get_ref_cnt(t))); - - itr = pjsip_transport_next(endpt->transport_mgr, itr); - } while (itr); - } - - /* Timer. */ - PJ_LOG(3,(LOG_THIS, " Timer heap has %u entries", - pj_timer_heap_count(endpt->timer_heap))); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->mutex); -#else - PJ_LOG(3,(LOG_THIS, "pjsip_end_dump: can't dump because it's disabled.")); -#endif -} - +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_endpoint.c 26 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION +#define LOG_THIS "endpoint..." + +#define MAX_METHODS 32 + +/** + * The SIP endpoint. + */ +struct pjsip_endpoint +{ + /** Pool to allocate memory for the endpoint. */ + pj_pool_t *pool; + + /** Mutex for the pool, hash table, and event list/queue. */ + pj_mutex_t *mutex; + + /** Pool factory. */ + pj_pool_factory *pf; + + /** Transaction table. */ + pj_hash_table_t *tsx_table; + + /** Mutex for transaction table. */ + pj_mutex_t *tsx_table_mutex; + + /** Timer heap. */ + pj_timer_heap_t *timer_heap; + + /** Transport manager. */ + pjsip_transport_mgr *transport_mgr; + + /** DNS Resolver. */ + pjsip_resolver_t *resolver; + + /** Number of modules registered. */ + pj_uint32_t mod_count; + + /** Modules. */ + pjsip_module *modules[PJSIP_MAX_MODULE]; + + /** Number of supported methods. */ + unsigned method_cnt; + + /** Array of supported methods. */ + const pjsip_method *methods[MAX_METHODS]; + + /** Allow header. */ + pjsip_allow_hdr *allow_hdr; + + /** Route header list. */ + pjsip_route_hdr route_hdr_list; + + /** Additional request headers. */ + pjsip_hdr req_hdr; +}; + + + +/* + * Prototypes. + */ +static void endpt_transport_callback( pjsip_endpoint *, pjsip_rx_data *rdata ); + + +/* + * Create transaction. + * Defined in sip_transaction.c + */ +pjsip_transaction * pjsip_tsx_create( pj_pool_t *pool, pjsip_endpoint *endpt); + +/* + * This is the global handler for memory allocation failure, for pools that + * are created by the endpoint (by default, all pools ARE allocated by + * endpoint). The error is handled by throwing exception, and hopefully, + * the exception will be handled by the application (or this library). + */ +static void pool_callback( pj_pool_t *pool, pj_size_t size ) +{ + PJ_UNUSED_ARG(pool) + PJ_UNUSED_ARG(size) + + PJ_THROW(PJSIP_EX_NO_MEMORY); +} + + +/* + * Initialize modules. + */ +static pj_status_t init_modules( pjsip_endpoint *endpt ) +{ + pj_status_t status; + unsigned i; + //pj_str_t str_COMMA = { ", ", 2 }; + extern pjsip_module aux_tsx_module; + + PJ_LOG(5, (LOG_THIS, "init_modules()")); + + /* Load static modules. */ + endpt->mod_count = PJSIP_MAX_MODULE; + status = register_static_modules( &endpt->mod_count, endpt->modules ); + if (status != 0) { + return status; + } + + /* Add mini aux module. */ + endpt->modules[endpt->mod_count++] = &aux_tsx_module; + + /* Load dynamic modules. */ + // Not supported yet! + + /* Sort modules on the priority. */ + for (i=endpt->mod_count-1; i>0; --i) { + pj_uint32_t max = 0; + unsigned j; + for (j=1; j<=i; ++j) { + if (endpt->modules[j]->priority > endpt->modules[max]->priority) + max = j; + } + if (max != i) { + pjsip_module *temp = endpt->modules[max]; + endpt->modules[max] = endpt->modules[i]; + endpt->modules[i] = temp; + } + } + + /* Initialize each module. */ + for (i=0; i < endpt->mod_count; ++i) { + int j; + + pjsip_module *mod = endpt->modules[i]; + if (mod->init_module) { + status = mod->init_module(endpt, mod, i); + if (status != 0) { + return status; + } + } + + /* Collect all supported methods from modules. */ + for (j=0; jmethod_cnt; ++j) { + unsigned k; + for (k=0; kmethod_cnt; ++k) { + if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0) + break; + } + if (k == endpt->method_cnt) { + if (endpt->method_cnt < MAX_METHODS) { + endpt->methods[endpt->method_cnt++] = mod->methods[j]; + } else { + PJ_LOG(1,(LOG_THIS, "Too many methods")); + return -1; + } + } + } + } + + /* Create Allow header. */ + endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool ); + endpt->allow_hdr->count = endpt->method_cnt; + for (i=0; imethod_cnt; ++i) { + endpt->allow_hdr->values[i] = endpt->methods[i]->name; + } + + /* Start each module. */ + for (i=0; i < endpt->mod_count; ++i) { + pjsip_module *mod = endpt->modules[i]; + if (mod->start_module) { + status = mod->start_module(mod); + if (status != 0) { + return status; + } + } + } + + /* Done. */ + return 0; +} + +/* + * Unregister the transaction from the hash table, and destroy the resources + * from the transaction. + */ +PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name)); + + pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED); + + /* No need to lock transaction. + * This function typically is called from the transaction callback, which + * means that transaction mutex is being held. + */ + pj_assert( pj_mutex_is_locked(tsx->mutex) ); + + /* Lock endpoint. */ + pj_mutex_lock( endpt->tsx_table_mutex ); + + /* Unregister from the hash table. */ + pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr, + tsx->transaction_key.slen, NULL); + + /* Unlock endpoint mutex. */ + pj_mutex_unlock( endpt->tsx_table_mutex ); + + /* Destroy transaction mutex. */ + pj_mutex_destroy( tsx->mutex ); + + /* Release the pool for the transaction. */ + pj_pool_release(tsx->pool); + + PJ_LOG(4, (LOG_THIS, "tsx%p destroyed", tsx)); +} + + +/* + * Receive transaction events from transactions and dispatch them to the + * modules. + */ +static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt) +{ + unsigned i; + + /* Dispatch event to modules. */ + for (i=0; imod_count; ++i) { + pjsip_module *mod = endpt->modules[i]; + if (mod && mod->tsx_handler) { + mod->tsx_handler( mod, evt ); + } + } + + /* Destroy transaction if it is terminated. */ + if (evt->type == PJSIP_EVENT_TSX_STATE_CHANGED && + evt->obj.tsx->state == PJSIP_TSX_STATE_DESTROYED) + { + /* No need to lock mutex. Mutex is locked inside the destroy function */ + pjsip_endpt_destroy_tsx( endpt, evt->obj.tsx ); + } +} + +/* + * Receive transaction events from transactions and put in the event queue + * to be processed later. + */ +void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ) +{ + endpt_do_event(endpt, evt); +} + +/* + * Get "Allow" header. + */ +PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ) +{ + return endpt->allow_hdr; +} + +/* + * Get additional headers to be put in outgoing request message. + */ +PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt) +{ + return &endpt->req_hdr; +} + +PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, + int url_cnt, const pj_str_t url[]) +{ + int i; + pjsip_route_hdr *hdr; + pj_str_t str_ROUTE = { "Route", 5 }; + + /* Lock endpoint mutex. */ + pj_mutex_lock(endpt->mutex); + + pj_list_init(&endpt->route_hdr_list); + + for (i=0; ipool, len + 1); + pj_memcpy(dup, url[i].ptr, len); + dup[len] = '\0'; + + hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL); + if (!hdr) { + pj_mutex_unlock(endpt->mutex); + PJ_LOG(4,(LOG_THIS, "Invalid URL %s in proxy URL", dup)); + return -1; + } + + pj_assert(hdr->type == PJSIP_H_ROUTE); + pj_list_insert_before(&endpt->route_hdr_list, hdr); + } + + /* Unlock endpoint mutex. */ + pj_mutex_unlock(endpt->mutex); + + return 0; +} + +/* + * Get "Route" header list. + */ +PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ) +{ + return &endpt->route_hdr_list; +} + + +/* + * Initialize endpoint. + */ +PJ_DEF(pjsip_endpoint*) pjsip_endpt_create(pj_pool_factory *pf) +{ + pj_status_t status; + pj_pool_t *pool; + pjsip_endpoint *endpt; + pjsip_max_forwards_hdr *mf_hdr; + + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create()")); + + /* Create pool */ + pool = pj_pool_create(pf, "pept%p", + PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT, + &pool_callback); + if (!pool) + return NULL; + + /* Create endpoint. */ + endpt = pj_pool_calloc(pool, 1, sizeof(*endpt)); + endpt->pool = pool; + endpt->pf = pf; + + /* Create mutex for the events, etc. */ + endpt->mutex = pj_mutex_create( endpt->pool, "ept%p", 0 ); + if (!endpt->mutex) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating endpoint mutex")); + goto on_error; + } + + /* Create mutex for the transaction table. */ + endpt->tsx_table_mutex = pj_mutex_create( endpt->pool, "mtbl%p", 0); + if (!endpt->tsx_table_mutex) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating endpoint mutex(2)")); + goto on_error; + } + + /* Create hash table for transaction. */ + endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT ); + if (!endpt->tsx_table) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating tsx hash table")); + goto on_error; + } + + /* Create timer heap to manage all timers within this endpoint. */ + endpt->timer_heap = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT, 0); + if (!endpt->timer_heap) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating timer heap")); + goto on_error; + } + + /* Create transport manager. */ + endpt->transport_mgr = pjsip_transport_mgr_create( endpt->pool, + endpt, + &endpt_transport_callback); + if (!endpt->transport_mgr) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating transport mgr")); + goto on_error; + } + + /* Create asynchronous DNS resolver. */ + endpt->resolver = pjsip_resolver_create(endpt->pool); + if (!endpt->resolver) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error creating resolver")); + goto on_error; + } + + /* Initialize TLS ID for transaction lock. */ + pjsip_tsx_lock_tls_id = pj_thread_local_alloc(); + if (pjsip_tsx_lock_tls_id == -1) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error allocating TLS")); + goto on_error; + } + pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL); + + /* Initialize request headers. */ + pj_list_init(&endpt->req_hdr); + + /* Initialist "Route" header list. */ + pj_list_init(&endpt->route_hdr_list); + + /* Add "Max-Forwards" for request header. */ + mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool); + mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE; + pj_list_insert_before( &endpt->req_hdr, mf_hdr); + + /* Load and init modules. */ + status = init_modules(endpt); + if (status != PJ_SUCCESS) { + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init(): error in init_modules()")); + return NULL; + } + + /* Done. */ + return endpt; + +on_error: + if (endpt->transport_mgr) { + pjsip_transport_mgr_destroy(endpt->transport_mgr); + endpt->transport_mgr = NULL; + } + if (endpt->mutex) { + pj_mutex_destroy(endpt->mutex); + endpt->mutex = NULL; + } + if (endpt->tsx_table_mutex) { + pj_mutex_destroy(endpt->tsx_table_mutex); + endpt->tsx_table_mutex = NULL; + } + pj_pool_release( endpt->pool ); + + PJ_LOG(4, (LOG_THIS, "pjsip_endpt_init() failed")); + return NULL; +} + +/* + * Destroy endpoint. + */ +PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy()")); + + /* Shutdown and destroy all transports. */ + pjsip_transport_mgr_destroy(endpt->transport_mgr); + + /* Delete endpoint mutex. */ + pj_mutex_destroy(endpt->mutex); + + /* Delete transaction table mutex. */ + pj_mutex_destroy(endpt->tsx_table_mutex); + + /* Finally destroy pool. */ + pj_pool_release(endpt->pool); +} + +/* + * Create new pool. + */ +PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, + const char *pool_name, + pj_size_t initial, + pj_size_t increment ) +{ + pj_pool_t *pool; + + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_pool()")); + + /* Lock endpoint mutex. */ + pj_mutex_lock(endpt->mutex); + + /* Create pool */ + pool = pj_pool_create( endpt->pf, pool_name, + initial, increment, &pool_callback); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->mutex); + + if (pool) { + PJ_LOG(5, (LOG_THIS, " pool %s created", pj_pool_getobjname(pool))); + } else { + PJ_LOG(4, (LOG_THIS, "Unable to create pool %s!", pool_name)); + } + + return pool; +} + +/* + * Return back pool to endpoint's pool manager to be either destroyed or + * recycled. + */ +PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool))); + + pj_mutex_lock(endpt->mutex); + pj_pool_release( pool ); + pj_mutex_unlock(endpt->mutex); +} + +/* + * Handle events. + */ +PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, + const pj_time_val *max_timeout) +{ + pj_time_val timeout; + int i; + + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_handle_events()")); + + /* Poll the timer. The timer heap has its own mutex for better + * granularity, so we don't need to lock end endpoint. We also keep + * polling the timer while we have events. + */ + timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */ + for (i=0; i<10; ++i) { + if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1) + break; + } + + /* If caller specifies maximum time to wait, then compare the value with + * the timeout to wait from timer, and use the minimum value. + */ + if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { + timeout = *max_timeout; + } + + /* Poll events in the transport manager. */ + pjsip_transport_mgr_handle_events( endpt->transport_mgr, &timeout); +} + +/* + * Schedule timer. + */ +PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry, + const pj_time_val *delay ) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", + entry, delay->sec, delay->msec)); + return pj_timer_heap_schedule( endpt->timer_heap, entry, delay ); +} + +/* + * Cancel the previously registered timer. + */ +PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry ) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_cancel_timer(entry=%p)", entry)); + pj_timer_heap_cancel( endpt->timer_heap, entry ); +} + +/* + * Create a new transaction. + * Endpoint must then initialize the new transaction as either UAS or UAC, and + * register it to the hash table. + */ +PJ_DEF(pjsip_transaction*) pjsip_endpt_create_tsx(pjsip_endpoint *endpt) +{ + pj_pool_t *pool; + pjsip_transaction *tsx; + + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_tsx()")); + + /* Request one pool for the transaction. Mutex is locked there. */ + pool = pjsip_endpt_create_pool(endpt, "ptsx%p", + PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX); + if (pool == NULL) { + PJ_LOG(2, (LOG_THIS, "failed to create transaction (no pool)")); + return NULL; + } + + /* Create the transaction. */ + tsx = pjsip_tsx_create(pool, endpt); + + /* Return */ + return tsx; +} + +/* + * Register the transaction to the endpoint. + * This will put the transaction to the transaction hash table. Before calling + * this function, the transaction must be INITIALIZED as either UAS or UAC, so + * that the transaction key is built. + */ +PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_register_tsx(%s)", tsx->obj_name)); + + pj_assert(tsx->transaction_key.slen != 0); + //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL); + + /* Lock hash table mutex. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Register the transaction to the hash table. */ + pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr, + tsx->transaction_key.slen, tsx); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); +} + +/* + * Find transaction by the key. + */ +PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, + const pj_str_t *key ) +{ + pjsip_transaction *tsx; + + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_find_tsx()")); + + /* Start lock mutex in the endpoint. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Find the transaction in the hash table. */ + tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen ); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); + + return tsx; +} + +/* + * Create key. + */ +static void rdata_create_key( pjsip_rx_data *rdata) +{ + pjsip_role_e role; + if (rdata->msg->type == PJSIP_REQUEST_MSG) { + role = PJSIP_ROLE_UAS; + } else { + role = PJSIP_ROLE_UAC; + } + pjsip_tsx_create_key(rdata->pool, &rdata->key, role, + &rdata->cseq->method, rdata); +} + +/* + * This is the callback that is called by the transport manager when it + * receives a message from the network. + */ +static void endpt_transport_callback( pjsip_endpoint *endpt, + pjsip_rx_data *rdata ) +{ + pjsip_msg *msg = rdata->msg; + pjsip_transaction *tsx; + pj_bool_t a_new_transaction_just_been_created = PJ_FALSE; + + PJ_LOG(5, (LOG_THIS, "endpt_transport_callback(rdata=%p)", rdata)); + + /* For response, check that the value in Via sent-by match the transport. + * If not matched, silently drop the response. + * Ref: RFC3261 Section 18.1.2 Receiving Response + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + const pj_sockaddr_in *addr; + const char *addr_addr; + int port = rdata->via->sent_by.port; + pj_bool_t mismatch = PJ_FALSE; + if (port == 0) { + int type; + type = pjsip_transport_get_type(rdata->transport); + port = pjsip_transport_get_default_port_for_type(type); + } + addr = pjsip_transport_get_addr_name(rdata->transport); + addr_addr = pj_sockaddr_get_str_addr(addr); + if (pj_strcmp2(&rdata->via->sent_by.host, addr_addr) != 0) + mismatch = PJ_TRUE; + else if (port != pj_sockaddr_get_port(addr)) { + /* Port or address mismatch, we should discard response */ + /* But we saw one implementation (we don't want to name it to + * protect the innocence) which put wrong sent-by port although + * the "rport" parameter is correct. + * So we discard the response only if the port doesn't match + * both the port in sent-by and rport. We try to be lenient here! + */ + if (rdata->via->rport_param != pj_sockaddr_get_port(addr)) + mismatch = PJ_TRUE; + else { + PJ_LOG(4,(LOG_THIS, "Response %p has mismatch port in sent-by" + " but the rport parameter is correct", + rdata)); + } + } + + if (mismatch) { + pjsip_event e; + + PJ_LOG(3, (LOG_THIS, "Response %p discarded: sent-by mismatch", + rdata)); + + e.type = PJSIP_EVENT_DISCARD_MSG; + e.src_type = PJSIP_EVENT_RX_MSG; + e.src.rdata = rdata; + e.obj.ptr = NULL; + endpt_do_event( endpt, &e ); + return; + } + } + + /* Create key for transaction lookup. */ + rdata_create_key( rdata); + + /* Find the transaction for the received message. */ + PJ_LOG(5, (LOG_THIS, "finding tsx with key=%.*s", + rdata->key.slen, rdata->key.ptr)); + + /* Start lock mutex in the endpoint. */ + pj_mutex_lock(endpt->tsx_table_mutex); + + /* Find the transaction in the hash table. */ + tsx = pj_hash_get( endpt->tsx_table, rdata->key.ptr, rdata->key.slen ); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->tsx_table_mutex); + + /* If the transaction is not found... */ + if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { + + /* + * For response message, discard the message, except if the response is + * an 2xx class response to INVITE, which in this case it must be + * passed to TU to be acked. + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + + /* Inform TU about the 200 message, only if it's INVITE. */ + if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) && + rdata->cseq->method.id == PJSIP_INVITE_METHOD) + { + pjsip_event e; + + /* Should not happen for UA. Tsx theoritically lives until + * all responses are absorbed. + */ + pj_assert(0); + + e.type = PJSIP_EVENT_RX_200_RESPONSE; + e.src_type = PJSIP_EVENT_RX_MSG; + e.src.rdata = rdata; + e.obj.ptr = NULL; + endpt_do_event( endpt, &e ); + + } else { + /* Just discard the response, inform TU. */ + pjsip_event e; + + PJ_LOG(3, (LOG_THIS, "Response %p discarded: transaction not found", + rdata)); + + e.type = PJSIP_EVENT_DISCARD_MSG; + e.src_type = PJSIP_EVENT_RX_MSG; + e.src.rdata = rdata; + e.obj.ptr = NULL; + endpt_do_event( endpt, &e ); + } + + /* + * For non-ACK request message, create a new transaction. + */ + } else if (rdata->msg->line.req.method.id != PJSIP_ACK_METHOD) { + /* Create transaction, mutex is locked there. */ + tsx = pjsip_endpt_create_tsx(endpt); + if (!tsx) + return; + + /* Initialize transaction as UAS. */ + pjsip_tsx_init_uas( tsx, rdata ); + + /* Register transaction, mutex is locked there. */ + pjsip_endpt_register_tsx( endpt, tsx ); + + a_new_transaction_just_been_created = PJ_TRUE; + } + } + + /* If transaction is found (or newly created), pass the message. + * Otherwise if it's an ACK request, pass directly to TU. + */ + if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) { + /* Dispatch message to transaction. */ + pjsip_tsx_on_rx_msg( tsx, rdata ); + + } else if (rdata->msg->line.req.method.id == PJSIP_ACK_METHOD) { + /* + * This is an ACK message, but the INVITE transaction could not + * be found (possibly because the branch parameter in Via in ACK msg + * is different than the branch in original INVITE). This happens with + * SER! + */ + pjsip_event event; + + event.type = PJSIP_EVENT_RX_ACK_MSG; + event.src_type = PJSIP_EVENT_RX_MSG; + event.src.rdata = rdata; + event.obj.ptr = NULL; + endpt_do_event( endpt, &event ); + } + + /* + * If a new request message has just been receieved, but no modules + * seem to be able to handle the request message, then terminate the + * transaction. + * + * Ideally for cases like "unsupported method", we should be able to + * answer the request statelessly. But we can not do that since the + * endpoint shoule be able to be used as both user agent and proxy stack, + * and a proxy stack should be able to handle arbitrary methods. + */ + if (a_new_transaction_just_been_created && tsx->status_code < 100) { + /* Certainly no modules has sent any response message. + * Check that any modules has attached a module data. + */ + int i; + for (i=0; imodule_data[i] != NULL) { + break; + } + } + if (i == PJSIP_MAX_MODULE) { + /* No modules have attached itself to the transaction. + * Terminate the transaction with 501/Not Implemented. + */ + pjsip_tx_data *tdata; + + if (tsx->method.id == PJSIP_OPTIONS_METHOD) { + tdata = pjsip_endpt_create_response(endpt, rdata, 200); + } else { + tdata = pjsip_endpt_create_response(endpt, rdata, + PJSIP_SC_METHOD_NOT_ALLOWED); + } + if (endpt->allow_hdr) { + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr)); + } + pjsip_tsx_on_tx_msg( tsx, tdata ); + + } else { + /* + * If a module has registered itself in the transaction but it + * hasn't responded the request, chances are the module wouldn't + * respond to the request at all. We terminate the request here + * with 500/Internal Server Error, to be safe. + */ + pjsip_tx_data *tdata; + tdata = pjsip_endpt_create_response(endpt, rdata, 500); + pjsip_tsx_on_tx_msg(tsx, tdata); + } + } +} + +/* + * Create transmit data buffer. + */ +PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_tdata( pjsip_endpoint *endpt ) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_tdata()")); + return pjsip_tx_data_create(endpt->transport_mgr); +} + +/* + * Resolve + */ +PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_resolve()")); + pjsip_resolve( endpt->resolver, pool, target, token, cb); +} + +/* + * Find/create transport. + */ +PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + void *token, + pjsip_transport_completion_callback *cb) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_get_transport()")); + pjsip_transport_get( endpt->transport_mgr, pool, type, + remote, token, cb); +} + + +PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt, + pjsip_transport_type_e type, + pj_sockaddr_in *addr, + const pj_sockaddr_in *addr_name) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_listener()")); + return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name ); +} + +PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt, + pj_sock_t sock, + const pj_sockaddr_in *addr_name) +{ + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_create_udp_listener()")); + return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name ); +} + +PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + unsigned count; + pj_hash_iterator_t itr_val; + pj_hash_iterator_t *itr; + + PJ_LOG(5, (LOG_THIS, "pjsip_endpt_dump()")); + + /* Lock mutex. */ + pj_mutex_lock(endpt->mutex); + + PJ_LOG(3, (LOG_THIS, "Dumping endpoint %p:", endpt)); + + /* Dumping pool factory. */ + (*endpt->pf->dump_status)(endpt->pf, detail); + + /* Pool health. */ + PJ_LOG(3, (LOG_THIS," Endpoint pool capacity=%u, used_size=%u", + pj_pool_get_capacity(endpt->pool), + pj_pool_get_used_size(endpt->pool))); + + /* Transaction tables. */ + count = pj_hash_count(endpt->tsx_table); + PJ_LOG(3, (LOG_THIS, " Number of transactions: %u", count)); + + if (count && detail) { + pj_hash_iterator_t it_val; + pj_hash_iterator_t *it; + pj_time_val now; + + PJ_LOG(3, (LOG_THIS, " Dumping transaction tables:")); + + pj_gettimeofday(&now); + it = pj_hash_first(endpt->tsx_table, &it_val); + + while (it != NULL) { + int timeout_diff; + + /* Get the transaction. No need to lock transaction's mutex + * since we already hold endpoint mutex, so that no transactions + * will be deleted. + */ + pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it); + + const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC"); + + if (tsx->timeout_timer._timer_id != -1) { + if (tsx->timeout_timer._timer_value.sec > now.sec) { + timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec; + } else { + timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec; + timeout_diff = 0 - timeout_diff; + } + } else { + timeout_diff = -1; + } + + PJ_LOG(3, (LOG_THIS, " %s %s %10.*s %.9u %s t=%ds", + tsx->obj_name, role, + tsx->method.name.slen, tsx->method.name.ptr, + tsx->cseq, + pjsip_tsx_state_str(tsx->state), + timeout_diff)); + + it = pj_hash_next(endpt->tsx_table, it); + } + } + + /* Transports. + * Note: transport is not properly locked in this function. + * See pjsip_transport_first, pjsip_transport_next. + */ + itr = pjsip_transport_first( endpt->transport_mgr, &itr_val ); + if (itr) { + PJ_LOG(3, (LOG_THIS, " Dumping transports:")); + + do { + char src_addr[128], dst_addr[128]; + int src_port, dst_port; + const pj_sockaddr_in *addr; + pjsip_transport_t *t; + + t = pjsip_transport_this(endpt->transport_mgr, itr); + addr = pjsip_transport_get_local_addr(t); + strcpy(src_addr, pj_sockaddr_get_str_addr(addr)); + src_port = pj_sockaddr_get_port(addr); + + addr = pjsip_transport_get_remote_addr(t); + strcpy(dst_addr, pj_sockaddr_get_str_addr(addr)); + dst_port = pj_sockaddr_get_port(addr); + + PJ_LOG(3, (LOG_THIS, " %s %s %s:%d --> %s:%d (refcnt=%d)", + pjsip_transport_get_type_name(t), + pjsip_transport_get_obj_name(t), + src_addr, src_port, + dst_addr, dst_port, + pjsip_transport_get_ref_cnt(t))); + + itr = pjsip_transport_next(endpt->transport_mgr, itr); + } while (itr); + } + + /* Timer. */ + PJ_LOG(3,(LOG_THIS, " Timer heap has %u entries", + pj_timer_heap_count(endpt->timer_heap))); + + /* Unlock mutex. */ + pj_mutex_unlock(endpt->mutex); +#else + PJ_LOG(3,(LOG_THIS, "pjsip_end_dump: can't dump because it's disabled.")); +#endif +} + diff --git a/pjsip/src/pjsip/sip_endpoint.h b/pjsip/src/pjsip/sip_endpoint.h index 3b1c18b4e..5e866eb8f 100644 --- a/pjsip/src/pjsip/sip_endpoint.h +++ b/pjsip/src/pjsip/sip_endpoint.h @@ -1,348 +1,348 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_endpoint.h 12 6/22/05 12:27a Bennylp $ */ -#ifndef __PJSIP_SIP_ENDPOINT_H__ -#define __PJSIP_SIP_ENDPOINT_H__ - -/** - * @file sip_endpoint.h - * @brief SIP Endpoint. - */ - -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP SIP Stack Core - * Implementation of core SIP protocol stack processing. - */ - -/** - * @defgroup PJSIP_ENDPT SIP Endpoint - * @ingroup PJSIP - * @brief - * Representation of SIP node instance. - * - * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of - * all SIP objects in an application. It performs the following roles: - * - it manages the allocation/deallocation of memory pools for all objects. - * - it manages listeners and transports, and how they are used by transactions. - * - it owns transaction hash table. - * - it receives incoming messages from transport layer and automatically - * dispatches them to the correct transaction (or create a new one). - * - it has a single instance of timer management (timer heap). - * - it manages modules, which is the primary means of extending the library. - * - it provides single polling function for all objects and distributes events. - * - it provides SIP policy such as which outbound proxy to use for all - * outgoing SIP request messages. - * - it automatically handles incoming requests which can not be handled by - * existing modules (such as when incoming request has unsupported method). - * - and so on.. - * - * Theoritically application can have multiple instances of SIP endpoint, - * although it's not clear why application may want to do it. - * - * @{ - */ - -/** - * Create an instance of SIP endpoint from the specified pool factory. - * The pool factory reference then will be kept by the endpoint, so that future - * memory allocations by SIP components will be taken from the same pool factory. - * - * @param pf Pool factory that will be used for the lifetime of endpoint. - * - * @return the endpoint instance on success. - */ -PJ_DECL(pjsip_endpoint*) pjsip_endpt_create(pj_pool_factory *pf); - -/** - * Destroy endpoint instance. Application must make sure that all pending - * transactions have been terminated properly, because this function does not - * check for the presence of pending transactions. - * - * @param endpt The SIP endpoint to be destroyed. - */ -PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt); - -/** - * Poll for events. Application must call this function periodically to ensure - * that all events from both transports and timer heap are handled in timely - * manner. This function, like all other endpoint functions, is thread safe, - * and application may have more than one thread concurrently calling this function. - * - * @param endpt The endpoint. - * @param max_timeout Maximum time to wait for events, or NULL to wait forever - * until event is received. - */ -PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, - const pj_time_val *max_timeout); - -/** - * Dump endpoint status to the log. This will print the status to the log - * with log level 3. - * - * @param endpt The endpoint. - * @param detail If non zero, then it will dump a detailed output. - * BEWARE that this option may crash the system because - * it tries to access all memory pools. - */ -PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ); - -/** - * Create pool from the endpoint. All SIP components should allocate their - * memory pool by calling this function, to make sure that the pools are - * allocated from the same pool factory. This function, like all other endpoint - * functions, is thread safe. - * - * @param endpt The SIP endpoint. - * @param pool_name Name to be assigned to the pool. - * @param initial The initial size of the pool. - * @param increment The resize size. - * @return Memory pool, or NULL on failure. - * - * @see pj_pool_create - */ -PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, - const char *pool_name, - pj_size_t initial, - pj_size_t increment ); - -/** - * Return back pool to endpoint to be released back to the pool factory. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt The endpoint. - * @param pool The pool to be destroyed. - */ -PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, - pj_pool_t *pool ); - -/** - * Schedule timer to endpoint's timer heap. Application must poll the endpoint - * periodically (by calling #pjsip_endpt_handle_events) to ensure that the - * timer events are handled in timely manner. When the timeout for the timer - * has elapsed, the callback specified in the entry argument will be called. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt The endpoint. - * @param entry The timer entry. - * @param delay The relative delay of the timer. - * @return PJ_OK (zero) if successfull. - */ -PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, - pj_timer_entry *entry, - const pj_time_val *delay ); - -/** - * Cancel the previously registered timer. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt The endpoint. - * @param entry The timer entry previously registered. - */ -PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, - pj_timer_entry *entry ); - -/** - * Create a new transaction. After creating the transaction, application MUST - * initialize the transaction as either UAC or UAS (by calling - * #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the - * transaction to endpoint with #pjsip_endpt_register_tsx. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt The SIP endpoint. - * @return The new transaction, or NULL on failure. - */ -PJ_DECL(pjsip_transaction*) pjsip_endpt_create_tsx(pjsip_endpoint *endpt); - -/** - * Register the transaction to the endpoint's transaction table. - * Before the transaction is registered, it must have been initialized as - * either UAS or UAC by calling #pjsip_tsx_init_uac or #pjsip_tsx_init_uas. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt The SIP endpoint. - * @param tsx The transaction. - */ -PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx); - -/** - * Forcefull destroy the transaction. - * The only time where application needs to call this function is when the - * transaction fails to initialize in #pjsip_tsx_init_uac or - * #pjsip_tsx_init_uas. For other cases. the transaction will be destroyed - * automaticly by endpoint. - * - * @param endpt The endpoint. - * @param tsx The transaction to destroy. - */ -PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx); - -/** - * Create a new transmit data buffer. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt the endpoint. - * @return new transmit data. - */ -PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_tdata( pjsip_endpoint *endpt ); - -/** - * Asynchronously resolve a SIP target host or domain according to rule - * specified in RFC 3263 (Locating SIP Servers). When the resolving operation - * has completed, the callback will be called. - * - * Note: at the moment we don't have implementation of RFC 3263 yet! - * - * @param resolver The resolver engine. - * @param pool The pool to allocate resolver job. - * @param target The target specification to be resolved. - * @param token A user defined token to be passed back to callback function. - * @param cb The callback function. - */ -PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, - pj_pool_t *pool, - pjsip_host_port *target, - void *token, - pjsip_resolver_callback *cb); - -/** - * Find a SIP transport suitable for sending SIP message to the specified - * address. This function will complete asynchronously when the transport is - * ready (for example, when TCP socket is connected), and when it completes, - * the callback will be called with the status of the operation. - * - * @see pjsip_transport_get - */ -PJ_DECL(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt, - pj_pool_t *pool, - pjsip_transport_type_e type, - const pj_sockaddr_in *remote, - void *token, - pjsip_transport_completion_callback *cb); - -/** - * Create listener a new transport listener. A listener is transport object - * that is capable of receiving SIP messages. For UDP listener, normally - * application should use #pjsip_endpt_create_udp_listener instead if the - * application has already created the socket. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt The endpoint instance. - * @param type Transport type (eg. UDP, TCP, etc.) - * @param addr The bound address of the transport. - * @param addr_name The address to be advertised in SIP messages. For example, - * the bound address can be 0.0.0.0, but the advertised address - * normally will be the IP address of the host. - * - * @return Zero if listener is created successfully. - */ -PJ_DECL(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt, - pjsip_transport_type_e type, - pj_sockaddr_in *addr, - const pj_sockaddr_in *addr_name); - -/** - * Create UDP listener. For UDP, normally the application would create the - * socket by itself (for STUN purpose), then it can register the socket as - * listener by calling this function. - * This function, like all other endpoint functions, is thread safe. - * - * @param endpt The endpoint instance. - * @param sock The socket handle. - * @param addr_name The address to be advertised in SIP message. If the socket - * has been resolved with STUN, then application may specify - * the mapped address in this parameter. - * - * @return Zero if listener is created successfully. - */ -PJ_DECL(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt, - pj_sock_t sock, - const pj_sockaddr_in *addr_name); - -/** - * Get additional headers to be put in outgoing request message. - * This function is normally called by transaction layer when sending outgoing - * requests. - * - * @param endpt The endpoint. - * - * @return List of additional headers to be put in outgoing requests. - */ -PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt); - -/** - * Get "Allow" header from endpoint. The endpoint builds the "Allow" header - * from the list of methods supported by modules. - * - * @param endpt The endpoint. - * - * @return "Allow" header, or NULL if endpoint doesn't have "Allow" header. - */ -PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ); - - -/** - * Find transaction in endpoint's transaction table by the transaction's key. - * This function normally is only used by modules. The key for a transaction - * can be created by calling #pjsip_tsx_create_key. - * - * @param endpt The endpoint instance. - * @param key Transaction key, as created with #pjsip_tsx_create_key. - * - * @return The transaction, or NULL if it's not found. - */ -PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, - const pj_str_t *key ); - -/** - * Set list of SIP proxies to be visited for all outbound request messages. - * Application can call this function to specify how outgoing request messages - * should be routed. For example, if outgoing requests should go through an - * outbound proxy, then application can specify the URL of the proxy when - * calling this function. More than one proxy can be specified, and the - * order of which proxy is specified when calling this function specifies - * the order of which proxy will be visited first by the request messages. - * - * @param endpt The endpoint instance. - * @param url_cnt Number of proxies/URLs in the array. - * @param url Array of proxy URL, which specifies the order of which - * proxy will be visited first (e.g. url[0] will be visited - * before url[1]). - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, - int url_cnt, const pj_str_t url[]); - -/** - * Get the list of "Route" header that are configured for this endpoint. - * The "Route" header specifies how outbound request messages will be sent, - * and is built when application sets the outbound proxy. - * - * @param endpt The endpoint instance. - * - * @return List of "Route" header. - */ -PJ_DECL(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ); - -/** - * @} - */ - -/* - * Internal functions. - */ -/* - * Receive transaction events from transactions and put in the event queue - * to be processed later. - */ -void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ); - -PJ_END_DECL - -#endif /* __PJSIP_SIP_ENDPOINT_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_endpoint.h 12 6/22/05 12:27a Bennylp $ */ +#ifndef __PJSIP_SIP_ENDPOINT_H__ +#define __PJSIP_SIP_ENDPOINT_H__ + +/** + * @file sip_endpoint.h + * @brief SIP Endpoint. + */ + +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP SIP Stack Core + * Implementation of core SIP protocol stack processing. + */ + +/** + * @defgroup PJSIP_ENDPT SIP Endpoint + * @ingroup PJSIP + * @brief + * Representation of SIP node instance. + * + * SIP Endpoint instance (pjsip_endpoint) can be viewed as the master/owner of + * all SIP objects in an application. It performs the following roles: + * - it manages the allocation/deallocation of memory pools for all objects. + * - it manages listeners and transports, and how they are used by transactions. + * - it owns transaction hash table. + * - it receives incoming messages from transport layer and automatically + * dispatches them to the correct transaction (or create a new one). + * - it has a single instance of timer management (timer heap). + * - it manages modules, which is the primary means of extending the library. + * - it provides single polling function for all objects and distributes events. + * - it provides SIP policy such as which outbound proxy to use for all + * outgoing SIP request messages. + * - it automatically handles incoming requests which can not be handled by + * existing modules (such as when incoming request has unsupported method). + * - and so on.. + * + * Theoritically application can have multiple instances of SIP endpoint, + * although it's not clear why application may want to do it. + * + * @{ + */ + +/** + * Create an instance of SIP endpoint from the specified pool factory. + * The pool factory reference then will be kept by the endpoint, so that future + * memory allocations by SIP components will be taken from the same pool factory. + * + * @param pf Pool factory that will be used for the lifetime of endpoint. + * + * @return the endpoint instance on success. + */ +PJ_DECL(pjsip_endpoint*) pjsip_endpt_create(pj_pool_factory *pf); + +/** + * Destroy endpoint instance. Application must make sure that all pending + * transactions have been terminated properly, because this function does not + * check for the presence of pending transactions. + * + * @param endpt The SIP endpoint to be destroyed. + */ +PJ_DECL(void) pjsip_endpt_destroy(pjsip_endpoint *endpt); + +/** + * Poll for events. Application must call this function periodically to ensure + * that all events from both transports and timer heap are handled in timely + * manner. This function, like all other endpoint functions, is thread safe, + * and application may have more than one thread concurrently calling this function. + * + * @param endpt The endpoint. + * @param max_timeout Maximum time to wait for events, or NULL to wait forever + * until event is received. + */ +PJ_DECL(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, + const pj_time_val *max_timeout); + +/** + * Dump endpoint status to the log. This will print the status to the log + * with log level 3. + * + * @param endpt The endpoint. + * @param detail If non zero, then it will dump a detailed output. + * BEWARE that this option may crash the system because + * it tries to access all memory pools. + */ +PJ_DECL(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ); + +/** + * Create pool from the endpoint. All SIP components should allocate their + * memory pool by calling this function, to make sure that the pools are + * allocated from the same pool factory. This function, like all other endpoint + * functions, is thread safe. + * + * @param endpt The SIP endpoint. + * @param pool_name Name to be assigned to the pool. + * @param initial The initial size of the pool. + * @param increment The resize size. + * @return Memory pool, or NULL on failure. + * + * @see pj_pool_create + */ +PJ_DECL(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, + const char *pool_name, + pj_size_t initial, + pj_size_t increment ); + +/** + * Return back pool to endpoint to be released back to the pool factory. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint. + * @param pool The pool to be destroyed. + */ +PJ_DECL(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, + pj_pool_t *pool ); + +/** + * Schedule timer to endpoint's timer heap. Application must poll the endpoint + * periodically (by calling #pjsip_endpt_handle_events) to ensure that the + * timer events are handled in timely manner. When the timeout for the timer + * has elapsed, the callback specified in the entry argument will be called. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint. + * @param entry The timer entry. + * @param delay The relative delay of the timer. + * @return PJ_OK (zero) if successfull. + */ +PJ_DECL(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry, + const pj_time_val *delay ); + +/** + * Cancel the previously registered timer. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint. + * @param entry The timer entry previously registered. + */ +PJ_DECL(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, + pj_timer_entry *entry ); + +/** + * Create a new transaction. After creating the transaction, application MUST + * initialize the transaction as either UAC or UAS (by calling + * #pjsip_tsx_init_uac or #pjsip_tsx_init_uas), then must register the + * transaction to endpoint with #pjsip_endpt_register_tsx. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The SIP endpoint. + * @return The new transaction, or NULL on failure. + */ +PJ_DECL(pjsip_transaction*) pjsip_endpt_create_tsx(pjsip_endpoint *endpt); + +/** + * Register the transaction to the endpoint's transaction table. + * Before the transaction is registered, it must have been initialized as + * either UAS or UAC by calling #pjsip_tsx_init_uac or #pjsip_tsx_init_uas. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The SIP endpoint. + * @param tsx The transaction. + */ +PJ_DECL(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx); + +/** + * Forcefull destroy the transaction. + * The only time where application needs to call this function is when the + * transaction fails to initialize in #pjsip_tsx_init_uac or + * #pjsip_tsx_init_uas. For other cases. the transaction will be destroyed + * automaticly by endpoint. + * + * @param endpt The endpoint. + * @param tsx The transaction to destroy. + */ +PJ_DECL(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, + pjsip_transaction *tsx); + +/** + * Create a new transmit data buffer. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt the endpoint. + * @return new transmit data. + */ +PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_tdata( pjsip_endpoint *endpt ); + +/** + * Asynchronously resolve a SIP target host or domain according to rule + * specified in RFC 3263 (Locating SIP Servers). When the resolving operation + * has completed, the callback will be called. + * + * Note: at the moment we don't have implementation of RFC 3263 yet! + * + * @param resolver The resolver engine. + * @param pool The pool to allocate resolver job. + * @param target The target specification to be resolved. + * @param token A user defined token to be passed back to callback function. + * @param cb The callback function. + */ +PJ_DECL(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb); + +/** + * Find a SIP transport suitable for sending SIP message to the specified + * address. This function will complete asynchronously when the transport is + * ready (for example, when TCP socket is connected), and when it completes, + * the callback will be called with the status of the operation. + * + * @see pjsip_transport_get + */ +PJ_DECL(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt, + pj_pool_t *pool, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + void *token, + pjsip_transport_completion_callback *cb); + +/** + * Create listener a new transport listener. A listener is transport object + * that is capable of receiving SIP messages. For UDP listener, normally + * application should use #pjsip_endpt_create_udp_listener instead if the + * application has already created the socket. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint instance. + * @param type Transport type (eg. UDP, TCP, etc.) + * @param addr The bound address of the transport. + * @param addr_name The address to be advertised in SIP messages. For example, + * the bound address can be 0.0.0.0, but the advertised address + * normally will be the IP address of the host. + * + * @return Zero if listener is created successfully. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt, + pjsip_transport_type_e type, + pj_sockaddr_in *addr, + const pj_sockaddr_in *addr_name); + +/** + * Create UDP listener. For UDP, normally the application would create the + * socket by itself (for STUN purpose), then it can register the socket as + * listener by calling this function. + * This function, like all other endpoint functions, is thread safe. + * + * @param endpt The endpoint instance. + * @param sock The socket handle. + * @param addr_name The address to be advertised in SIP message. If the socket + * has been resolved with STUN, then application may specify + * the mapped address in this parameter. + * + * @return Zero if listener is created successfully. + */ +PJ_DECL(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt, + pj_sock_t sock, + const pj_sockaddr_in *addr_name); + +/** + * Get additional headers to be put in outgoing request message. + * This function is normally called by transaction layer when sending outgoing + * requests. + * + * @param endpt The endpoint. + * + * @return List of additional headers to be put in outgoing requests. + */ +PJ_DECL(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt); + +/** + * Get "Allow" header from endpoint. The endpoint builds the "Allow" header + * from the list of methods supported by modules. + * + * @param endpt The endpoint. + * + * @return "Allow" header, or NULL if endpoint doesn't have "Allow" header. + */ +PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ); + + +/** + * Find transaction in endpoint's transaction table by the transaction's key. + * This function normally is only used by modules. The key for a transaction + * can be created by calling #pjsip_tsx_create_key. + * + * @param endpt The endpoint instance. + * @param key Transaction key, as created with #pjsip_tsx_create_key. + * + * @return The transaction, or NULL if it's not found. + */ +PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, + const pj_str_t *key ); + +/** + * Set list of SIP proxies to be visited for all outbound request messages. + * Application can call this function to specify how outgoing request messages + * should be routed. For example, if outgoing requests should go through an + * outbound proxy, then application can specify the URL of the proxy when + * calling this function. More than one proxy can be specified, and the + * order of which proxy is specified when calling this function specifies + * the order of which proxy will be visited first by the request messages. + * + * @param endpt The endpoint instance. + * @param url_cnt Number of proxies/URLs in the array. + * @param url Array of proxy URL, which specifies the order of which + * proxy will be visited first (e.g. url[0] will be visited + * before url[1]). + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, + int url_cnt, const pj_str_t url[]); + +/** + * Get the list of "Route" header that are configured for this endpoint. + * The "Route" header specifies how outbound request messages will be sent, + * and is built when application sets the outbound proxy. + * + * @param endpt The endpoint instance. + * + * @return List of "Route" header. + */ +PJ_DECL(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ); + +/** + * @} + */ + +/* + * Internal functions. + */ +/* + * Receive transaction events from transactions and put in the event queue + * to be processed later. + */ +void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ); + +PJ_END_DECL + +#endif /* __PJSIP_SIP_ENDPOINT_H__ */ + diff --git a/pjsip/src/pjsip/sip_event.h b/pjsip/src/pjsip/sip_event.h index 043c78452..c326822b2 100644 --- a/pjsip/src/pjsip/sip_event.h +++ b/pjsip/src/pjsip/sip_event.h @@ -1,131 +1,131 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_event.h 5 6/17/05 11:16p Bennylp $ */ -#ifndef __PJSIP_SIP_EVENT_H__ -#define __PJSIP_SIP_EVENT_H__ - -/** - * @file sip_event.h - * @brief SIP Event - */ - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_EVENT SIP Event - * @ingroup PJSIP - * @{ - */ -#include - - -/** - * Event IDs. - */ -typedef enum pjsip_event_id_e -{ - /** Unidentified event. */ - PJSIP_EVENT_UNIDENTIFIED, - - /** Timer event, normally only used internally in transaction. */ - PJSIP_EVENT_TIMER, - - /** Message transmission event. */ - PJSIP_EVENT_TX_MSG, - - /** Message received event. */ - PJSIP_EVENT_RX_MSG, - - /** Transport error event. */ - PJSIP_EVENT_TRANSPORT_ERROR, - - /** Transaction state changed event. */ - PJSIP_EVENT_TSX_STATE_CHANGED, - - /** 2xx response received event. */ - PJSIP_EVENT_RX_200_RESPONSE, - - /** ACK request received event. */ - PJSIP_EVENT_RX_ACK_MSG, - - /** Message discarded event. */ - PJSIP_EVENT_DISCARD_MSG, - - /** Indicates that the event was triggered by user action. */ - PJSIP_EVENT_USER, - - /** On before transmitting message. */ - PJSIP_EVENT_BEFORE_TX, - -} pjsip_event_id_e; - - -/** - * \struct - * \brief Event descriptor to fully identify a SIP event. - * - * Events are the only way for a lower layer object to inform something - * to higher layer objects. Normally this is achieved by means of callback, - * i.e. the higher layer objects register a callback to handle the event on - * the lower layer objects. - * - * This event descriptor is used for example by transactions, to inform - * endpoint about events, and by transports, to inform endpoint about - * unexpected transport error. - */ -struct pjsip_event -{ - /** This is necessary so that we can put events as a list. */ - PJ_DECL_LIST_MEMBER(struct pjsip_event) - - /** The event type, can be any value of \b pjsip_event_id_e. - * @see pjsip_event_id_e - */ - pjsip_event_id_e type; - - /** This field determines what is the content of \b src (source data). - */ - pjsip_event_id_e src_type; - - /** Source data, which content is dependent on \b src_type. - * - if src_type==PJSIP_EVENT_RX_MSG, src.rdata is valid. - * - if src_type==PJSIP_EVENT_TX_MSG, src.tdata is valid. - * - if src_type==PJSIP_EVENT_TIMER, src.timer is valid. - */ - union - { - pjsip_rx_data *rdata; - pjsip_tx_data *tdata; - pj_timer_entry *timer; - void *data; - unsigned long udata; - } src; - - /** The object that generates this event. */ - union - { - pjsip_transaction *tsx; - void *ptr; - unsigned long udata; - } obj; - - /** Other data. */ - union - { - long long_data; - void * ptr_data; - } data; -}; - -/** - * Get the event string from the event ID. - * @param e the event ID. - * @notes defined in sip_misc.c - */ -PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_SIP_EVENT_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip/sip_event.h 5 6/17/05 11:16p Bennylp $ */ +#ifndef __PJSIP_SIP_EVENT_H__ +#define __PJSIP_SIP_EVENT_H__ + +/** + * @file sip_event.h + * @brief SIP Event + */ + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_EVENT SIP Event + * @ingroup PJSIP + * @{ + */ +#include + + +/** + * Event IDs. + */ +typedef enum pjsip_event_id_e +{ + /** Unidentified event. */ + PJSIP_EVENT_UNIDENTIFIED, + + /** Timer event, normally only used internally in transaction. */ + PJSIP_EVENT_TIMER, + + /** Message transmission event. */ + PJSIP_EVENT_TX_MSG, + + /** Message received event. */ + PJSIP_EVENT_RX_MSG, + + /** Transport error event. */ + PJSIP_EVENT_TRANSPORT_ERROR, + + /** Transaction state changed event. */ + PJSIP_EVENT_TSX_STATE_CHANGED, + + /** 2xx response received event. */ + PJSIP_EVENT_RX_200_RESPONSE, + + /** ACK request received event. */ + PJSIP_EVENT_RX_ACK_MSG, + + /** Message discarded event. */ + PJSIP_EVENT_DISCARD_MSG, + + /** Indicates that the event was triggered by user action. */ + PJSIP_EVENT_USER, + + /** On before transmitting message. */ + PJSIP_EVENT_BEFORE_TX, + +} pjsip_event_id_e; + + +/** + * \struct + * \brief Event descriptor to fully identify a SIP event. + * + * Events are the only way for a lower layer object to inform something + * to higher layer objects. Normally this is achieved by means of callback, + * i.e. the higher layer objects register a callback to handle the event on + * the lower layer objects. + * + * This event descriptor is used for example by transactions, to inform + * endpoint about events, and by transports, to inform endpoint about + * unexpected transport error. + */ +struct pjsip_event +{ + /** This is necessary so that we can put events as a list. */ + PJ_DECL_LIST_MEMBER(struct pjsip_event) + + /** The event type, can be any value of \b pjsip_event_id_e. + * @see pjsip_event_id_e + */ + pjsip_event_id_e type; + + /** This field determines what is the content of \b src (source data). + */ + pjsip_event_id_e src_type; + + /** Source data, which content is dependent on \b src_type. + * - if src_type==PJSIP_EVENT_RX_MSG, src.rdata is valid. + * - if src_type==PJSIP_EVENT_TX_MSG, src.tdata is valid. + * - if src_type==PJSIP_EVENT_TIMER, src.timer is valid. + */ + union + { + pjsip_rx_data *rdata; + pjsip_tx_data *tdata; + pj_timer_entry *timer; + void *data; + unsigned long udata; + } src; + + /** The object that generates this event. */ + union + { + pjsip_transaction *tsx; + void *ptr; + unsigned long udata; + } obj; + + /** Other data. */ + union + { + long long_data; + void * ptr_data; + } data; +}; + +/** + * Get the event string from the event ID. + * @param e the event ID. + * @notes defined in sip_misc.c + */ +PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_EVENT_H__ */ diff --git a/pjsip/src/pjsip/sip_misc.c b/pjsip/src/pjsip/sip_misc.c index f82ca6844..ec36e0907 100644 --- a/pjsip/src/pjsip/sip_misc.c +++ b/pjsip/src/pjsip/sip_misc.c @@ -1,678 +1,678 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_misc.c 15 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_THIS "endpoint..." - -static const char *event_str[] = -{ - "UNIDENTIFIED", - "TIMER", - "TX_MSG", - "RX_MSG", - "TRANSPORT_ERROR", - "TSX_STATE", - "RX_2XX_RESPONSE", - "RX_ACK", - "DISCARD_MSG", - "USER", - "BEFORE_TX", -}; - -static pj_str_t str_TEXT = { "text", 4}, - str_PLAIN = { "plain", 5 }; -static int aux_mod_id; - -struct aux_tsx_data -{ - void *token; - void (*cb)(void*,pjsip_event*); -}; - -static pj_status_t aux_tsx_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - PJ_UNUSED_ARG(endpt) - PJ_UNUSED_ARG(mod) - - aux_mod_id = id; - return 0; -} - -static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_transaction *tsx = event->obj.tsx; - struct aux_tsx_data *tsx_data; - - PJ_UNUSED_ARG(mod) - - if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED) - return; - if (tsx == NULL) - return; - if (tsx->module_data[aux_mod_id] == NULL) - return; - if (tsx->status_code < 200) - return; - - /* Call the callback, if any, and prevent the callback to be called again - * by clearing the transaction's module_data. - */ - tsx_data = tsx->module_data[aux_mod_id]; - tsx->module_data[aux_mod_id] = NULL; - - if (tsx_data->cb) { - (*tsx_data->cb)(tsx_data->token, event); - } -} - -pjsip_module aux_tsx_module = -{ - { "Aux-Tsx", 7}, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - NULL, /* Arbitrary data. */ - 0, /* Number of methods supported (none). */ - { 0 }, /* Array of methods (none) */ - &aux_tsx_init, /* init_module() */ - NULL, /* start_module() */ - NULL, /* deinit_module() */ - &aux_tsx_handler, /* tsx_handler() */ -}; - -PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, - pjsip_tx_data *tdata, - int timeout, - void *token, - void (*cb)(void*,pjsip_event*)) -{ - pjsip_transaction *tsx; - struct aux_tsx_data *tsx_data; - - tsx = pjsip_endpt_create_tsx(endpt); - if (!tsx) { - pjsip_tx_data_dec_ref(tdata); - return -1; - } - - tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data)); - tsx_data->token = token; - tsx_data->cb = cb; - tsx->module_data[aux_mod_id] = tsx_data; - - if (pjsip_tsx_init_uac(tsx, tdata) != 0) { - pjsip_endpt_destroy_tsx(endpt, tsx); - pjsip_tx_data_dec_ref(tdata); - return -1; - } - - pjsip_endpt_register_tsx(endpt, tsx); - pjsip_tx_data_invalidate_msg(tdata); - pjsip_tsx_on_tx_msg(tsx, tdata); - pjsip_tx_data_dec_ref(tdata); - return 0; -} - -/* - * Initialize transmit data (msg) with the headers and optional body. - * This will just put the headers in the message as it is. Be carefull - * when calling this function because once a header is put in a message, - * it CAN NOT be put in other message until the first message is deleted, - * because the way the header is put in the list. - * That's why the session will shallow_clone it's headers before calling - * this function. - */ -static void init_request_throw( pjsip_tx_data *tdata, - pjsip_method *method, - pjsip_uri *param_target, - pjsip_from_hdr *param_from, - pjsip_to_hdr *param_to, - pjsip_contact_hdr *param_contact, - pjsip_cid_hdr *param_call_id, - pjsip_cseq_hdr *param_cseq, - const pj_str_t *param_text) -{ - pjsip_msg *msg; - pjsip_msg_body *body; - - /* Create the message. */ - msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); - - /* Init request URI. */ - pj_memcpy(&msg->line.req.method, method, sizeof(*method)); - msg->line.req.uri = param_target; - - /* Add From header. */ - if (param_from->tag.slen == 0) - pj_create_unique_string(tdata->pool, ¶m_from->tag); - pjsip_msg_add_hdr(msg, (void*)param_from); - - /* Add To header. */ - pjsip_msg_add_hdr(msg, (void*)param_to); - - /* Add Contact header. */ - if (param_contact) { - pjsip_msg_add_hdr(msg, (void*)param_contact); - } - - /* Add Call-ID header. */ - pjsip_msg_add_hdr(msg, (void*)param_call_id); - - /* Add CSeq header. */ - pjsip_msg_add_hdr(msg, (void*)param_cseq); - - /* Create message body. */ - if (param_text) { - body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body)); - body->content_type.type = str_TEXT; - body->content_type.subtype = str_PLAIN; - body->data = pj_pool_alloc(tdata->pool, param_text->slen ); - pj_memcpy(body->data, param_text->ptr, param_text->slen); - body->len = param_text->slen; - body->print_body = &pjsip_print_text_body; - msg->body = body; - } -} - -/* - * Create arbitrary request. - */ -PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_request( pjsip_endpoint *endpt, - const pjsip_method *method, - const pj_str_t *param_target, - const pj_str_t *param_from, - const pj_str_t *param_to, - const pj_str_t *param_contact, - const pj_str_t *param_call_id, - int param_cseq, - const pj_str_t *param_text) -{ - pjsip_uri *target; - pjsip_tx_data *tdata; - pjsip_from_hdr *from; - pjsip_to_hdr *to; - pjsip_contact_hdr *contact; - pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */ - pjsip_cid_hdr *call_id; - pj_str_t tmp; - PJ_USE_EXCEPTION; - - PJ_LOG(5,(LOG_THIS, "Entering pjsip_endpt_create_request()")); - - tdata = pjsip_endpt_create_tdata(endpt); - if (!tdata) - return NULL; - - /* Init reference counter to 1. */ - pjsip_tx_data_add_ref(tdata); - - PJ_TRY { - /* Request target. */ - pj_strdup_with_null(tdata->pool, &tmp, param_target); - target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0); - if (target == NULL) { - PJ_LOG(4,(LOG_THIS, "Error creating request: invalid target %s", - tmp.ptr)); - goto on_error; - } - - /* From */ - from = pjsip_from_hdr_create(tdata->pool); - pj_strdup_with_null(tdata->pool, &tmp, param_from); - from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (from->uri == NULL) { - PJ_LOG(4,(LOG_THIS, "Error creating request: invalid 'From' URI '%s'", - tmp.ptr)); - goto on_error; - } - pj_create_unique_string(tdata->pool, &from->tag); - - /* To */ - to = pjsip_to_hdr_create(tdata->pool); - pj_strdup_with_null(tdata->pool, &tmp, param_to); - to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (to->uri == NULL) { - PJ_LOG(4,(LOG_THIS, "Error creating request: invalid 'To' URI '%s'", - tmp.ptr)); - goto on_error; - } - - /* Contact. */ - if (param_contact) { - contact = pjsip_contact_hdr_create(tdata->pool); - pj_strdup_with_null(tdata->pool, &tmp, param_contact); - contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (contact->uri == NULL) { - PJ_LOG(4,(LOG_THIS, - "Error creating request: invalid 'Contact' URI '%s'", - tmp.ptr)); - goto on_error; - } - } else { - contact = NULL; - } - - /* Call-ID */ - call_id = pjsip_cid_hdr_create(tdata->pool); - if (param_call_id != NULL && param_call_id->slen) - pj_strdup(tdata->pool, &call_id->id, param_call_id); - else - pj_create_unique_string(tdata->pool, &call_id->id); - - /* CSeq */ - cseq = pjsip_cseq_hdr_create(tdata->pool); - if (param_cseq >= 0) - cseq->cseq = param_cseq; - else - cseq->cseq = pj_rand() & 0xFFFF; - - /* Method */ - pjsip_method_copy(tdata->pool, &cseq->method, method); - - /* Create the request. */ - init_request_throw( tdata, &cseq->method, target, from, to, contact, - call_id, cseq, param_text); - } - PJ_DEFAULT { - PJ_LOG(4,(LOG_THIS, "Caught exception %d when creating request", - PJ_GET_EXCEPTION())); - goto on_error; - } - PJ_END - - PJ_LOG(4,(LOG_THIS, "Request %s (%d %.*s) created.", - tdata->obj_name, - cseq->cseq, - cseq->method.name.slen, - cseq->method.name.ptr)); - - return tdata; - -on_error: - pjsip_tx_data_dec_ref(tdata); - return NULL; -} - -PJ_DEF(pjsip_tx_data*) -pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, - const pjsip_method *method, - const pjsip_uri *param_target, - const pjsip_from_hdr *param_from, - const pjsip_to_hdr *param_to, - const pjsip_contact_hdr *param_contact, - const pjsip_cid_hdr *param_call_id, - int param_cseq, - const pj_str_t *param_text ) -{ - pjsip_uri *target; - pjsip_tx_data *tdata; - pjsip_from_hdr *from; - pjsip_to_hdr *to; - pjsip_contact_hdr *contact; - pjsip_cid_hdr *call_id; - pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */ - PJ_USE_EXCEPTION; - - PJ_LOG(5,(LOG_THIS, "Entering pjsip_endpt_create_request_from_hdr()")); - - tdata = pjsip_endpt_create_tdata(endpt); - if (!tdata) - return NULL; - - pjsip_tx_data_add_ref(tdata); - - PJ_TRY { - target = pjsip_uri_clone(tdata->pool, param_target); - from = pjsip_hdr_shallow_clone(tdata->pool, param_from); - pjsip_fromto_set_from(from); - to = pjsip_hdr_shallow_clone(tdata->pool, param_to); - pjsip_fromto_set_to(to); - if (param_contact) - contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact); - else - contact = NULL; - call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id); - cseq = pjsip_cseq_hdr_create(tdata->pool); - if (param_cseq >= 0) - cseq->cseq = param_cseq; - else - cseq->cseq = pj_rand() % 0xFFFF; - pjsip_method_copy(tdata->pool, &cseq->method, method); - - init_request_throw(tdata, &cseq->method, target, from, to, contact, - call_id, cseq, param_text); - } - PJ_DEFAULT { - PJ_LOG(4,(LOG_THIS, "Caught exception %d when creating request", - PJ_GET_EXCEPTION())); - goto on_error; - } - PJ_END; - - PJ_LOG(4,(LOG_THIS, "Request %s (%d %.*s) created.", - tdata->obj_name, - cseq->cseq, - cseq->method.name.slen, - cseq->method.name.ptr)); - return tdata; - -on_error: - pjsip_tx_data_dec_ref(tdata); - return NULL; -} - -/* - * Construct a minimal response message for the received request. - */ -PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_response( pjsip_endpoint *endpt, - const pjsip_rx_data *rdata, - int code) -{ - pjsip_tx_data *tdata; - pjsip_msg *msg, *req_msg; - pjsip_hdr *hdr; - pjsip_via_hdr *via; - pjsip_rr_hdr *rr; - - /* rdata must be a request message. */ - req_msg = rdata->msg; - pj_assert(req_msg->type == PJSIP_REQUEST_MSG); - - /* Log this action. */ - PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_response(rdata=%p, code=%d)", - rdata, code)); - - /* Create a new transmit buffer. */ - tdata = pjsip_endpt_create_tdata( endpt ); - if (!tdata) - return NULL; - - /* Create new response message. */ - tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG); - - /* Set status code and reason text. */ - msg->line.status.code = code; - msg->line.status.reason = *pjsip_get_status_text(code); - - /* Set TX data attributes. */ - tdata->rx_timestamp = rdata->timestamp; - - /* Copy all the via headers, in order. */ - via = rdata->via; - while (via) { - pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via)); - via = via->next; - if (via != (void*)&req_msg->hdr) - via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via); - else - break; - } - - /* Copy all Record-Route headers, in order. */ - rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL); - while (rr) { - pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr)); - rr = rr->next; - if (rr != (void*)&req_msg->hdr) - rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr); - else - break; - } - - /* Copy Call-ID header. */ - hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL); - pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr)); - - /* Copy From header. */ - hdr = pjsip_hdr_clone(tdata->pool, rdata->from); - pjsip_msg_add_hdr( msg, hdr); - - /* Copy To header. */ - hdr = pjsip_hdr_clone(tdata->pool, rdata->to); - pjsip_msg_add_hdr( msg, hdr); - - /* Copy CSeq header. */ - hdr = pjsip_hdr_clone(tdata->pool, rdata->cseq); - pjsip_msg_add_hdr( msg, hdr); - - /* All done. */ - return tdata; -} - - -/* - * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of - * RFC3261). Note that the generation of ACK for 2xx response is different, - * and one must not use this function to generate such ACK. - */ -PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt, - pjsip_tx_data *tdata, - const pjsip_rx_data *rdata ) -{ - pjsip_msg *ack_msg, *invite_msg; - pjsip_to_hdr *to; - pjsip_from_hdr *from; - pjsip_cseq_hdr *cseq; - pjsip_hdr *hdr; - - /* Make compiler happy. */ - PJ_UNUSED_ARG(endpt); - - /* rdata must be a final response. */ - pj_assert(rdata->msg->type==PJSIP_RESPONSE_MSG && - rdata->msg->line.status.code >= 300); - - /* Log this action. */ - PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_ack(rdata=%p)", rdata)); - - /* Create new request message. */ - ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); - pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD ); - - /* The original INVITE message. */ - invite_msg = tdata->msg; - - /* Copy Request-Uri from the original INVITE. */ - ack_msg->line.req.uri = invite_msg->line.req.uri; - - /* Copy Call-ID from the original INVITE */ - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL); - pjsip_msg_add_hdr( ack_msg, hdr ); - - /* Copy From header from the original INVITE. */ - from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg, - PJSIP_H_FROM, NULL); - pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from ); - - /* Copy To header from the original INVITE. */ - to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg, - PJSIP_H_TO, NULL); - pj_strdup(tdata->pool, &to->tag, &rdata->to_tag); - pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to ); - - /* Must contain single Via, just as the original INVITE. */ - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL); - pjsip_msg_insert_first_hdr( ack_msg, hdr ); - - /* Must have the same CSeq value as the original INVITE, but method - * changed to ACK - */ - cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg, - PJSIP_H_CSEQ, NULL); - pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD ); - pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq ); - - /* If the original INVITE has Route headers, those header fields MUST - * appear in the ACK. - */ - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); - while (hdr != NULL) { - pjsip_msg_add_hdr( ack_msg, hdr ); - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); - } - - /* Set the message in the "tdata" to point to the ACK message. */ - tdata->msg = ack_msg; - - /* Reset transmit packet buffer, to force 're-printing' of message. */ - tdata->buf.cur = tdata->buf.start; - - /* We're done. - * "tdata" parameter now contains the ACK message. - */ -} - - -/* - * Construct CANCEL request for the previously sent request, according to - * chapter 9.1 of RFC3261. - */ -PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, - pjsip_tx_data *req_tdata ) -{ - pjsip_msg *req_msg; /* the original request. */ - pjsip_tx_data *cancel_tdata; - pjsip_msg *cancel_msg; - pjsip_hdr *hdr; - pjsip_cseq_hdr *req_cseq, *cseq; - pjsip_uri *req_uri; - - /* Log this action. */ - PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata)); - - /* Get the original request. */ - req_msg = req_tdata->msg; - - /* The transmit buffer must INVITE request. */ - pj_assert(req_msg->type == PJSIP_REQUEST_MSG && - req_msg->line.req.method.id == PJSIP_INVITE_METHOD ); - - /* Create new transmit buffer. */ - cancel_tdata = pjsip_endpt_create_tdata( endpt ); - if (!cancel_tdata) { - return NULL; - } - - /* Create CANCEL request message. */ - cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG); - cancel_tdata->msg = cancel_msg; - - /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are - * copied from the original request. - */ - /* Set request line. */ - pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD); - req_uri = req_msg->line.req.uri; - cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri); - - /* Copy Call-ID */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL); - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* Copy From header. */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL); - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* Copy To header. */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL); - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* Create new CSeq with equal number, but method set to CANCEL. */ - req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL); - cseq = pjsip_cseq_hdr_create(cancel_tdata->pool); - cseq->cseq = req_cseq->cseq; - pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD); - pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq); - - /* Must only have single Via which matches the top-most Via in the - * request being cancelled. - */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL); - pjsip_msg_insert_first_hdr(cancel_msg, - pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* If the original request has Route header, the CANCEL request must also - * has exactly the same. - * Copy "Route" header from the request. - */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL); - while (hdr != NULL) { - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - hdr = hdr->next; - if (hdr != &cancel_msg->hdr) - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr); - else - break; - } - - /* Done. - * Return the transmit buffer containing the CANCEL request. - */ - return cancel_tdata; -} - -/* Get the address parameters (host, port, flag, TTL, etc) to send the - * response. - */ -PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, - const pjsip_transport_t *req_transport, - const pjsip_via_hdr *via, - pjsip_host_port *send_addr) -{ - /* Determine the destination address (section 18.2.2): - * - for TCP, SCTP, or TLS, send the response using the transport where - * the request was received. - * - if maddr parameter is present, send to this address using the port - * in sent-by or 5060. If multicast is used, the TTL in the Via must - * be used, or 1 if ttl parameter is not present. - * - otherwise if received parameter is present, set to this address. - * - otherwise send to the address in sent-by. - */ - send_addr->flag = pjsip_transport_get_flag(req_transport); - send_addr->type = pjsip_transport_get_type(req_transport); - - if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) { - const pj_sockaddr_in *remote_addr; - remote_addr = pjsip_transport_get_remote_addr(req_transport); - pj_strdup2(pool, &send_addr->host, - pj_sockaddr_get_str_addr(remote_addr)); - send_addr->port = pj_sockaddr_get_port(remote_addr); - - } else { - /* Set the host part */ - if (via->maddr_param.slen) { - pj_strdup(pool, &send_addr->host, &via->maddr_param); - } else if (via->recvd_param.slen) { - pj_strdup(pool, &send_addr->host, &via->recvd_param); - } else { - pj_strdup(pool, &send_addr->host, &via->sent_by.host); - } - - /* Set the port */ - send_addr->port = via->sent_by.port; - } - - return PJ_SUCCESS; -} - -/* - * Get the event string from the event ID. - */ -PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e) -{ - return event_str[e]; -} - +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_misc.c 15 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_THIS "endpoint..." + +static const char *event_str[] = +{ + "UNIDENTIFIED", + "TIMER", + "TX_MSG", + "RX_MSG", + "TRANSPORT_ERROR", + "TSX_STATE", + "RX_2XX_RESPONSE", + "RX_ACK", + "DISCARD_MSG", + "USER", + "BEFORE_TX", +}; + +static pj_str_t str_TEXT = { "text", 4}, + str_PLAIN = { "plain", 5 }; +static int aux_mod_id; + +struct aux_tsx_data +{ + void *token; + void (*cb)(void*,pjsip_event*); +}; + +static pj_status_t aux_tsx_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + PJ_UNUSED_ARG(endpt) + PJ_UNUSED_ARG(mod) + + aux_mod_id = id; + return 0; +} + +static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_transaction *tsx = event->obj.tsx; + struct aux_tsx_data *tsx_data; + + PJ_UNUSED_ARG(mod) + + if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED) + return; + if (tsx == NULL) + return; + if (tsx->module_data[aux_mod_id] == NULL) + return; + if (tsx->status_code < 200) + return; + + /* Call the callback, if any, and prevent the callback to be called again + * by clearing the transaction's module_data. + */ + tsx_data = tsx->module_data[aux_mod_id]; + tsx->module_data[aux_mod_id] = NULL; + + if (tsx_data->cb) { + (*tsx_data->cb)(tsx_data->token, event); + } +} + +pjsip_module aux_tsx_module = +{ + { "Aux-Tsx", 7}, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + NULL, /* Arbitrary data. */ + 0, /* Number of methods supported (none). */ + { 0 }, /* Array of methods (none) */ + &aux_tsx_init, /* init_module() */ + NULL, /* start_module() */ + NULL, /* deinit_module() */ + &aux_tsx_handler, /* tsx_handler() */ +}; + +PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + int timeout, + void *token, + void (*cb)(void*,pjsip_event*)) +{ + pjsip_transaction *tsx; + struct aux_tsx_data *tsx_data; + + tsx = pjsip_endpt_create_tsx(endpt); + if (!tsx) { + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data)); + tsx_data->token = token; + tsx_data->cb = cb; + tsx->module_data[aux_mod_id] = tsx_data; + + if (pjsip_tsx_init_uac(tsx, tdata) != 0) { + pjsip_endpt_destroy_tsx(endpt, tsx); + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + pjsip_endpt_register_tsx(endpt, tsx); + pjsip_tx_data_invalidate_msg(tdata); + pjsip_tsx_on_tx_msg(tsx, tdata); + pjsip_tx_data_dec_ref(tdata); + return 0; +} + +/* + * Initialize transmit data (msg) with the headers and optional body. + * This will just put the headers in the message as it is. Be carefull + * when calling this function because once a header is put in a message, + * it CAN NOT be put in other message until the first message is deleted, + * because the way the header is put in the list. + * That's why the session will shallow_clone it's headers before calling + * this function. + */ +static void init_request_throw( pjsip_tx_data *tdata, + pjsip_method *method, + pjsip_uri *param_target, + pjsip_from_hdr *param_from, + pjsip_to_hdr *param_to, + pjsip_contact_hdr *param_contact, + pjsip_cid_hdr *param_call_id, + pjsip_cseq_hdr *param_cseq, + const pj_str_t *param_text) +{ + pjsip_msg *msg; + pjsip_msg_body *body; + + /* Create the message. */ + msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); + + /* Init request URI. */ + pj_memcpy(&msg->line.req.method, method, sizeof(*method)); + msg->line.req.uri = param_target; + + /* Add From header. */ + if (param_from->tag.slen == 0) + pj_create_unique_string(tdata->pool, ¶m_from->tag); + pjsip_msg_add_hdr(msg, (void*)param_from); + + /* Add To header. */ + pjsip_msg_add_hdr(msg, (void*)param_to); + + /* Add Contact header. */ + if (param_contact) { + pjsip_msg_add_hdr(msg, (void*)param_contact); + } + + /* Add Call-ID header. */ + pjsip_msg_add_hdr(msg, (void*)param_call_id); + + /* Add CSeq header. */ + pjsip_msg_add_hdr(msg, (void*)param_cseq); + + /* Create message body. */ + if (param_text) { + body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body)); + body->content_type.type = str_TEXT; + body->content_type.subtype = str_PLAIN; + body->data = pj_pool_alloc(tdata->pool, param_text->slen ); + pj_memcpy(body->data, param_text->ptr, param_text->slen); + body->len = param_text->slen; + body->print_body = &pjsip_print_text_body; + msg->body = body; + } +} + +/* + * Create arbitrary request. + */ +PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_request( pjsip_endpoint *endpt, + const pjsip_method *method, + const pj_str_t *param_target, + const pj_str_t *param_from, + const pj_str_t *param_to, + const pj_str_t *param_contact, + const pj_str_t *param_call_id, + int param_cseq, + const pj_str_t *param_text) +{ + pjsip_uri *target; + pjsip_tx_data *tdata; + pjsip_from_hdr *from; + pjsip_to_hdr *to; + pjsip_contact_hdr *contact; + pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */ + pjsip_cid_hdr *call_id; + pj_str_t tmp; + PJ_USE_EXCEPTION; + + PJ_LOG(5,(LOG_THIS, "Entering pjsip_endpt_create_request()")); + + tdata = pjsip_endpt_create_tdata(endpt); + if (!tdata) + return NULL; + + /* Init reference counter to 1. */ + pjsip_tx_data_add_ref(tdata); + + PJ_TRY { + /* Request target. */ + pj_strdup_with_null(tdata->pool, &tmp, param_target); + target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0); + if (target == NULL) { + PJ_LOG(4,(LOG_THIS, "Error creating request: invalid target %s", + tmp.ptr)); + goto on_error; + } + + /* From */ + from = pjsip_from_hdr_create(tdata->pool); + pj_strdup_with_null(tdata->pool, &tmp, param_from); + from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (from->uri == NULL) { + PJ_LOG(4,(LOG_THIS, "Error creating request: invalid 'From' URI '%s'", + tmp.ptr)); + goto on_error; + } + pj_create_unique_string(tdata->pool, &from->tag); + + /* To */ + to = pjsip_to_hdr_create(tdata->pool); + pj_strdup_with_null(tdata->pool, &tmp, param_to); + to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (to->uri == NULL) { + PJ_LOG(4,(LOG_THIS, "Error creating request: invalid 'To' URI '%s'", + tmp.ptr)); + goto on_error; + } + + /* Contact. */ + if (param_contact) { + contact = pjsip_contact_hdr_create(tdata->pool); + pj_strdup_with_null(tdata->pool, &tmp, param_contact); + contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (contact->uri == NULL) { + PJ_LOG(4,(LOG_THIS, + "Error creating request: invalid 'Contact' URI '%s'", + tmp.ptr)); + goto on_error; + } + } else { + contact = NULL; + } + + /* Call-ID */ + call_id = pjsip_cid_hdr_create(tdata->pool); + if (param_call_id != NULL && param_call_id->slen) + pj_strdup(tdata->pool, &call_id->id, param_call_id); + else + pj_create_unique_string(tdata->pool, &call_id->id); + + /* CSeq */ + cseq = pjsip_cseq_hdr_create(tdata->pool); + if (param_cseq >= 0) + cseq->cseq = param_cseq; + else + cseq->cseq = pj_rand() & 0xFFFF; + + /* Method */ + pjsip_method_copy(tdata->pool, &cseq->method, method); + + /* Create the request. */ + init_request_throw( tdata, &cseq->method, target, from, to, contact, + call_id, cseq, param_text); + } + PJ_DEFAULT { + PJ_LOG(4,(LOG_THIS, "Caught exception %d when creating request", + PJ_GET_EXCEPTION())); + goto on_error; + } + PJ_END + + PJ_LOG(4,(LOG_THIS, "Request %s (%d %.*s) created.", + tdata->obj_name, + cseq->cseq, + cseq->method.name.slen, + cseq->method.name.ptr)); + + return tdata; + +on_error: + pjsip_tx_data_dec_ref(tdata); + return NULL; +} + +PJ_DEF(pjsip_tx_data*) +pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + const pjsip_method *method, + const pjsip_uri *param_target, + const pjsip_from_hdr *param_from, + const pjsip_to_hdr *param_to, + const pjsip_contact_hdr *param_contact, + const pjsip_cid_hdr *param_call_id, + int param_cseq, + const pj_str_t *param_text ) +{ + pjsip_uri *target; + pjsip_tx_data *tdata; + pjsip_from_hdr *from; + pjsip_to_hdr *to; + pjsip_contact_hdr *contact; + pjsip_cid_hdr *call_id; + pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */ + PJ_USE_EXCEPTION; + + PJ_LOG(5,(LOG_THIS, "Entering pjsip_endpt_create_request_from_hdr()")); + + tdata = pjsip_endpt_create_tdata(endpt); + if (!tdata) + return NULL; + + pjsip_tx_data_add_ref(tdata); + + PJ_TRY { + target = pjsip_uri_clone(tdata->pool, param_target); + from = pjsip_hdr_shallow_clone(tdata->pool, param_from); + pjsip_fromto_set_from(from); + to = pjsip_hdr_shallow_clone(tdata->pool, param_to); + pjsip_fromto_set_to(to); + if (param_contact) + contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact); + else + contact = NULL; + call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id); + cseq = pjsip_cseq_hdr_create(tdata->pool); + if (param_cseq >= 0) + cseq->cseq = param_cseq; + else + cseq->cseq = pj_rand() % 0xFFFF; + pjsip_method_copy(tdata->pool, &cseq->method, method); + + init_request_throw(tdata, &cseq->method, target, from, to, contact, + call_id, cseq, param_text); + } + PJ_DEFAULT { + PJ_LOG(4,(LOG_THIS, "Caught exception %d when creating request", + PJ_GET_EXCEPTION())); + goto on_error; + } + PJ_END; + + PJ_LOG(4,(LOG_THIS, "Request %s (%d %.*s) created.", + tdata->obj_name, + cseq->cseq, + cseq->method.name.slen, + cseq->method.name.ptr)); + return tdata; + +on_error: + pjsip_tx_data_dec_ref(tdata); + return NULL; +} + +/* + * Construct a minimal response message for the received request. + */ +PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_response( pjsip_endpoint *endpt, + const pjsip_rx_data *rdata, + int code) +{ + pjsip_tx_data *tdata; + pjsip_msg *msg, *req_msg; + pjsip_hdr *hdr; + pjsip_via_hdr *via; + pjsip_rr_hdr *rr; + + /* rdata must be a request message. */ + req_msg = rdata->msg; + pj_assert(req_msg->type == PJSIP_REQUEST_MSG); + + /* Log this action. */ + PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_response(rdata=%p, code=%d)", + rdata, code)); + + /* Create a new transmit buffer. */ + tdata = pjsip_endpt_create_tdata( endpt ); + if (!tdata) + return NULL; + + /* Create new response message. */ + tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG); + + /* Set status code and reason text. */ + msg->line.status.code = code; + msg->line.status.reason = *pjsip_get_status_text(code); + + /* Set TX data attributes. */ + tdata->rx_timestamp = rdata->timestamp; + + /* Copy all the via headers, in order. */ + via = rdata->via; + while (via) { + pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via)); + via = via->next; + if (via != (void*)&req_msg->hdr) + via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via); + else + break; + } + + /* Copy all Record-Route headers, in order. */ + rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL); + while (rr) { + pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr)); + rr = rr->next; + if (rr != (void*)&req_msg->hdr) + rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr); + else + break; + } + + /* Copy Call-ID header. */ + hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL); + pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr)); + + /* Copy From header. */ + hdr = pjsip_hdr_clone(tdata->pool, rdata->from); + pjsip_msg_add_hdr( msg, hdr); + + /* Copy To header. */ + hdr = pjsip_hdr_clone(tdata->pool, rdata->to); + pjsip_msg_add_hdr( msg, hdr); + + /* Copy CSeq header. */ + hdr = pjsip_hdr_clone(tdata->pool, rdata->cseq); + pjsip_msg_add_hdr( msg, hdr); + + /* All done. */ + return tdata; +} + + +/* + * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of + * RFC3261). Note that the generation of ACK for 2xx response is different, + * and one must not use this function to generate such ACK. + */ +PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + const pjsip_rx_data *rdata ) +{ + pjsip_msg *ack_msg, *invite_msg; + pjsip_to_hdr *to; + pjsip_from_hdr *from; + pjsip_cseq_hdr *cseq; + pjsip_hdr *hdr; + + /* Make compiler happy. */ + PJ_UNUSED_ARG(endpt); + + /* rdata must be a final response. */ + pj_assert(rdata->msg->type==PJSIP_RESPONSE_MSG && + rdata->msg->line.status.code >= 300); + + /* Log this action. */ + PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_ack(rdata=%p)", rdata)); + + /* Create new request message. */ + ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); + pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD ); + + /* The original INVITE message. */ + invite_msg = tdata->msg; + + /* Copy Request-Uri from the original INVITE. */ + ack_msg->line.req.uri = invite_msg->line.req.uri; + + /* Copy Call-ID from the original INVITE */ + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL); + pjsip_msg_add_hdr( ack_msg, hdr ); + + /* Copy From header from the original INVITE. */ + from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg, + PJSIP_H_FROM, NULL); + pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from ); + + /* Copy To header from the original INVITE. */ + to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg, + PJSIP_H_TO, NULL); + pj_strdup(tdata->pool, &to->tag, &rdata->to_tag); + pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to ); + + /* Must contain single Via, just as the original INVITE. */ + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL); + pjsip_msg_insert_first_hdr( ack_msg, hdr ); + + /* Must have the same CSeq value as the original INVITE, but method + * changed to ACK + */ + cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg, + PJSIP_H_CSEQ, NULL); + pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD ); + pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq ); + + /* If the original INVITE has Route headers, those header fields MUST + * appear in the ACK. + */ + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); + while (hdr != NULL) { + pjsip_msg_add_hdr( ack_msg, hdr ); + hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); + } + + /* Set the message in the "tdata" to point to the ACK message. */ + tdata->msg = ack_msg; + + /* Reset transmit packet buffer, to force 're-printing' of message. */ + tdata->buf.cur = tdata->buf.start; + + /* We're done. + * "tdata" parameter now contains the ACK message. + */ +} + + +/* + * Construct CANCEL request for the previously sent request, according to + * chapter 9.1 of RFC3261. + */ +PJ_DEF(pjsip_tx_data*) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + pjsip_tx_data *req_tdata ) +{ + pjsip_msg *req_msg; /* the original request. */ + pjsip_tx_data *cancel_tdata; + pjsip_msg *cancel_msg; + pjsip_hdr *hdr; + pjsip_cseq_hdr *req_cseq, *cseq; + pjsip_uri *req_uri; + + /* Log this action. */ + PJ_LOG(5,(LOG_THIS, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata)); + + /* Get the original request. */ + req_msg = req_tdata->msg; + + /* The transmit buffer must INVITE request. */ + pj_assert(req_msg->type == PJSIP_REQUEST_MSG && + req_msg->line.req.method.id == PJSIP_INVITE_METHOD ); + + /* Create new transmit buffer. */ + cancel_tdata = pjsip_endpt_create_tdata( endpt ); + if (!cancel_tdata) { + return NULL; + } + + /* Create CANCEL request message. */ + cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG); + cancel_tdata->msg = cancel_msg; + + /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are + * copied from the original request. + */ + /* Set request line. */ + pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD); + req_uri = req_msg->line.req.uri; + cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri); + + /* Copy Call-ID */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL); + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* Copy From header. */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL); + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* Copy To header. */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL); + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* Create new CSeq with equal number, but method set to CANCEL. */ + req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL); + cseq = pjsip_cseq_hdr_create(cancel_tdata->pool); + cseq->cseq = req_cseq->cseq; + pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD); + pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq); + + /* Must only have single Via which matches the top-most Via in the + * request being cancelled. + */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL); + pjsip_msg_insert_first_hdr(cancel_msg, + pjsip_hdr_clone(cancel_tdata->pool, hdr)); + + /* If the original request has Route header, the CANCEL request must also + * has exactly the same. + * Copy "Route" header from the request. + */ + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL); + while (hdr != NULL) { + pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); + hdr = hdr->next; + if (hdr != &cancel_msg->hdr) + hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr); + else + break; + } + + /* Done. + * Return the transmit buffer containing the CANCEL request. + */ + return cancel_tdata; +} + +/* Get the address parameters (host, port, flag, TTL, etc) to send the + * response. + */ +PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, + const pjsip_transport_t *req_transport, + const pjsip_via_hdr *via, + pjsip_host_port *send_addr) +{ + /* Determine the destination address (section 18.2.2): + * - for TCP, SCTP, or TLS, send the response using the transport where + * the request was received. + * - if maddr parameter is present, send to this address using the port + * in sent-by or 5060. If multicast is used, the TTL in the Via must + * be used, or 1 if ttl parameter is not present. + * - otherwise if received parameter is present, set to this address. + * - otherwise send to the address in sent-by. + */ + send_addr->flag = pjsip_transport_get_flag(req_transport); + send_addr->type = pjsip_transport_get_type(req_transport); + + if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) { + const pj_sockaddr_in *remote_addr; + remote_addr = pjsip_transport_get_remote_addr(req_transport); + pj_strdup2(pool, &send_addr->host, + pj_sockaddr_get_str_addr(remote_addr)); + send_addr->port = pj_sockaddr_get_port(remote_addr); + + } else { + /* Set the host part */ + if (via->maddr_param.slen) { + pj_strdup(pool, &send_addr->host, &via->maddr_param); + } else if (via->recvd_param.slen) { + pj_strdup(pool, &send_addr->host, &via->recvd_param); + } else { + pj_strdup(pool, &send_addr->host, &via->sent_by.host); + } + + /* Set the port */ + send_addr->port = via->sent_by.port; + } + + return PJ_SUCCESS; +} + +/* + * Get the event string from the event ID. + */ +PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e) +{ + return event_str[e]; +} + diff --git a/pjsip/src/pjsip/sip_misc.h b/pjsip/src/pjsip/sip_misc.h index 77d3546b0..15897f7ed 100644 --- a/pjsip/src/pjsip/sip_misc.h +++ b/pjsip/src/pjsip/sip_misc.h @@ -1,168 +1,168 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_misc.h 7 8/31/05 9:05p Bennylp $ */ -#ifndef __PJSIP_SIP_MISC_H__ -#define __PJSIP_SIP_MISC_H__ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_ENDPT SIP Endpoint - * @ingroup PJSIP - * @{ - */ - -/** - * Create an independent request message. This can be used to build any - * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request - * inside a dialog, application should use #pjsip_dlg_create_request. - * - * Once a transmit data is created, the reference counter is initialized to 1. - * - * @param endpt Endpoint instance. - * @param method SIP Method. - * @param target Target URI. - * @param from URL to put in From header. - * @param to URL to put in To header. - * @param contact URL to put in Contact header. - * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). - * @param cseq Optional CSeq (put -1 to generate random CSeq). - * @param text Optional text body (put NULL to omit body). - * - * @return The transmit data. - */ -PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_request( pjsip_endpoint *endpt, - const pjsip_method *method, - const pj_str_t *target, - const pj_str_t *from, - const pj_str_t *to, - const pj_str_t *contact, - const pj_str_t *call_id, - int cseq, - const pj_str_t *text); - -/** - * Create an independent request message from the specified headers. This - * function will shallow clone the headers and put them in the request. - * - * Once a transmit data is created, the reference counter is initialized to 1. - * - * @param endpt Endpoint instance. - * @param method SIP Method. - * @param target Target URI. - * @param from URL to put in From header. - * @param to URL to put in To header. - * @param contact URL to put in Contact header. - * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). - * @param cseq Optional CSeq (put -1 to generate random CSeq). - * @param text Optional text body (put NULL to omit body). - * - * @return The transmit data. - */ -PJ_DECL(pjsip_tx_data*) -pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, - const pjsip_method *method, - const pjsip_uri *target, - const pjsip_from_hdr *from, - const pjsip_to_hdr *to, - const pjsip_contact_hdr *contact, - const pjsip_cid_hdr *call_id, - int cseq, - const pj_str_t *text ); - -/** - * Send outgoing request and initiate UAC transaction for the request. - * This is an auxiliary function to be used by application to send arbitrary - * requests outside a dialog. To send a request within a dialog, application - * should use #pjsip_dlg_send_msg instead. - * - * @param endpt The endpoint instance. - * @param tdata The transmit data to be sent. - * @param timeout Optional timeout for final response to be received, or -1 - * if the transaction should not have a timeout restriction. - * @param token Optional token to be associated with the transaction, and - * to be passed to the callback. - * @param cb Optional callback to be called when the transaction has - * received a final response. The callback will be called with - * the previously registered token and the event that triggers - * the completion of the transaction. - * - * @return Zero if transaction is started successfully. - */ -PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, - pjsip_tx_data *tdata, - int timeout, - void *token, - void (*cb)(void*,pjsip_event*)); - -/** - * Construct a minimal response message for the received request. This function - * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and - * Call-ID headers from the request. - * - * Note: the txdata reference counter is set to ZERO!. - * - * @param endpt The endpoint. - * @param rdata The request receive data. - * @param code Status code to be put in the response. - * - * @return Transmit data. - */ -PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_response(pjsip_endpoint *endpt, - const pjsip_rx_data *rdata, - int code); - -/** - * Construct a full ACK request for the received non-2xx final response. - * This utility function is normally called by the transaction to construct - * an ACK request to 3xx-6xx final response. - * The generation of ACK message for 2xx final response is different than - * this one. - * - * @param endpt The endpoint. - * @param tdata On input, this contains the original INVITE request, and on - * output, it contains the ACK message. - * @param rdata The final response message. - */ -PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt, - pjsip_tx_data *tdata, - const pjsip_rx_data *rdata ); - - -/** - * Construct CANCEL request for the previously sent request. - * - * @param endpt The endpoint. - * @param tdata The transmit buffer for the request being cancelled. - * - * @return Cancel request. - */ -PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, - pjsip_tx_data *tdata ); - - -/** - * Get the address parameters (host, port, flag, TTL, etc) to send the - * response. - * - * @param pool The pool. - * @param tr The transport where the request was received. - * @param via The top-most Via header of the request. - * @param addr The send address concluded from the calculation. - * - * @return zero (PJ_OK) if successfull. - */ -PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, - const pjsip_transport_t *tr, - const pjsip_via_hdr *via, - pjsip_host_port *addr); - - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_SIP_MISC_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_misc.h 7 8/31/05 9:05p Bennylp $ */ +#ifndef __PJSIP_SIP_MISC_H__ +#define __PJSIP_SIP_MISC_H__ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_ENDPT SIP Endpoint + * @ingroup PJSIP + * @{ + */ + +/** + * Create an independent request message. This can be used to build any + * request outside a dialog, such as OPTIONS, MESSAGE, etc. To create a request + * inside a dialog, application should use #pjsip_dlg_create_request. + * + * Once a transmit data is created, the reference counter is initialized to 1. + * + * @param endpt Endpoint instance. + * @param method SIP Method. + * @param target Target URI. + * @param from URL to put in From header. + * @param to URL to put in To header. + * @param contact URL to put in Contact header. + * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). + * @param cseq Optional CSeq (put -1 to generate random CSeq). + * @param text Optional text body (put NULL to omit body). + * + * @return The transmit data. + */ +PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_request( pjsip_endpoint *endpt, + const pjsip_method *method, + const pj_str_t *target, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *contact, + const pj_str_t *call_id, + int cseq, + const pj_str_t *text); + +/** + * Create an independent request message from the specified headers. This + * function will shallow clone the headers and put them in the request. + * + * Once a transmit data is created, the reference counter is initialized to 1. + * + * @param endpt Endpoint instance. + * @param method SIP Method. + * @param target Target URI. + * @param from URL to put in From header. + * @param to URL to put in To header. + * @param contact URL to put in Contact header. + * @param call_id Optional Call-ID (put NULL to generate unique Call-ID). + * @param cseq Optional CSeq (put -1 to generate random CSeq). + * @param text Optional text body (put NULL to omit body). + * + * @return The transmit data. + */ +PJ_DECL(pjsip_tx_data*) +pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, + const pjsip_method *method, + const pjsip_uri *target, + const pjsip_from_hdr *from, + const pjsip_to_hdr *to, + const pjsip_contact_hdr *contact, + const pjsip_cid_hdr *call_id, + int cseq, + const pj_str_t *text ); + +/** + * Send outgoing request and initiate UAC transaction for the request. + * This is an auxiliary function to be used by application to send arbitrary + * requests outside a dialog. To send a request within a dialog, application + * should use #pjsip_dlg_send_msg instead. + * + * @param endpt The endpoint instance. + * @param tdata The transmit data to be sent. + * @param timeout Optional timeout for final response to be received, or -1 + * if the transaction should not have a timeout restriction. + * @param token Optional token to be associated with the transaction, and + * to be passed to the callback. + * @param cb Optional callback to be called when the transaction has + * received a final response. The callback will be called with + * the previously registered token and the event that triggers + * the completion of the transaction. + * + * @return Zero if transaction is started successfully. + */ +PJ_DECL(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + int timeout, + void *token, + void (*cb)(void*,pjsip_event*)); + +/** + * Construct a minimal response message for the received request. This function + * will construct all the Via, Record-Route, Call-ID, From, To, CSeq, and + * Call-ID headers from the request. + * + * Note: the txdata reference counter is set to ZERO!. + * + * @param endpt The endpoint. + * @param rdata The request receive data. + * @param code Status code to be put in the response. + * + * @return Transmit data. + */ +PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_response(pjsip_endpoint *endpt, + const pjsip_rx_data *rdata, + int code); + +/** + * Construct a full ACK request for the received non-2xx final response. + * This utility function is normally called by the transaction to construct + * an ACK request to 3xx-6xx final response. + * The generation of ACK message for 2xx final response is different than + * this one. + * + * @param endpt The endpoint. + * @param tdata On input, this contains the original INVITE request, and on + * output, it contains the ACK message. + * @param rdata The final response message. + */ +PJ_DECL(void) pjsip_endpt_create_ack( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + const pjsip_rx_data *rdata ); + + +/** + * Construct CANCEL request for the previously sent request. + * + * @param endpt The endpoint. + * @param tdata The transmit buffer for the request being cancelled. + * + * @return Cancel request. + */ +PJ_DECL(pjsip_tx_data*) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + pjsip_tx_data *tdata ); + + +/** + * Get the address parameters (host, port, flag, TTL, etc) to send the + * response. + * + * @param pool The pool. + * @param tr The transport where the request was received. + * @param via The top-most Via header of the request. + * @param addr The send address concluded from the calculation. + * + * @return zero (PJ_OK) if successfull. + */ +PJ_DECL(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, + const pjsip_transport_t *tr, + const pjsip_via_hdr *via, + pjsip_host_port *addr); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_MISC_H__ */ + diff --git a/pjsip/src/pjsip/sip_module.h b/pjsip/src/pjsip/sip_module.h index 533340685..10237e8eb 100644 --- a/pjsip/src/pjsip/sip_module.h +++ b/pjsip/src/pjsip/sip_module.h @@ -1,123 +1,123 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_module.h 6 6/17/05 11:16p Bennylp $ */ -#ifndef __PJSIP_SIP_MODULE_H__ -#define __PJSIP_SIP_MODULE_H__ - -/** - * @file sip_module.h - * @brief Module helpers - */ -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_MOD SIP Modules - * @ingroup PJSIP - * @{ - */ - -/** - * Module registration structure, which is passed by the module to the - * endpoint during the module registration process. This structure enables - * the endpoint to query the module capability and to further communicate - * with the module. - */ -struct pjsip_module -{ - /** - * Module name. - */ - pj_str_t name; - - /** - * Flag to indicate the type of interfaces supported by the module. - */ - pj_uint32_t flag; - - /** - * Integer number to identify module initialization and start order with - * regard to other modules. Higher number will make the module gets - * initialized later. - */ - pj_uint32_t priority; - - /** - * Opaque data which can be used by a module to identify a resource within - * the module itself. - */ - void *mod_data; - - /** - * Number of methods supported by this module. - */ - int method_cnt; - - /** - * Array of methods supported by this module. - */ - const pjsip_method *methods[8]; - - /** - * Pointer to function to be called to initialize the module. - * - * @param endpt The endpoint instance. - * @param mod The module. - * @param id The unique module ID assigned to this module. - * - * @return Module should return zero when initialization succeed. - */ - pj_status_t (*init_module)(pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id); - - /** - * Pointer to function to be called to start the module. - * - * @param mod The module. - * - * @return Module should return zero to indicate success. - */ - pj_status_t (*start_module)(struct pjsip_module *mod); - - /** - * Pointer to function to be called to deinitialize the module before - * it is unloaded. - * - * @param mod The module. - * - * @return Module should return zero to indicate success. - */ - pj_status_t (*deinit_module)(struct pjsip_module *mod); - - /** - * Pointer to function to receive transaction related events. - * If the module doesn't wish to receive such notification, this member - * must be set to NULL. - * - * @param mod The module. - * @param event The transaction event. - */ - void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event); -}; - - -/** - * Prototype of function to register static modules (eg modules that are - * linked staticly with the application). This function must be implemented - * by any applications that use PJSIP library. - * - * @param count [input/output] On input, it contains the maximum number of - * elements in the array. On output, the function fills with - * the number of modules to be registered. - * @param modules [output] array of pointer to modules to be registered. - */ -pj_status_t register_static_modules( pj_size_t *count, - pjsip_module **modules ); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_SIP_MODULE_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_module.h 6 6/17/05 11:16p Bennylp $ */ +#ifndef __PJSIP_SIP_MODULE_H__ +#define __PJSIP_SIP_MODULE_H__ + +/** + * @file sip_module.h + * @brief Module helpers + */ +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MOD SIP Modules + * @ingroup PJSIP + * @{ + */ + +/** + * Module registration structure, which is passed by the module to the + * endpoint during the module registration process. This structure enables + * the endpoint to query the module capability and to further communicate + * with the module. + */ +struct pjsip_module +{ + /** + * Module name. + */ + pj_str_t name; + + /** + * Flag to indicate the type of interfaces supported by the module. + */ + pj_uint32_t flag; + + /** + * Integer number to identify module initialization and start order with + * regard to other modules. Higher number will make the module gets + * initialized later. + */ + pj_uint32_t priority; + + /** + * Opaque data which can be used by a module to identify a resource within + * the module itself. + */ + void *mod_data; + + /** + * Number of methods supported by this module. + */ + int method_cnt; + + /** + * Array of methods supported by this module. + */ + const pjsip_method *methods[8]; + + /** + * Pointer to function to be called to initialize the module. + * + * @param endpt The endpoint instance. + * @param mod The module. + * @param id The unique module ID assigned to this module. + * + * @return Module should return zero when initialization succeed. + */ + pj_status_t (*init_module)(pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id); + + /** + * Pointer to function to be called to start the module. + * + * @param mod The module. + * + * @return Module should return zero to indicate success. + */ + pj_status_t (*start_module)(struct pjsip_module *mod); + + /** + * Pointer to function to be called to deinitialize the module before + * it is unloaded. + * + * @param mod The module. + * + * @return Module should return zero to indicate success. + */ + pj_status_t (*deinit_module)(struct pjsip_module *mod); + + /** + * Pointer to function to receive transaction related events. + * If the module doesn't wish to receive such notification, this member + * must be set to NULL. + * + * @param mod The module. + * @param event The transaction event. + */ + void (*tsx_handler)(struct pjsip_module *mod, pjsip_event *event); +}; + + +/** + * Prototype of function to register static modules (eg modules that are + * linked staticly with the application). This function must be implemented + * by any applications that use PJSIP library. + * + * @param count [input/output] On input, it contains the maximum number of + * elements in the array. On output, the function fills with + * the number of modules to be registered. + * @param modules [output] array of pointer to modules to be registered. + */ +pj_status_t register_static_modules( pj_size_t *count, + pjsip_module **modules ); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_MODULE_H__ */ + diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c index 6c8d2a633..2038b988c 100644 --- a/pjsip/src/pjsip/sip_msg.c +++ b/pjsip/src/pjsip/sip_msg.c @@ -1,1415 +1,1415 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_msg.c 16 6/22/05 12:40a Bennylp $ */ -#include -#include -#include -#include - -/* - * Include inline definitions here if functions are NOT inlined. - */ -#if PJ_FUNCTIONS_ARE_INLINED==0 -# include -#endif - -static const pj_str_t method_names[] = -{ - { "INVITE", 6 }, - { "CANCEL", 6 }, - { "ACK", 3 }, - { "BYE", 3 }, - { "REGISTER", 8 }, - { "OPTIONS", 7 }, -}; - -const pj_str_t pjsip_hdr_names[] = -{ - { "Accept", 6 }, // PJSIP_H_ACCEPT, - { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING, - { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE, - { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO, - { "Allow", 5 }, // PJSIP_H_ALLOW, - { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO, - { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION, - { "Call-ID", 7 }, // PJSIP_H_CALL_ID, - { "Call-Info", 9 }, // PJSIP_H_CALL_INFO, - { "Contact", 7 }, // PJSIP_H_CONTACT, - { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION, - { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING, - { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE, - { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH, - { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE, - { "CSeq", 4 }, // PJSIP_H_CSEQ, - { "Date", 4 }, // PJSIP_H_DATE, - { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO, - { "Expires", 7 }, // PJSIP_H_EXPIRES, - { "From", 4 }, // PJSIP_H_FROM, - { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO, - { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS, - { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION, - { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES, - { "Organization", 12 }, // PJSIP_H_ORGANIZATION, - { "Priority", 8 }, // PJSIP_H_PRIORITY, - { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE, - { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION, - { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE, - { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE, - { "Reply-To", 8 }, // PJSIP_H_REPLY_TO, - { "Require", 7 }, // PJSIP_H_REQUIRE, - { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER, - { "Route", 5 }, // PJSIP_H_ROUTE, - { "Server", 6 }, // PJSIP_H_SERVER, - { "Subject", 7 }, // PJSIP_H_SUBJECT, - { "Supported", 9 }, // PJSIP_H_SUPPORTED, - { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP, - { "To", 2 }, // PJSIP_H_TO, - { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED, - { "User-Agent", 10 }, // PJSIP_H_USER_AGENT, - { "Via", 3 }, // PJSIP_H_VIA, - { "Warning", 7 }, // PJSIP_H_WARNING, - { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE, - - { "_Unknown-Header", 15 }, // PJSIP_H_OTHER, -}; - -static pj_str_t status_phrase[710]; -static int print_media_type(char *buf, const pjsip_media_type *media); - -static int init_status_phrase() -{ - int i; - pj_str_t default_reason_phrase = { "Default status message", 22}; - - for (i=0; iid = me; - m->name = method_names[me]; -} - -PJ_DEF(void) pjsip_method_init_np(pjsip_method *m, - pj_str_t *str) -{ - int i; - for (i=0; iid = (pjsip_method_e)i; - m->name = method_names[i]; - return; - } - } - m->id = PJSIP_OTHER_METHOD; - m->name = *str; -} - -PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool, - pjsip_method *method, - const pjsip_method *rhs ) -{ - method->id = rhs->id; - if (rhs->id != PJSIP_OTHER_METHOD) { - method->name = rhs->name; - } else { - pj_strdup(pool, &method->name, &rhs->name); - } -} - - -PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2) -{ - if (m1->id == m2->id) { - if (m1->id != PJSIP_OTHER_METHOD) - return 0; - return pj_stricmp(&m1->name, &m2->name); - } - - return ( m1->id < m2->id ) ? -1 : 1; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Message. - */ - -PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type) -{ - pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg)); - pj_list_init(&msg->hdr); - msg->type = type; - msg->body = NULL; - return msg; -} - -PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg, - pjsip_hdr_e hdr_type, void *start) -{ - pjsip_hdr *hdr=start, *end=&msg->hdr; - - if (hdr == NULL) { - hdr = msg->hdr.next; - } - for (; hdr!=end; hdr = hdr->next) { - if (hdr->type == hdr_type) - return hdr; - } - return NULL; -} - -PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg, - const pj_str_t *name, void *start) -{ - pjsip_hdr *hdr=start, *end=&msg->hdr; - - if (hdr == NULL) { - hdr = msg->hdr.next; - } - for (; hdr!=end; hdr = hdr->next) { - if (hdr->type < PJSIP_H_OTHER) { - if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0) - return hdr; - } else { - if (pj_stricmp(&hdr->name, name) == 0) - return hdr; - } - } - return NULL; -} - -PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, - pjsip_hdr_e hdr_type, void *start) -{ - pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start); - if (hdr) { - pj_list_erase(hdr); - } - return hdr; -} - -PJ_DEF(int) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size) -{ - char *p=buf, *end=buf+size; - int len; - pjsip_hdr *hdr; - pj_str_t clen_hdr = { "Content-Length: ", 16}; - - /* Get a wild guess on how many bytes are typically needed. - * We'll check this later in detail, but this serves as a quick check. - */ - if (size < 256) - return -1; - - /* Print request line or status line depending on message type */ - if (msg->type == PJSIP_REQUEST_MSG) { - pjsip_uri *uri; - - /* Add method. */ - len = msg->line.req.method.name.slen; - pj_memcpy(p, msg->line.req.method.name.ptr, len); - p += len; - *p++ = ' '; - - /* Add URI */ - uri = pjsip_uri_get_uri(msg->line.req.uri); - len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p); - if (len < 1) - return -1; - p += len; - - /* Add ' SIP/2.0' */ - if (end-p < 16) - return -1; - pj_memcpy(p, " SIP/2.0\r\n", 10); - p += 10; - - } else { - - /* Add 'SIP/2.0 ' */ - pj_memcpy(p, "SIP/2.0 ", 8); - p += 8; - - /* Add status code. */ - len = pj_utoa(msg->line.status.code, p); - p += len; - *p++ = ' '; - - /* Add reason text. */ - len = msg->line.status.reason.slen; - pj_memcpy(p, msg->line.status.reason.ptr, len ); - p += len; - - /* Add newline. */ - *p++ = '\r'; - *p++ = '\n'; - } - - /* Print each of the headers. */ - for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { - len = (*hdr->vptr->print_on)(hdr, p, end-p); - if (len < 1) - return -1; - p += len; - - if (p+3 >= end) - return -1; - - *p++ = '\r'; - *p++ = '\n'; - } - - /* Process message body. */ - if (msg->body) { - pj_str_t ctype_hdr = { "Content-Type: ", 14}; - int len; - const pjsip_media_type *media = &msg->body->content_type; - char *clen_pos; - - /* Add Content-Type header. */ - if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) { - return -1; - } - pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); - p += ctype_hdr.slen; - p += print_media_type(p, media); - *p++ = '\r'; - *p++ = '\n'; - - /* Add Content-Length header. */ - if ((end-p) < clen_hdr.slen+12+2) { - return -1; - } - pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); - p += clen_hdr.slen; - - /* Print blanks after "Content-Type:", this is where we'll put - * the content length value after we know the length of the - * body. - */ - pj_memset(p, ' ', 12); - clen_pos = p; - p += 12; - *p++ = '\r'; - *p++ = '\n'; - - /* Add blank newline. */ - *p++ = '\r'; - *p++ = '\n'; - - /* Print the message body itself. */ - len = (*msg->body->print_body)(msg->body, p, end-p); - if (len < 0) { - return -1; - } - p += len; - - /* Now that we have the length of the body, print this to the - * Content-Length header. - */ - len = pj_utoa(len, clen_pos); - clen_pos[len] = ' '; - - } else { - /* There's no message body. - * Add Content-Length with zero value. - */ - if ((end-p) < clen_hdr.slen+8) { - return -1; - } - pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); - p += clen_hdr.slen; - *p++ = '0'; - *p++ = '\r'; - *p++ = '\n'; - *p++ = '\r'; - *p++ = '\n'; - } - - *p = '\0'; - return p-buf; -} - -/////////////////////////////////////////////////////////////////////////////// -PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr ) -{ - const pjsip_hdr *hdr = hdr_ptr; - return (*hdr->vptr->clone)(pool, hdr_ptr); -} - - -PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr ) -{ - const pjsip_hdr *hdr = hdr_ptr; - return (*hdr->vptr->shallow_clone)(pool, hdr_ptr); -} - -PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len) -{ - pjsip_hdr *hdr = hdr_ptr; - return (*hdr->vptr->print_on)(hdr_ptr, buf, len); -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Status/Reason Phrase - */ - -PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code) -{ - static int is_initialized; - if (is_initialized == 0) { - is_initialized = 1; - init_status_phrase(); - } - - return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ? - &status_phrase[code] : &status_phrase[0]; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Generic pjsip_hdr_names/hvalue header. - */ - -static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, - const pjsip_generic_string_hdr *hdr); -static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_string_hdr *hdr ); - -static pjsip_hdr_vptr generic_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print, -}; - -PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, - const pj_str_t *hnames ) -{ - pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr)); - init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr); - if (hnames) { - pj_strdup(pool, &hdr->name, hnames); - hdr->sname = hdr->name; - } - hdr->hvalue.ptr = NULL; - hdr->hvalue.slen = 0; - return hdr; -} - -PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool, - const pj_str_t *hname, - const pj_str_t *hvalue) -{ - pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname); - pj_strdup(pool, &hdr->hvalue, hvalue); - return hdr; -} - -static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - - if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen); - p += hdr->hvalue.slen; - *p = '\0'; - - return p - buf; -} - -static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, - const pjsip_generic_string_hdr *rhs) -{ - pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name); - - hdr->type = rhs->type; - hdr->sname = hdr->name; - pj_strdup( pool, &hdr->hvalue, &rhs->hvalue); - return hdr; -} - -static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_string_hdr *rhs ) -{ - pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Generic pjsip_hdr_names/integer value header. - */ - -static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *hdr); -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *hdr ); - -static pjsip_hdr_vptr generic_int_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print, -}; - -PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, - const pj_str_t *hnames ) -{ - pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr)); - init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr); - if (hnames) { - pj_strdup(pool, &hdr->name, hnames); - hdr->sname = hdr->name; - } - hdr->ivalue = 0; - return hdr; -} - -PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool, - const pj_str_t *hname, - int value) -{ - pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname); - hdr->ivalue = value; - return hdr; -} - -static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - - if ((pj_ssize_t)size < hdr->name.slen + 15) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - p += pj_utoa(hdr->ivalue, p); - - return p - buf; -} - -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *rhs) -{ - pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *rhs ) -{ - pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Generic array header. - */ -static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size); -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *hdr); -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *hdr); - -static pjsip_hdr_vptr generic_array_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print, -}; - -PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool, - const pj_str_t *hnames) -{ - pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr)); - init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr); - if (hnames) { - pj_strdup(pool, &hdr->name, hnames); - hdr->sname = hdr->name; - } - hdr->count = 0; - return hdr; - -} - -static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf, *endbuf = buf+size; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - if (hdr->count > 0) { - unsigned i; - int printed; - copy_advance(p, hdr->values[0]); - for (i=1; icount; ++i) { - copy_advance_pair(p, ", ", 2, hdr->values[i]); - } - } - - return p - buf; -} - -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *rhs) -{ - unsigned i; - pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - - pj_memcpy(hdr, rhs, sizeof(*hdr)); - for (i=0; icount; ++i) { - pj_strdup(pool, &hdr->values[i], &rhs->values[i]); - } - - return hdr; -} - - -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *rhs) -{ - pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Accept header. - */ -PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool) -{ - pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Allow header. - */ - -PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool) -{ - pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Call-ID header. - */ - -PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ) -{ - pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Content-Length header. - */ -static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size); -static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr); -#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone - -static pjsip_hdr_vptr clen_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print, -}; - -PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ) -{ - pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr)); - init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr); - hdr->len = 0; - return hdr; -} - -static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - int len; - - if ((pj_ssize_t)size < hdr->name.slen + 14) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - len = pj_utoa(hdr->len, p); - p += len; - *p = '\0'; - - return p-buf; -} - -static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs) -{ - pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool); - hdr->len = rhs->len; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * CSeq header. - */ -static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size); -static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr); -static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr ); - -static pjsip_hdr_vptr cseq_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print, -}; - -PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) -{ - pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr)); - init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr); - hdr->cseq = 0; - hdr->method.id = PJSIP_OTHER_METHOD; - hdr->method.name.ptr = NULL; - hdr->method.name.slen = 0; - return hdr; -} - -static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size) -{ - char *p = buf; - int len; - - if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - len = pj_utoa(hdr->cseq, p); - p += len; - *p++ = ' '; - - pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen); - p += hdr->method.name.slen; - - *p = '\0'; - - return p-buf; -} - -static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, - const pjsip_cseq_hdr *rhs) -{ - pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool); - hdr->cseq = rhs->cseq; - pjsip_method_copy(pool, &hdr->method, &rhs->method); - return hdr; -} - -static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_cseq_hdr *rhs ) -{ - pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Contact header. - */ -static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size); -static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr); -static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *); - -static pjsip_hdr_vptr contact_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print, -}; - -PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ) -{ - pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr); - hdr->expires = -1; - return hdr; -} - -static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, - pj_size_t size) -{ - if (hdr->star) { - char *p = buf; - if ((pj_ssize_t)size < hdr->name.slen + 6) - return -1; - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - *p++ = '*'; - *p = '\0'; - return p - buf; - - } else { - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri, - buf, endbuf-buf); - if (printed < 1) - return -1; - - buf += printed; - - if (hdr->q1000) { - if (buf+19 >= endbuf) - return -1; - - /* - printed = sprintf(buf, ";q=%u.%03u", - hdr->q1000/1000, hdr->q1000 % 1000); - */ - pj_memcpy(buf, ";q=", 3); - printed = pj_utoa(hdr->q1000/1000, buf+3); - buf += printed + 3; - *buf++ = '.'; - printed = pj_utoa(hdr->q1000 % 1000, buf); - buf += printed; - } - - if (hdr->expires >= 0) { - if (buf+23 >= endbuf) - return -1; - - pj_memcpy(buf, ";expires=", 9); - printed = pj_utoa(hdr->expires, buf+9); - buf += printed + 9; - } - - if (hdr->other_param.slen) { - copy_advance(buf, hdr->other_param); - } - - *buf = '\0'; - return buf-startbuf; - } -} - -static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, - const pjsip_contact_hdr *rhs) -{ - pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool); - - hdr->star = rhs->star; - if (hdr->star) - return hdr; - - hdr->uri = pjsip_uri_clone(pool, rhs->uri); - hdr->q1000 = rhs->q1000; - hdr->expires = rhs->expires; - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_contact_hdr *rhs) -{ - pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Content-Type header.. - */ -static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size); -static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *hdr); -#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone - -static pjsip_hdr_vptr ctype_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print, -}; - -PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ) -{ - pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr); - return hdr; -} - -static int print_media_type(char *buf, const pjsip_media_type *media) -{ - char *p = buf; - - pj_memcpy(p, media->type.ptr, media->type.slen); - p += media->type.slen; - *p++ = '/'; - pj_memcpy(p, media->subtype.ptr, media->subtype.slen); - p += media->subtype.slen; - - if (media->param.slen) { - pj_memcpy(p, media->param.ptr, media->param.slen); - p += media->param.slen; - } - - return p-buf; -} - -static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - int len; - - if ((pj_ssize_t)size < hdr->name.slen + - hdr->media.type.slen + hdr->media.subtype.slen + - hdr->media.param.slen + 8) - { - return -1; - } - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - len = print_media_type(p, &hdr->media); - p += len; - - *p = '\0'; - return p-buf; -} - -static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, - const pjsip_ctype_hdr *rhs) -{ - pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool); - pj_strdup(pool, &hdr->media.type, &rhs->media.type); - pj_strdup(pool, &hdr->media.param, &rhs->media.param); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Expires header. - */ -PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ) -{ - pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * To or From header. - */ -static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *hdr); -static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *hdr); - - -static pjsip_hdr_vptr fromto_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print, -}; - -PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ) -{ - pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr); - return hdr; -} - -PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ) -{ - pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr); - return hdr; -} - -PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr ) -{ - hdr->type = PJSIP_H_FROM; - hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM]; - return hdr; -} - -PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr ) -{ - hdr->type = PJSIP_H_TO; - hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO]; - return hdr; -} - -static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf); - if (printed < 1) - return -1; - - buf += printed; - - copy_advance_pair(buf, ";tag=", 5, hdr->tag); - if (hdr->other_param.slen) - copy_advance(buf, hdr->other_param); - - return buf-startbuf; -} - -static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *rhs) -{ - pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool); - - hdr->type = rhs->type; - hdr->name = rhs->name; - hdr->sname = rhs->sname; - hdr->uri = pjsip_uri_clone(pool, rhs->uri); - pj_strdup( pool, &hdr->tag, &rhs->tag); - pj_strdup( pool, &hdr->other_param, &rhs->other_param); - - return hdr; -} - -static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *rhs) -{ - pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Max-Forwards header. - */ -PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool) -{ - pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Min-Expires header. - */ -PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool) -{ - pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Record-Route and Route header. - */ -static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size ); -static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); -static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); - -static pjsip_hdr_vptr routing_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print, -}; - -PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ) -{ - pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr); - pjsip_name_addr_init(&hdr->name_addr); - pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param)); - return hdr; -} - -PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ) -{ - pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr); - pjsip_name_addr_init(&hdr->name_addr); - pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param)); - return hdr; -} - -PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr ) -{ - hdr->type = PJSIP_H_RECORD_ROUTE; - hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE]; - return hdr; -} - -PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr ) -{ - hdr->type = PJSIP_H_ROUTE; - hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE]; - return hdr; -} - -static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr, - char *buf, pj_size_t size ) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf); - if (printed < 1) - return -1; - buf += printed; - - if (hdr->other_param.slen) { - copy_advance(buf, hdr->other_param); - } - - return buf-startbuf; -} - -static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, - const pjsip_routing_hdr *rhs ) -{ - pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - - init_hdr(hdr, rhs->type, rhs->vptr); - pjsip_name_addr_init(&hdr->name_addr); - pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr); - pj_strdup( pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_routing_hdr *rhs ) -{ - pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Require header. - */ -PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool) -{ - pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Retry-After header. - */ -PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool) -{ - pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Supported header. - */ -PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool) -{ - pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Unsupported header. - */ -PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool) -{ - pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Via header. - */ -static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size); -static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr); -static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr ); - -static pjsip_hdr_vptr via_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_via_hdr_print, -}; - -PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ) -{ - pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr); - hdr->sent_by.port = 5060; - hdr->ttl_param = -1; - hdr->rport_param = -1; - return hdr; -} - -static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - pj_str_t sip_ver = { "SIP/2.0/", 8 }; - - if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen + - hdr->transport.slen + hdr->sent_by.host.slen + 12) - { - return -1; - } - - /* pjsip_hdr_names */ - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - /* SIP/2.0/transport host:port */ - pj_memcpy(buf, sip_ver.ptr, sip_ver.slen); - buf += sip_ver.slen; - pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen); - buf += hdr->transport.slen; - *buf++ = ' '; - pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen); - buf += hdr->sent_by.host.slen; - *buf++ = ':'; - printed = pj_utoa(hdr->sent_by.port, buf); - buf += printed; - - if (hdr->ttl_param >= 0) { - size = endbuf-buf; - if (size < 14) - return -1; - pj_memcpy(buf, ";ttl=", 5); - printed = pj_utoa(hdr->ttl_param, buf+5); - buf += printed + 5; - } - - if (hdr->rport_param >= 0) { - size = endbuf-buf; - if (size < 14) - return -1; - pj_memcpy(buf, ";rport", 6); - buf += 6; - if (hdr->rport_param > 0) { - *buf++ = '='; - buf += pj_utoa(hdr->rport_param, buf); - } - } - - - copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param); - copy_advance_pair(buf, ";received=", 10, hdr->recvd_param); - copy_advance_pair(buf, ";branch=", 8, hdr->branch_param); - copy_advance(buf, hdr->other_param); - - *buf = '\0'; - return buf-startbuf; -} - -static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, - const pjsip_via_hdr *rhs) -{ - pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool); - pj_strdup(pool, &hdr->transport, &rhs->transport); - pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host); - hdr->sent_by.port = rhs->sent_by.port; - hdr->ttl_param = rhs->ttl_param; - hdr->rport_param = rhs->rport_param; - pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param); - pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param); - pj_strdup(pool, &hdr->branch_param, &rhs->branch_param); - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_via_hdr *rhs ) -{ - pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * General purpose function to textual data in a SIP body. - */ -PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) -{ - if (size < msg_body->len) - return -1; - pj_memcpy(buf, msg_body->data, msg_body->len); - return msg_body->len; -} +/* $Header: /pjproject/pjsip/src/pjsip/sip_msg.c 16 6/22/05 12:40a Bennylp $ */ +#include +#include +#include +#include + +/* + * Include inline definitions here if functions are NOT inlined. + */ +#if PJ_FUNCTIONS_ARE_INLINED==0 +# include +#endif + +static const pj_str_t method_names[] = +{ + { "INVITE", 6 }, + { "CANCEL", 6 }, + { "ACK", 3 }, + { "BYE", 3 }, + { "REGISTER", 8 }, + { "OPTIONS", 7 }, +}; + +const pj_str_t pjsip_hdr_names[] = +{ + { "Accept", 6 }, // PJSIP_H_ACCEPT, + { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING, + { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE, + { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO, + { "Allow", 5 }, // PJSIP_H_ALLOW, + { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO, + { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION, + { "Call-ID", 7 }, // PJSIP_H_CALL_ID, + { "Call-Info", 9 }, // PJSIP_H_CALL_INFO, + { "Contact", 7 }, // PJSIP_H_CONTACT, + { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION, + { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING, + { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE, + { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH, + { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE, + { "CSeq", 4 }, // PJSIP_H_CSEQ, + { "Date", 4 }, // PJSIP_H_DATE, + { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO, + { "Expires", 7 }, // PJSIP_H_EXPIRES, + { "From", 4 }, // PJSIP_H_FROM, + { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO, + { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS, + { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION, + { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES, + { "Organization", 12 }, // PJSIP_H_ORGANIZATION, + { "Priority", 8 }, // PJSIP_H_PRIORITY, + { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE, + { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION, + { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE, + { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE, + { "Reply-To", 8 }, // PJSIP_H_REPLY_TO, + { "Require", 7 }, // PJSIP_H_REQUIRE, + { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER, + { "Route", 5 }, // PJSIP_H_ROUTE, + { "Server", 6 }, // PJSIP_H_SERVER, + { "Subject", 7 }, // PJSIP_H_SUBJECT, + { "Supported", 9 }, // PJSIP_H_SUPPORTED, + { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP, + { "To", 2 }, // PJSIP_H_TO, + { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED, + { "User-Agent", 10 }, // PJSIP_H_USER_AGENT, + { "Via", 3 }, // PJSIP_H_VIA, + { "Warning", 7 }, // PJSIP_H_WARNING, + { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE, + + { "_Unknown-Header", 15 }, // PJSIP_H_OTHER, +}; + +static pj_str_t status_phrase[710]; +static int print_media_type(char *buf, const pjsip_media_type *media); + +static int init_status_phrase() +{ + int i; + pj_str_t default_reason_phrase = { "Default status message", 22}; + + for (i=0; iid = me; + m->name = method_names[me]; +} + +PJ_DEF(void) pjsip_method_init_np(pjsip_method *m, + pj_str_t *str) +{ + int i; + for (i=0; iid = (pjsip_method_e)i; + m->name = method_names[i]; + return; + } + } + m->id = PJSIP_OTHER_METHOD; + m->name = *str; +} + +PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool, + pjsip_method *method, + const pjsip_method *rhs ) +{ + method->id = rhs->id; + if (rhs->id != PJSIP_OTHER_METHOD) { + method->name = rhs->name; + } else { + pj_strdup(pool, &method->name, &rhs->name); + } +} + + +PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2) +{ + if (m1->id == m2->id) { + if (m1->id != PJSIP_OTHER_METHOD) + return 0; + return pj_stricmp(&m1->name, &m2->name); + } + + return ( m1->id < m2->id ) ? -1 : 1; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Message. + */ + +PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type) +{ + pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg)); + pj_list_init(&msg->hdr); + msg->type = type; + msg->body = NULL; + return msg; +} + +PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg, + pjsip_hdr_e hdr_type, void *start) +{ + pjsip_hdr *hdr=start, *end=&msg->hdr; + + if (hdr == NULL) { + hdr = msg->hdr.next; + } + for (; hdr!=end; hdr = hdr->next) { + if (hdr->type == hdr_type) + return hdr; + } + return NULL; +} + +PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg, + const pj_str_t *name, void *start) +{ + pjsip_hdr *hdr=start, *end=&msg->hdr; + + if (hdr == NULL) { + hdr = msg->hdr.next; + } + for (; hdr!=end; hdr = hdr->next) { + if (hdr->type < PJSIP_H_OTHER) { + if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0) + return hdr; + } else { + if (pj_stricmp(&hdr->name, name) == 0) + return hdr; + } + } + return NULL; +} + +PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, + pjsip_hdr_e hdr_type, void *start) +{ + pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start); + if (hdr) { + pj_list_erase(hdr); + } + return hdr; +} + +PJ_DEF(int) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size) +{ + char *p=buf, *end=buf+size; + int len; + pjsip_hdr *hdr; + pj_str_t clen_hdr = { "Content-Length: ", 16}; + + /* Get a wild guess on how many bytes are typically needed. + * We'll check this later in detail, but this serves as a quick check. + */ + if (size < 256) + return -1; + + /* Print request line or status line depending on message type */ + if (msg->type == PJSIP_REQUEST_MSG) { + pjsip_uri *uri; + + /* Add method. */ + len = msg->line.req.method.name.slen; + pj_memcpy(p, msg->line.req.method.name.ptr, len); + p += len; + *p++ = ' '; + + /* Add URI */ + uri = pjsip_uri_get_uri(msg->line.req.uri); + len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p); + if (len < 1) + return -1; + p += len; + + /* Add ' SIP/2.0' */ + if (end-p < 16) + return -1; + pj_memcpy(p, " SIP/2.0\r\n", 10); + p += 10; + + } else { + + /* Add 'SIP/2.0 ' */ + pj_memcpy(p, "SIP/2.0 ", 8); + p += 8; + + /* Add status code. */ + len = pj_utoa(msg->line.status.code, p); + p += len; + *p++ = ' '; + + /* Add reason text. */ + len = msg->line.status.reason.slen; + pj_memcpy(p, msg->line.status.reason.ptr, len ); + p += len; + + /* Add newline. */ + *p++ = '\r'; + *p++ = '\n'; + } + + /* Print each of the headers. */ + for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { + len = (*hdr->vptr->print_on)(hdr, p, end-p); + if (len < 1) + return -1; + p += len; + + if (p+3 >= end) + return -1; + + *p++ = '\r'; + *p++ = '\n'; + } + + /* Process message body. */ + if (msg->body) { + pj_str_t ctype_hdr = { "Content-Type: ", 14}; + int len; + const pjsip_media_type *media = &msg->body->content_type; + char *clen_pos; + + /* Add Content-Type header. */ + if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) { + return -1; + } + pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); + p += ctype_hdr.slen; + p += print_media_type(p, media); + *p++ = '\r'; + *p++ = '\n'; + + /* Add Content-Length header. */ + if ((end-p) < clen_hdr.slen+12+2) { + return -1; + } + pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); + p += clen_hdr.slen; + + /* Print blanks after "Content-Type:", this is where we'll put + * the content length value after we know the length of the + * body. + */ + pj_memset(p, ' ', 12); + clen_pos = p; + p += 12; + *p++ = '\r'; + *p++ = '\n'; + + /* Add blank newline. */ + *p++ = '\r'; + *p++ = '\n'; + + /* Print the message body itself. */ + len = (*msg->body->print_body)(msg->body, p, end-p); + if (len < 0) { + return -1; + } + p += len; + + /* Now that we have the length of the body, print this to the + * Content-Length header. + */ + len = pj_utoa(len, clen_pos); + clen_pos[len] = ' '; + + } else { + /* There's no message body. + * Add Content-Length with zero value. + */ + if ((end-p) < clen_hdr.slen+8) { + return -1; + } + pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); + p += clen_hdr.slen; + *p++ = '0'; + *p++ = '\r'; + *p++ = '\n'; + *p++ = '\r'; + *p++ = '\n'; + } + + *p = '\0'; + return p-buf; +} + +/////////////////////////////////////////////////////////////////////////////// +PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr ) +{ + const pjsip_hdr *hdr = hdr_ptr; + return (*hdr->vptr->clone)(pool, hdr_ptr); +} + + +PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr ) +{ + const pjsip_hdr *hdr = hdr_ptr; + return (*hdr->vptr->shallow_clone)(pool, hdr_ptr); +} + +PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len) +{ + pjsip_hdr *hdr = hdr_ptr; + return (*hdr->vptr->print_on)(hdr_ptr, buf, len); +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Status/Reason Phrase + */ + +PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code) +{ + static int is_initialized; + if (is_initialized == 0) { + is_initialized = 1; + init_status_phrase(); + } + + return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ? + &status_phrase[code] : &status_phrase[0]; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Generic pjsip_hdr_names/hvalue header. + */ + +static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *hdr); +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *hdr ); + +static pjsip_hdr_vptr generic_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print, +}; + +PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, + const pj_str_t *hnames ) +{ + pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr)); + init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr); + if (hnames) { + pj_strdup(pool, &hdr->name, hnames); + hdr->sname = hdr->name; + } + hdr->hvalue.ptr = NULL; + hdr->hvalue.slen = 0; + return hdr; +} + +PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool, + const pj_str_t *hname, + const pj_str_t *hvalue) +{ + pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname); + pj_strdup(pool, &hdr->hvalue, hvalue); + return hdr; +} + +static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + + if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen); + p += hdr->hvalue.slen; + *p = '\0'; + + return p - buf; +} + +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *rhs) +{ + pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name); + + hdr->type = rhs->type; + hdr->sname = hdr->name; + pj_strdup( pool, &hdr->hvalue, &rhs->hvalue); + return hdr; +} + +static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_string_hdr *rhs ) +{ + pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Generic pjsip_hdr_names/integer value header. + */ + +static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *hdr); +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *hdr ); + +static pjsip_hdr_vptr generic_int_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print, +}; + +PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, + const pj_str_t *hnames ) +{ + pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr)); + init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr); + if (hnames) { + pj_strdup(pool, &hdr->name, hnames); + hdr->sname = hdr->name; + } + hdr->ivalue = 0; + return hdr; +} + +PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool, + const pj_str_t *hname, + int value) +{ + pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname); + hdr->ivalue = value; + return hdr; +} + +static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + + if ((pj_ssize_t)size < hdr->name.slen + 15) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + p += pj_utoa(hdr->ivalue, p); + + return p - buf; +} + +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *rhs) +{ + pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_int_hdr *rhs ) +{ + pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Generic array header. + */ +static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size); +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *hdr); +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *hdr); + +static pjsip_hdr_vptr generic_array_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print, +}; + +PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool, + const pj_str_t *hnames) +{ + pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr)); + init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr); + if (hnames) { + pj_strdup(pool, &hdr->name, hnames); + hdr->sname = hdr->name; + } + hdr->count = 0; + return hdr; + +} + +static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf, *endbuf = buf+size; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + if (hdr->count > 0) { + unsigned i; + int printed; + copy_advance(p, hdr->values[0]); + for (i=1; icount; ++i) { + copy_advance_pair(p, ", ", 2, hdr->values[i]); + } + } + + return p - buf; +} + +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *rhs) +{ + unsigned i; + pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + + pj_memcpy(hdr, rhs, sizeof(*hdr)); + for (i=0; icount; ++i) { + pj_strdup(pool, &hdr->values[i], &rhs->values[i]); + } + + return hdr; +} + + +static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_generic_array_hdr *rhs) +{ + pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Accept header. + */ +PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool) +{ + pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Allow header. + */ + +PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool) +{ + pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Call-ID header. + */ + +PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ) +{ + pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Content-Length header. + */ +static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size); +static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr); +#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone + +static pjsip_hdr_vptr clen_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print, +}; + +PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ) +{ + pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr)); + init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr); + hdr->len = 0; + return hdr; +} + +static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + int len; + + if ((pj_ssize_t)size < hdr->name.slen + 14) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + len = pj_utoa(hdr->len, p); + p += len; + *p = '\0'; + + return p-buf; +} + +static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs) +{ + pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool); + hdr->len = rhs->len; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * CSeq header. + */ +static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size); +static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr); +static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr ); + +static pjsip_hdr_vptr cseq_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print, +}; + +PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) +{ + pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr)); + init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr); + hdr->cseq = 0; + hdr->method.id = PJSIP_OTHER_METHOD; + hdr->method.name.ptr = NULL; + hdr->method.name.slen = 0; + return hdr; +} + +static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size) +{ + char *p = buf; + int len; + + if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15) + return -1; + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + len = pj_utoa(hdr->cseq, p); + p += len; + *p++ = ' '; + + pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen); + p += hdr->method.name.slen; + + *p = '\0'; + + return p-buf; +} + +static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, + const pjsip_cseq_hdr *rhs) +{ + pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool); + hdr->cseq = rhs->cseq; + pjsip_method_copy(pool, &hdr->method, &rhs->method); + return hdr; +} + +static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_cseq_hdr *rhs ) +{ + pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Contact header. + */ +static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size); +static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr); +static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *); + +static pjsip_hdr_vptr contact_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print, +}; + +PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ) +{ + pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr); + hdr->expires = -1; + return hdr; +} + +static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, + pj_size_t size) +{ + if (hdr->star) { + char *p = buf; + if ((pj_ssize_t)size < hdr->name.slen + 6) + return -1; + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + *p++ = '*'; + *p = '\0'; + return p - buf; + + } else { + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri, + buf, endbuf-buf); + if (printed < 1) + return -1; + + buf += printed; + + if (hdr->q1000) { + if (buf+19 >= endbuf) + return -1; + + /* + printed = sprintf(buf, ";q=%u.%03u", + hdr->q1000/1000, hdr->q1000 % 1000); + */ + pj_memcpy(buf, ";q=", 3); + printed = pj_utoa(hdr->q1000/1000, buf+3); + buf += printed + 3; + *buf++ = '.'; + printed = pj_utoa(hdr->q1000 % 1000, buf); + buf += printed; + } + + if (hdr->expires >= 0) { + if (buf+23 >= endbuf) + return -1; + + pj_memcpy(buf, ";expires=", 9); + printed = pj_utoa(hdr->expires, buf+9); + buf += printed + 9; + } + + if (hdr->other_param.slen) { + copy_advance(buf, hdr->other_param); + } + + *buf = '\0'; + return buf-startbuf; + } +} + +static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, + const pjsip_contact_hdr *rhs) +{ + pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool); + + hdr->star = rhs->star; + if (hdr->star) + return hdr; + + hdr->uri = pjsip_uri_clone(pool, rhs->uri); + hdr->q1000 = rhs->q1000; + hdr->expires = rhs->expires; + pj_strdup(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_contact_hdr *rhs) +{ + pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Content-Type header.. + */ +static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size); +static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *hdr); +#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone + +static pjsip_hdr_vptr ctype_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print, +}; + +PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ) +{ + pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr); + return hdr; +} + +static int print_media_type(char *buf, const pjsip_media_type *media) +{ + char *p = buf; + + pj_memcpy(p, media->type.ptr, media->type.slen); + p += media->type.slen; + *p++ = '/'; + pj_memcpy(p, media->subtype.ptr, media->subtype.slen); + p += media->subtype.slen; + + if (media->param.slen) { + pj_memcpy(p, media->param.ptr, media->param.slen); + p += media->param.slen; + } + + return p-buf; +} + +static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + int len; + + if ((pj_ssize_t)size < hdr->name.slen + + hdr->media.type.slen + hdr->media.subtype.slen + + hdr->media.param.slen + 8) + { + return -1; + } + + pj_memcpy(p, hdr->name.ptr, hdr->name.slen); + p += hdr->name.slen; + *p++ = ':'; + *p++ = ' '; + + len = print_media_type(p, &hdr->media); + p += len; + + *p = '\0'; + return p-buf; +} + +static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, + const pjsip_ctype_hdr *rhs) +{ + pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool); + pj_strdup(pool, &hdr->media.type, &rhs->media.type); + pj_strdup(pool, &hdr->media.param, &rhs->media.param); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Expires header. + */ +PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ) +{ + pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * To or From header. + */ +static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *hdr); +static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *hdr); + + +static pjsip_hdr_vptr fromto_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print, +}; + +PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ) +{ + pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr); + return hdr; +} + +PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ) +{ + pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr); + return hdr; +} + +PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr ) +{ + hdr->type = PJSIP_H_FROM; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM]; + return hdr; +} + +PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr ) +{ + hdr->type = PJSIP_H_TO; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO]; + return hdr; +} + +static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf); + if (printed < 1) + return -1; + + buf += printed; + + copy_advance_pair(buf, ";tag=", 5, hdr->tag); + if (hdr->other_param.slen) + copy_advance(buf, hdr->other_param); + + return buf-startbuf; +} + +static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *rhs) +{ + pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool); + + hdr->type = rhs->type; + hdr->name = rhs->name; + hdr->sname = rhs->sname; + hdr->uri = pjsip_uri_clone(pool, rhs->uri); + pj_strdup( pool, &hdr->tag, &rhs->tag); + pj_strdup( pool, &hdr->other_param, &rhs->other_param); + + return hdr; +} + +static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_fromto_hdr *rhs) +{ + pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Max-Forwards header. + */ +PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool) +{ + pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Min-Expires header. + */ +PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool) +{ + pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Record-Route and Route header. + */ +static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size ); +static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); +static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); + +static pjsip_hdr_vptr routing_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print, +}; + +PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ) +{ + pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr); + pjsip_name_addr_init(&hdr->name_addr); + pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param)); + return hdr; +} + +PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ) +{ + pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr); + pjsip_name_addr_init(&hdr->name_addr); + pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param)); + return hdr; +} + +PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr ) +{ + hdr->type = PJSIP_H_RECORD_ROUTE; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE]; + return hdr; +} + +PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr ) +{ + hdr->type = PJSIP_H_ROUTE; + hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE]; + return hdr; +} + +static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr, + char *buf, pj_size_t size ) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf); + if (printed < 1) + return -1; + buf += printed; + + if (hdr->other_param.slen) { + copy_advance(buf, hdr->other_param); + } + + return buf-startbuf; +} + +static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, + const pjsip_routing_hdr *rhs ) +{ + pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + + init_hdr(hdr, rhs->type, rhs->vptr); + pjsip_name_addr_init(&hdr->name_addr); + pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr); + pj_strdup( pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_routing_hdr *rhs ) +{ + pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Require header. + */ +PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool) +{ + pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Retry-After header. + */ +PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool) +{ + pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr); + hdr->ivalue = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Supported header. + */ +PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool) +{ + pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + + +/////////////////////////////////////////////////////////////////////////////// +/* + * Unsupported header. + */ +PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool) +{ + pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr); + hdr->count = 0; + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * Via header. + */ +static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size); +static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr); +static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr ); + +static pjsip_hdr_vptr via_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_via_hdr_print, +}; + +PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ) +{ + pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr); + hdr->sent_by.port = 5060; + hdr->ttl_param = -1; + hdr->rport_param = -1; + return hdr; +} + +static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + pj_str_t sip_ver = { "SIP/2.0/", 8 }; + + if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen + + hdr->transport.slen + hdr->sent_by.host.slen + 12) + { + return -1; + } + + /* pjsip_hdr_names */ + copy_advance(buf, hdr->name); + *buf++ = ':'; + *buf++ = ' '; + + /* SIP/2.0/transport host:port */ + pj_memcpy(buf, sip_ver.ptr, sip_ver.slen); + buf += sip_ver.slen; + pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen); + buf += hdr->transport.slen; + *buf++ = ' '; + pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen); + buf += hdr->sent_by.host.slen; + *buf++ = ':'; + printed = pj_utoa(hdr->sent_by.port, buf); + buf += printed; + + if (hdr->ttl_param >= 0) { + size = endbuf-buf; + if (size < 14) + return -1; + pj_memcpy(buf, ";ttl=", 5); + printed = pj_utoa(hdr->ttl_param, buf+5); + buf += printed + 5; + } + + if (hdr->rport_param >= 0) { + size = endbuf-buf; + if (size < 14) + return -1; + pj_memcpy(buf, ";rport", 6); + buf += 6; + if (hdr->rport_param > 0) { + *buf++ = '='; + buf += pj_utoa(hdr->rport_param, buf); + } + } + + + copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param); + copy_advance_pair(buf, ";received=", 10, hdr->recvd_param); + copy_advance_pair(buf, ";branch=", 8, hdr->branch_param); + copy_advance(buf, hdr->other_param); + + *buf = '\0'; + return buf-startbuf; +} + +static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, + const pjsip_via_hdr *rhs) +{ + pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool); + pj_strdup(pool, &hdr->transport, &rhs->transport); + pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host); + hdr->sent_by.port = rhs->sent_by.port; + hdr->ttl_param = rhs->ttl_param; + hdr->rport_param = rhs->rport_param; + pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param); + pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param); + pj_strdup(pool, &hdr->branch_param, &rhs->branch_param); + pj_strdup(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_via_hdr *rhs ) +{ + pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +/////////////////////////////////////////////////////////////////////////////// +/* + * General purpose function to textual data in a SIP body. + */ +PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) +{ + if (size < msg_body->len) + return -1; + pj_memcpy(buf, msg_body->data, msg_body->len); + return msg_body->len; +} diff --git a/pjsip/src/pjsip/sip_msg.h b/pjsip/src/pjsip/sip_msg.h index 1f82f54e7..69350ec8a 100644 --- a/pjsip/src/pjsip/sip_msg.h +++ b/pjsip/src/pjsip/sip_msg.h @@ -1,1510 +1,1510 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_msg.h 13 6/22/05 12:27a Bennylp $ */ -#ifndef __PJSIP_SIP_MSG_H__ -#define __PJSIP_SIP_MSG_H__ - -/** - * @file sip_msg.h - * @brief SIP Message Structure. - */ - -#include -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_MSG SIP Message Structure - * @ingroup PJSIP - * @{ - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_METHOD Methods - * @brief Method names and manipulation. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * This enumeration declares SIP methods as described by RFC3261. Additional - * methods do exist, and they are described by corresponding RFCs for the SIP - * extentensions. Since they won't alter the characteristic of the processing - * of the message, they don't need to be explicitly mentioned here. - */ -typedef enum pjsip_method_e -{ - /** INVITE method, for establishing dialogs. */ - PJSIP_INVITE_METHOD, - - /** CANCEL method, for cancelling request. */ - PJSIP_CANCEL_METHOD, - - /** ACK method, for acknowledging final response to INVITE. */ - PJSIP_ACK_METHOD, - - /** BYE method, for terminating dialog. */ - PJSIP_BYE_METHOD, - - /** REGISTER method. */ - PJSIP_REGISTER_METHOD, - - /** OPTIONS method, for querying remote capabilities. */ - PJSIP_OPTIONS_METHOD, - - /** Other method, which means that the method name itself will be stored - elsewhere. */ - PJSIP_OTHER_METHOD, - -} pjsip_method_e; - - - -/** - * This structure represents a SIP method. - * Application must always use either #pjsip_method_init or #pjsip_method_set - * to make sure that method name is initialized correctly. This way, the name - * member will always contain a valid method string regardless whether the ID - * is recognized or not. - */ -typedef struct pjsip_method -{ - pjsip_method_e id; /**< Method ID, from \a pjsip_method_e. */ - pj_str_t name; /**< Method name, which will always contain the - method string. */ -} pjsip_method; - - -/** - * Initialize the method structure from a string. - * This function will check whether the method is a known method then set - * both the id and name accordingly. - * - * @param m The method to initialize. - * @param pool Pool where memory allocation will be allocated from, if required. - * @param str The method string. - */ -PJ_DECL(void) pjsip_method_init( pjsip_method *m, - pj_pool_t *pool, - const pj_str_t *str); - -/** - * Initialize the method structure from a string, without cloning the string. - * See #pjsip_method_init. - * - * @param m The method structure to be initialized. - * @param str The method string. - */ -PJ_DECL(void) pjsip_method_init_np( pjsip_method *m, - pj_str_t *str); - -/** - * Set the method with the predefined method ID. - * This function will also set the name member of the structure to the correct - * string according to the method. - * - * @param m The method structure. - * @param id The method ID. - */ -PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id ); - - -/** - * Copy one method structure to another. If the method is of the known methods, - * then memory allocation is not required. - * - * @param pool Pool to allocate memory from, if required. - * @param method The destination method to copy to. - * @param rhs The source method to copy from. - */ -PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool, - pjsip_method *method, - const pjsip_method *rhs ); - -/** - * Compare one method with another, and conveniently determine whether the - * first method is equal, less than, or greater than the second method. - * - * @param m1 The first method. - * @param m2 The second method. - * - * @return Zero if equal, otherwise will return -1 if less or +1 if greater. - */ -PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR Header Fields General Structure. - * @brief General Header Fields Structure. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * Header types, as defined by RFC3261. - */ -typedef enum pjsip_hdr_e -{ - /* - * These are the headers documented in RFC3261. Headers not documented - * there must have type PJSIP_H_OTHER, and the header type itself is - * recorded in the header name string. - * - * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!. - */ - PJSIP_H_ACCEPT, - PJSIP_H_ACCEPT_ENCODING_UNIMP, - PJSIP_H_ACCEPT_LANGUAGE_UNIMP, - PJSIP_H_ALERT_INFO_UNIMP, - PJSIP_H_ALLOW, - PJSIP_H_AUTHENTICATION_INFO_UNIMP, - PJSIP_H_AUTHORIZATION, - PJSIP_H_CALL_ID, - PJSIP_H_CALL_INFO_UNIMP, - PJSIP_H_CONTACT, - PJSIP_H_CONTENT_DISPOSITION_UNIMP, - PJSIP_H_CONTENT_ENCODING_UNIMP, - PJSIP_H_CONTENT_LANGUAGE_UNIMP, - PJSIP_H_CONTENT_LENGTH, - PJSIP_H_CONTENT_TYPE, - PJSIP_H_CSEQ, - PJSIP_H_DATE_UNIMP, - PJSIP_H_ERROR_INFO_UNIMP, - PJSIP_H_EXPIRES, - PJSIP_H_FROM, - PJSIP_H_IN_REPLY_TO_UNIMP, - PJSIP_H_MAX_FORWARDS, - PJSIP_H_MIME_VERSION_UNIMP, - PJSIP_H_MIN_EXPIRES, - PJSIP_H_ORGANIZATION_UNIMP, - PJSIP_H_PRIORITY_UNIMP, - PJSIP_H_PROXY_AUTHENTICATE, - PJSIP_H_PROXY_AUTHORIZATION, - PJSIP_H_PROXY_REQUIRE_UNIMP, - PJSIP_H_RECORD_ROUTE, - PJSIP_H_REPLY_TO_UNIMP, - PJSIP_H_REQUIRE, - PJSIP_H_RETRY_AFTER, - PJSIP_H_ROUTE, - PJSIP_H_SERVER_UNIMP, - PJSIP_H_SUBJECT_UNIMP, - PJSIP_H_SUPPORTED, - PJSIP_H_TIMESTAMP_UNIMP, - PJSIP_H_TO, - PJSIP_H_UNSUPPORTED, - PJSIP_H_USER_AGENT_UNIMP, - PJSIP_H_VIA, - PJSIP_H_WARNING_UNIMP, - PJSIP_H_WWW_AUTHENTICATE, - - PJSIP_H_OTHER, - -} pjsip_hdr_e; - -/** - * This structure provides the pointer to basic functions that are needed - * for generic header operations. All header fields will have pointer to - * this structure, so that they can be manipulated uniformly. - */ -typedef struct pjsip_hdr_vptr -{ - /** - * Function to clone the header. - * - * @param pool Memory pool to allocate the new header. - * @param hdr Header to clone. - * - * @return A new instance of the header. - */ - void *(*clone)(pj_pool_t *pool, const void *hdr); - - /** - * Pointer to function to shallow clone the header. - * Shallow cloning will just make a memory copy of the original header, - * thus all pointers in original header will be kept intact. Because the - * function does not need to perform deep copy, the operation should be - * faster, but the application must make sure that the original header - * is still valid throughout the lifetime of new header. - * - * @param pool Memory pool to allocate the new header. - * @param hdr The header to clone. - */ - void *(*shallow_clone)(pj_pool_t *pool, const void *hdr); - - /** Pointer to function to print the header to the specified buffer. - * Returns the length of string written, or -1 if the remaining buffer - * is not enough to hold the header. - * - * @param hdr The header to print. - * @param buf The buffer. - * @param len The size of the buffer. - * - * @return The size copied to buffer, or -1 if there's not enough space. - */ - int (*print_on)(void *hdr, char *buf, pj_size_t len); - -} pjsip_hdr_vptr; - - -/** - * Generic fields for all SIP headers are declared using this macro, to make - * sure that all headers will have exactly the same layout in their start of - * the storage. This behaves like C++ inheritance actually. - */ -#define PJSIP_DECL_HDR_MEMBER(hdr) \ - /** List members. */ \ - PJ_DECL_LIST_MEMBER(hdr) \ - /** Header type */ \ - pjsip_hdr_e type; \ - /** Header name. */ \ - pj_str_t name; \ - /** Header short name version. */ \ - pj_str_t sname; \ - /** Virtual function table. */ \ - pjsip_hdr_vptr *vptr; - - -/** - * Generic SIP header structure, for generic manipulation for headers in the - * message. All header fields can be typecasted to this type. - */ -typedef struct pjsip_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr) -} pjsip_hdr; - - -/** - * This generic function will clone any header, by calling "clone" function - * in header's virtual function table. - * - * @param pool The pool to allocate memory from. - * @param hdr The header to clone. - * - * @return A new instance copied from the original header. - */ -PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr ); - - -/** - * This generic function will clone any header, by calling "shallow_clone" - * function in header's virtual function table. - * - * @param pool The pool to allocate memory from. - * @param hdr The header to clone. - * - * @return A new instance copied from the original header. - */ -PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr ); - -/** - * This generic function will print any header, by calling "print" - * function in header's virtual function table. - * - * @param hdr The header to print. - * @param buf The buffer. - * @param len The size of the buffer. - * - * @return The size copied to buffer, or -1 if there's not enough space. - */ -PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_LINE Request and Status Line. - * @brief Request and status line structures and manipulation. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * This structure describes SIP request line. - */ -typedef struct pjsip_request_line -{ - pjsip_method method; /**< Method for this request line. */ - pjsip_uri *uri; /**< URI for this request line. */ -} pjsip_request_line; - - -/** - * This structure describes SIP status line. - */ -typedef struct pjsip_status_line -{ - int code; /**< Status code. */ - pj_str_t reason; /**< Reason string. */ -} pjsip_status_line; - - -/** - * This enumeration lists standard SIP status codes according to RFC 3261. - * In addition, it also declares new status class 7xx for errors generated - * by the stack. This status class however should not get transmitted on the - * wire. - */ -typedef enum pjsip_status_code -{ - PJSIP_SC_TRYING = 100, - PJSIP_SC_RINGING = 180, - PJSIP_SC_CALL_BEING_FORWARDED = 181, - PJSIP_SC_QUEUED = 182, - PJSIP_SC_PROGRESS = 183, - - PJSIP_SC_OK = 200, - - PJSIP_SC_MULTIPLE_CHOICES = 300, - PJSIP_SC_MOVED_PERMANENTLY = 301, - PJSIP_SC_MOVED_TEMPORARILY = 302, - PJSIP_SC_USE_PROXY = 305, - PJSIP_SC_ALTERNATIVE_SERVICE = 380, - - PJSIP_SC_BAD_REQUEST = 400, - PJSIP_SC_UNAUTHORIZED = 401, - PJSIP_SC_PAYMENT_REQUIRED = 402, - PJSIP_SC_FORBIDDEN = 403, - PJSIP_SC_NOT_FOUND = 404, - PJSIP_SC_METHOD_NOT_ALLOWED = 405, - PJSIP_SC_NOT_ACCEPTABLE = 406, - PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407, - PJSIP_SC_REQUEST_TIMEOUT = 408, - PJSIP_SC_GONE = 410, - PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413, - PJSIP_SC_REQUEST_URI_TOO_LONG = 414, - PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415, - PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416, - PJSIP_SC_BAD_EXTENSION = 420, - PJSIP_SC_EXTENSION_REQUIRED = 421, - PJSIP_SC_INTERVAL_TOO_BRIEF = 423, - PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480, - PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481, - PJSIP_SC_LOOP_DETECTED = 482, - PJSIP_SC_TOO_MANY_HOPS = 483, - PJSIP_SC_ADDRESS_INCOMPLETE = 484, - PJSIP_AC_AMBIGUOUS = 485, - PJSIP_SC_BUSY_HERE = 486, - PJSIP_SC_REQUEST_TERMINATED = 487, - PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, - PJSIP_SC_REQUEST_PENDING = 491, - PJSIP_SC_UNDECIPHERABLE = 493, - - PJSIP_SC_INTERNAL_SERVER_ERROR = 500, - PJSIP_SC_NOT_IMPLEMENTED = 501, - PJSIP_SC_BAD_GATEWAY = 502, - PJSIP_SC_SERVICE_UNAVAILABLE = 503, - PJSIP_SC_SERVER_TIMEOUT = 504, - PJSIP_SC_VERSION_NOT_SUPPORTED = 505, - PJSIP_SC_MESSAGE_TOO_LARGE = 513, - - PJSIP_SC_BUSY_EVERYWHERE = 600, - PJSIP_SC_DECLINE = 603, - PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604, - PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, - - PJSIP_SC_TSX_TIMEOUT = 701, - PJSIP_SC_TSX_RESOLVE_ERROR = 702, - PJSIP_SC_TSX_TRANSPORT_ERROR = 703, - -} pjsip_status_code; - -/** - * Get the default status text for the status code. - * - * @param status_code SIP Status Code - * - * @return textual message for the status code. - */ -PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code); - -/** - * This macro returns non-zero (TRUE) if the specified status_code is - * in the same class as the code_class. - * - * @param status_code The status code. - * @param code_class The status code in the class (for example 100, 200). - */ -#define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class) \ - (status_code/100 == code_class/100) - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @addtogroup PJSIP_MSG_MEDIA Media Type - * @brief Media type definitions and manipulations. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * This structure describes SIP media type, as used for example in - * Accept and Content-Type header.. - */ -typedef struct pjsip_media_type -{ - pj_str_t type; /**< Media type. */ - pj_str_t subtype; /**< Media subtype. */ - pj_str_t param; /**< Media type parameters (concatenated). */ -} pjsip_media_type; - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @addtogroup PJSIP_MSG_BODY Message Body - * @brief SIP message body structures and manipulation. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * Generic abstraction to message body. - * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in - * all members with the appropriate value. The 'data' and 'len' member will - * describe portion of incoming packet which denotes the message body. - * When application needs to attach message body to outgoing SIP message, it - * must fill in all members of this structure. - */ -typedef struct pjsip_msg_body -{ - /** MIME content type. - * For incoming messages, the parser will fill in this member with the - * content type found in Content-Type header. - * - * For outgoing messages, application must fill in this member with - * appropriate value, because the stack will generate Content-Type header - * based on the value specified here. - */ - pjsip_media_type content_type; - - /** Pointer to buffer which holds the message body data. - * For incoming messages, the parser will fill in this member with the - * pointer to the body string. - * - * When sending outgoing message, this member doesn't need to point to the - * actual message body string. It can be assigned with arbitrary pointer, - * because the value will only need to be understood by the print_body() - * function. The stack itself will not try to interpret this value, but - * instead will always call the print_body() whenever it needs to get the - * actual body string. - */ - void *data; - - /** The length of the data. - * For incoming messages, the parser will fill in this member with the - * actual length of message body. - * - * When sending outgoing message, again just like the "data" member, the - * "len" member doesn't need to point to the actual length of the body - * string. - */ - unsigned len; - - /** Pointer to function to print this message body. - * Application must set a proper function here when sending outgoing - * message. - * - * @param msg_body This structure itself. - * @param buf The buffer. - * @param size The buffer size. - * - * @return The length of the string printed, or -1 if there is - * not enough space in the buffer to print the whole - * message body. - */ - int (*print_body)(struct pjsip_msg_body *msg_body, - char *buf, pj_size_t size); - -} pjsip_msg_body; - -/** - * General purpose function to textual data in a SIP body. Attach this function - * in a SIP message body only if the data in pjsip_msg_body is a textual - * message ready to be embedded in a SIP message. If the data in the message - * body is not a textual body, then application must supply a custom function - * to print that body. - * - * @param msg_body The message body. - * @param buf Buffer to copy the message body to. - * @param size The size of the buffer. - * - * @return The length copied to the buffer, or -1. - */ -PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body, - char *buf, pj_size_t size); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_MSG Message Structure - * @brief Message structure and operations. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * Message type (request or response). - */ -typedef enum pjsip_msg_type_e -{ - PJSIP_REQUEST_MSG, /**< Indicates request message. */ - PJSIP_RESPONSE_MSG, /**< Indicates response message. */ -} pjsip_msg_type_e; - - -/** - * This structure describes a SIP message. - */ -struct pjsip_msg -{ - /** Message type (ie request or response). */ - pjsip_msg_type_e type; - - /** The first line of the message can be either request line for request - * messages, or status line for response messages. It is represented here - * as a union. - */ - union - { - /** Request Line. */ - struct pjsip_request_line req; - - /** Status Line. */ - struct pjsip_status_line status; - } line; - - /** List of message headers. */ - pjsip_hdr hdr; - - /** Pointer to message body, or NULL if no message body is attached to - * this mesage. - */ - pjsip_msg_body *body; -}; - - -/** - * Create new request or response message. - * - * @param pool The pool. - * @param type Message type. - * @return New message, or THROW exception if failed. - */ -PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type); - -/** - * Find a header in the message by the header type. - * - * @param msg The message. - * @param type The header type to find. - * @param start The first header field where the search should begin. - * If NULL is specified, then the search will begin from the - * first header, otherwise the search will begin at the - * specified header. - * - * @return The header field, or NULL if no header with the specified - * type is found. - */ -PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg, - pjsip_hdr_e type, void *start); - -/** - * Find a header in the message by its name. - * - * @param msg The message. - * @param name The header name to find. - * @param start The first header field where the search should begin. - * If NULL is specified, then the search will begin from the - * first header, otherwise the search will begin at the - * specified header. - * - * @return The header field, or NULL if no header with the specified - * type is found. - */ -PJ_DECL(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg, - const pj_str_t *name, void *start); - -/** - * Find and remove a header in the message. - * - * @param msg The message. - * @param hdr The header type to find. - * @param start The first header field where the search should begin, - * or NULL to search from the first header in the message. - * - * @return The header field, or NULL if not found. - */ -PJ_DECL(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, - pjsip_hdr_e hdr, void *start); - -/** - * Add a header to the message, putting it last in the header list. - * - * @param msg The message. - * @param hdr The header to add. - * - * @bug Once the header is put in a list (or message), it can not be put in - * other list (or message). Otherwise Real Bad Thing will happen. - */ -PJ_IDECL(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr ); - -/** - * Add header field to the message, putting it in the front of the header list. - * - * @param msg The message. - * @param hdr The header to add. - * - * @bug Once the header is put in a list (or message), it can not be put in - * other list (or message). Otherwise Real Bad Thing will happen. - */ -PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr ); - -/** - * Print the message to the specified buffer. - * - * @param msg The message to print. - * @param buf The buffer - * @param size The size of the buffer. - * - * @return The length of the printed characters (in bytes), or NEGATIVE - * value if the message is too large for the specified buffer. - */ -PJ_DECL(int) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @addtogroup PJSIP_MSG_HDR_GEN Header Field: Generic - * @brief Generic header field which contains header name and value. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * Generic SIP header, which contains hname and a string hvalue. - * Note that this header is not supposed to be used as 'base' class for headers. - */ -typedef struct pjsip_generic_string_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr) /**< Standard header field. */ - pj_str_t hvalue; /**< hvalue */ -} pjsip_generic_string_hdr; - - -/** - * Create a new instance of generic header. A generic header can have an - * arbitrary header name. - * - * @param pool The pool. - * @param hname The header name to be assigned to the header, or NULL to - * assign the header name with some string. - * - * @return The header, or THROW exception. - */ -PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, - const pj_str_t *hname ); - -/** - * Create a generic header along with the text content. - * - * @param pool The pool. - * @param hname The header name. - * @param hvalue The header text content. - * - * @return The header instance. - */ -PJ_DECL(pjsip_generic_string_hdr*) -pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool, - const pj_str_t *hname, - const pj_str_t *hvalue); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @addtogroup PJSIP_MSG_HDR_GEN_INT Header Field: Generic Integer - * @brief Generic header field which contains header name and value. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * Generic SIP header, which contains hname and a string hvalue. - */ -typedef struct pjsip_generic_int_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr) /**< Standard header field. */ - pj_int32_t ivalue; /**< ivalue */ -} pjsip_generic_int_hdr; - - -/** - * Create a new instance of generic header. A generic header can have an - * arbitrary header name. - * - * @param pool The pool. - * @param hname The header name to be assigned to the header, or NULL to - * assign the header name with some string. - * - * @return The header, or THROW exception. - */ -PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, - const pj_str_t *hname ); - -/** - * Create a generic header along with the value. - * - * @param pool The pool. - * @param hname The header name. - * @param value The header value content. - * - * @return The header instance. - */ -PJ_DECL(pjsip_generic_int_hdr*) -pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool, - const pj_str_t *hname, - int value); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_GENERIC_LIST Header Field: Generic string list. - * @brief Header with list of strings separated with comma - * @ingroup PJSIP_MSG - * @{ - */ - -/** Maximum elements in the header array. */ -#define PJSIP_GENERIC_ARRAY_MAX_COUNT 32 - -typedef struct pjsip_generic_array_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr) - unsigned count; /**< Number of elements. */ - pj_str_t values[PJSIP_GENERIC_ARRAY_MAX_COUNT]; /**< Elements. */ -} pjsip_generic_array_hdr; - -/** - * Create generic array header. - * - * @param pool Pool to allocate memory from. - * - * @return New generic array header. - */ -PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_create(pj_pool_t *pool, - const pj_str_t *hnames); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_ACCEPT Header Field: Accept - * @brief Accept header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** Accept header. */ -typedef pjsip_generic_array_hdr pjsip_accept_hdr; - -/** Maximum fields in Accept header. */ -#define PJSIP_MAX_ACCEPT_COUNT PJSIP_GENERIC_ARRAY_MAX_COUNT - -/** - * Create new Accept header instance. - * - * @param pool The pool. - * - * @return New Accept header instance. - */ -PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_ALLOW Header Field: Allow - * @brief Allow header field. - * @ingroup PJSIP_MSG - * @{ - */ -typedef pjsip_generic_array_hdr pjsip_allow_hdr; - -/** - * Create new Allow header instance. - * - * @param pool The pool. - * - * @return New Allow header instance. - */ -PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_CID Header Field: Call-ID - * @brief Call-ID header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * Call-ID header. - */ -typedef struct pjsip_cid_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr) - pj_str_t id; /**< Call-ID string. */ -} pjsip_cid_hdr; - - -/** - * Create new Call-ID header. - * - * @param pool The pool. - * - * @return new Call-ID header. - */ -PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ); - - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_CLEN Header Field: Content-Length - * @brief Content-Length header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * Content-Length header. - */ -typedef struct pjsip_clen_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr) - int len; /**< Content length. */ -} pjsip_clen_hdr; - -/** - * Create new Content-Length header. - * - * @param pool the pool. - * @return A new Content-Length header instance. - */ -PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_CSEQ Header Field: CSeq - * @brief CSeq header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * CSeq header. - */ -typedef struct pjsip_cseq_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr) - int cseq; /**< CSeq number. */ - pjsip_method method; /**< CSeq method. */ -} pjsip_cseq_hdr; - - -/** Create new CSeq header. - * - * @param pool The pool. - * @return A new CSeq header instance. - */ -PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_CONTACT Header Field: Contact - * @brief Contact header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * Contact header. - * In this library, contact header only contains single URI. If a message has - * multiple URI in the Contact header, the URI will be put in separate Contact - * headers. - */ -typedef struct pjsip_contact_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr) - int star; /**< The contact contains only a '*' character */ - pjsip_uri *uri; /**< URI in the contact. */ - int q1000; /**< The "q" value times 1000 (to avoid float) */ - pj_int32_t expires; /**< Expires parameter, otherwise -1 if not present. */ - pj_str_t other_param; /**< Other parameters, concatenated in a single string. */ -} pjsip_contact_hdr; - - -/** - * Create a new Contact header. - * - * @param pool The pool. - * @return A new instance of Contact header. - */ -PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_CTYPE Header Field: Content-Type - * @brief Content-Type header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * Content-Type. - */ -typedef struct pjsip_ctype_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr) - pjsip_media_type media; /**< Media type. */ -} pjsip_ctype_hdr; - - -/** - * Create a nwe Content Type header. - * - * @param pool The pool. - * @return A new Content-Type header. - */ -PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_EXPIRES Header Field: Expires - * @brief Expires header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** Expires header. */ -typedef pjsip_generic_int_hdr pjsip_expires_hdr; - -/** - * Create a new Expires header. - * - * @param pool The pool. - * @return A new Expires header. - */ -PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_FROMTO Header Field: From/To - * @brief From and To header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * To or From header. - */ -typedef struct pjsip_fromto_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr) - pjsip_uri *uri; /**< URI in From/To header. */ - pj_str_t tag; /**< Header "tag" parameter. */ - pj_str_t other_param; /**< Other params, concatenated as a single string. */ -} pjsip_fromto_hdr; - -/** Alias for From header. */ -typedef pjsip_fromto_hdr pjsip_from_hdr; - -/** Alias for To header. */ -typedef pjsip_fromto_hdr pjsip_to_hdr; - -/** - * Create a From header. - * - * @param pool The pool. - * @return New instance of From header. - */ -PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ); - -/** - * Create a To header. - * - * @param pool The pool. - * @return New instance of To header. - */ -PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ); - -/** - * Convert the header to a From header. - * - * @param pool The pool. - * @return "From" header. - */ -PJ_DECL(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr ); - -/** - * Convert the header to a To header. - * - * @param pool The pool. - * @return "To" header. - */ -PJ_DECL(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr ); - -/** - * @} - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_MAX_FORWARDS Header Field: Max-Forwards - * @brief Max-Forwards header field. - * @ingroup PJSIP_MSG - * @{ - */ -typedef pjsip_generic_int_hdr pjsip_max_forwards_hdr; - -/** - * Create new Max-Forwards header instance. - * - * @param pool The pool. - * - * @return New Max-Forwards header instance. - */ -PJ_DECL(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_MIN_EXPIRES Header Field: Min-Expires - * @brief Min-Expires header field. - * @ingroup PJSIP_MSG - * @{ - */ -typedef pjsip_generic_int_hdr pjsip_min_expires_hdr; - -/** - * Create new Max-Forwards header instance. - * - * @param pool The pool. - * - * @return New Max-Forwards header instance. - */ -PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_ROUTING Header Field: Record-Route/Route - * @brief Record-Route and Route header fields. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * Record-Route and Route headers. - */ -typedef struct pjsip_routing_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr) /**< Generic header fields. */ - pjsip_name_addr name_addr; /**< The URL in the Route/Record-Route header. */ - pj_str_t other_param; /** Other parameter. */ -} pjsip_routing_hdr; - -/** Alias for Record-Route header. */ -typedef pjsip_routing_hdr pjsip_rr_hdr; - -/** Alias for Route header. */ -typedef pjsip_routing_hdr pjsip_route_hdr; - - -/** - * Create new Record-Route header from the pool. - * - * @param pool The pool. - * @return A new instance of Record-Route header. - */ -PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ); - -/** - * Create new Route header from the pool. - * - * @param pool The pool. - * @return A new instance of "Route" header. - */ -PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ); - -/** - * Convert generic routing header to Record-Route header. - * - * @param r The generic routing header, or a "Routing" header. - * @return Record-Route header. - */ -PJ_DECL(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r ); - -/** - * Convert generic routing header to "Route" header. - * - * @param r The generic routing header, or a "Record-Route" header. - * @return "Route" header. - */ -PJ_DECL(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *r ); - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_REQUIRE Header Field: Require - * @brief Require header field. - * @ingroup PJSIP_MSG - * @{ - */ -typedef pjsip_generic_array_hdr pjsip_require_hdr; - -/** - * Create new Require header instance. - * - * @param pool The pool. - * - * @return New Require header instance. - */ -PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_RETRY_AFTER Header Field: Retry-After - * @brief Retry-After header field. - * @ingroup PJSIP_MSG - * @{ - */ -typedef pjsip_generic_int_hdr pjsip_retry_after_hdr; - -/** - * Create new Retry-After header instance. - * - * @param pool The pool. - * - * @return New Retry-After header instance. - */ -PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_SUPPORTED Header Field: Supported - * @brief Supported header field. - * @ingroup PJSIP_MSG - * @{ - */ -typedef pjsip_generic_array_hdr pjsip_supported_hdr; - -/** - * Create new Supported header instance. - * - * @param pool The pool. - * - * @return New Supported header instance. - */ -PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_UNSUPPORTED Header Field: Unsupported - * @brief Unsupported header field. - * @ingroup PJSIP_MSG - * @{ - */ -typedef pjsip_generic_array_hdr pjsip_unsupported_hdr; - -/** - * Create new Unsupported header instance. - * - * @param pool The pool. - * - * @return New Unsupported header instance. - */ -PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool); - - -/** - * @} - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_VIA Header Field: Via - * @brief Via header field. - * @ingroup PJSIP_MSG - * @{ - */ -/** - * SIP Via header. - * In this implementation, Via header can only have one element in each header. - * If a message arrives with multiple elements in a single Via, then they will - * be split up into multiple Via headers. - */ -typedef struct pjsip_via_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr) - pj_str_t transport; /**< Transport type. */ - pjsip_host_port sent_by; /**< Host and optional port */ - int ttl_param; /**< TTL parameter, or -1 if it's not specified. */ - int rport_param; /**< "rport" parameter, 0 to specify without - port number, -1 means doesn't exist. */ - pj_str_t maddr_param; /**< "maddr" parameter. */ - pj_str_t recvd_param; /**< "received" parameter. */ - pj_str_t branch_param; /**< "branch" parameter. */ - pj_str_t other_param; /**< Other parameters, concatenated as single string. */ - pj_str_t comment; /**< Comment. */ -} pjsip_via_hdr; - -/** - * Create a new Via header. - * - * @param pool The pool. - * @return A new "Via" header instance. - */ -PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ); - -/** - * @} - */ - -/** - * @bug Once a header is put in the message, the header CAN NOT be put in - * other list. Solution: - * - always clone header in the message. - * - create a list node for each header in the message. - */ - - -/////////////////////////////////////////////////////////////////////////////// -/** - * @defgroup PJSIP_MSG_HDR_UNIMP Unimplemented Header Fields - * @brief Unimplemented header fields. - * @ingroup PJSIP_MSG - * @{ - */ -/** Accept-Encoding header. */ -typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr; - -/** Create Accept-Encoding header. */ -#define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create - -/** Accept-Language header. */ -typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr; - -/** Create Accept-Language header. */ -#define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create - -/** Alert-Info header. */ -typedef pjsip_generic_string_hdr pjsip_alert_info_hdr; - -/** Create Alert-Info header. */ -#define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create - -/** Authentication-Info header. */ -typedef pjsip_generic_string_hdr pjsip_auth_info_hdr; - -/** Create Authentication-Info header. */ -#define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create - -/** Call-Info header. */ -typedef pjsip_generic_string_hdr pjsip_call_info_hdr; - -/** Create Call-Info header. */ -#define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create - -/** Content-Disposition header. */ -typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr; - -/** Create Content-Disposition header. */ -#define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create - -/** Content-Encoding header. */ -typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr; - -/** Create Content-Encoding header. */ -#define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create - -/** Content-Language header. */ -typedef pjsip_generic_string_hdr pjsip_content_lang_hdr; - -/** Create Content-Language header. */ -#define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create - -/** Date header. */ -typedef pjsip_generic_string_hdr pjsip_date_hdr; - -/** Create Date header. */ -#define pjsip_date_hdr_create pjsip_generic_string_hdr_create - -/** Error-Info header. */ -typedef pjsip_generic_string_hdr pjsip_err_info_hdr; - -/** Create Error-Info header. */ -#define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create - -/** In-Reply-To header. */ -typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr; - -/** Create In-Reply-To header. */ -#define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create - -/** MIME-Version header. */ -typedef pjsip_generic_string_hdr pjsip_mime_version_hdr; - -/** Create MIME-Version header. */ -#define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create - -/** Organization header. */ -typedef pjsip_generic_string_hdr pjsip_organization_hdr; - -/** Create Organization header. */ -#define pjsip_organization_hdr_create pjsip_genric_string_hdr_create - -/** Priority header. */ -typedef pjsip_generic_string_hdr pjsip_priority_hdr; - -/** Create Priority header. */ -#define pjsip_priority_hdr_create pjsip_generic_string_hdr_create - -/** Proxy-Require header. */ -typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr; - -/** Reply-To header. */ -typedef pjsip_generic_string_hdr pjsip_reply_to_hdr; - -/** Create Reply-To header. */ -#define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create - -/** Server header. */ -typedef pjsip_generic_string_hdr pjsip_server_hdr; - -/** Create Server header. */ -#define pjsip_server_hdr_create pjsip_generic_string_hdr_create - -/** Subject header. */ -typedef pjsip_generic_string_hdr pjsip_subject_hdr; - -/** Create Subject header. */ -#define pjsip_subject_hdr_create pjsip_generic_string_hdr_create - -/** Timestamp header. */ -typedef pjsip_generic_string_hdr pjsip_timestamp_hdr; - -/** Create Timestamp header. */ -#define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create - -/** User-Agent header. */ -typedef pjsip_generic_string_hdr pjsip_user_agent_hdr; - -/** Create User-Agent header. */ -#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create - -/** Warning header. */ -typedef pjsip_generic_string_hdr pjsip_warning_hdr; - -/** Create Warning header. */ -#define pjsip_warning_hdr_create pjsip_generic_string_hdr_create - -/** - * @} - */ - -/** - * @} // PJSIP_MSG - */ - -/* - * Include inline definitions. - */ -#if PJ_FUNCTIONS_ARE_INLINED -# include -#endif - - -PJ_END_DECL - -#endif /* __PJSIP_SIP_MSG_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_msg.h 13 6/22/05 12:27a Bennylp $ */ +#ifndef __PJSIP_SIP_MSG_H__ +#define __PJSIP_SIP_MSG_H__ + +/** + * @file sip_msg.h + * @brief SIP Message Structure. + */ + +#include +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MSG SIP Message Structure + * @ingroup PJSIP + * @{ + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_METHOD Methods + * @brief Method names and manipulation. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * This enumeration declares SIP methods as described by RFC3261. Additional + * methods do exist, and they are described by corresponding RFCs for the SIP + * extentensions. Since they won't alter the characteristic of the processing + * of the message, they don't need to be explicitly mentioned here. + */ +typedef enum pjsip_method_e +{ + /** INVITE method, for establishing dialogs. */ + PJSIP_INVITE_METHOD, + + /** CANCEL method, for cancelling request. */ + PJSIP_CANCEL_METHOD, + + /** ACK method, for acknowledging final response to INVITE. */ + PJSIP_ACK_METHOD, + + /** BYE method, for terminating dialog. */ + PJSIP_BYE_METHOD, + + /** REGISTER method. */ + PJSIP_REGISTER_METHOD, + + /** OPTIONS method, for querying remote capabilities. */ + PJSIP_OPTIONS_METHOD, + + /** Other method, which means that the method name itself will be stored + elsewhere. */ + PJSIP_OTHER_METHOD, + +} pjsip_method_e; + + + +/** + * This structure represents a SIP method. + * Application must always use either #pjsip_method_init or #pjsip_method_set + * to make sure that method name is initialized correctly. This way, the name + * member will always contain a valid method string regardless whether the ID + * is recognized or not. + */ +typedef struct pjsip_method +{ + pjsip_method_e id; /**< Method ID, from \a pjsip_method_e. */ + pj_str_t name; /**< Method name, which will always contain the + method string. */ +} pjsip_method; + + +/** + * Initialize the method structure from a string. + * This function will check whether the method is a known method then set + * both the id and name accordingly. + * + * @param m The method to initialize. + * @param pool Pool where memory allocation will be allocated from, if required. + * @param str The method string. + */ +PJ_DECL(void) pjsip_method_init( pjsip_method *m, + pj_pool_t *pool, + const pj_str_t *str); + +/** + * Initialize the method structure from a string, without cloning the string. + * See #pjsip_method_init. + * + * @param m The method structure to be initialized. + * @param str The method string. + */ +PJ_DECL(void) pjsip_method_init_np( pjsip_method *m, + pj_str_t *str); + +/** + * Set the method with the predefined method ID. + * This function will also set the name member of the structure to the correct + * string according to the method. + * + * @param m The method structure. + * @param id The method ID. + */ +PJ_DECL(void) pjsip_method_set( pjsip_method *m, pjsip_method_e id ); + + +/** + * Copy one method structure to another. If the method is of the known methods, + * then memory allocation is not required. + * + * @param pool Pool to allocate memory from, if required. + * @param method The destination method to copy to. + * @param rhs The source method to copy from. + */ +PJ_DECL(void) pjsip_method_copy( pj_pool_t *pool, + pjsip_method *method, + const pjsip_method *rhs ); + +/** + * Compare one method with another, and conveniently determine whether the + * first method is equal, less than, or greater than the second method. + * + * @param m1 The first method. + * @param m2 The second method. + * + * @return Zero if equal, otherwise will return -1 if less or +1 if greater. + */ +PJ_DECL(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR Header Fields General Structure. + * @brief General Header Fields Structure. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Header types, as defined by RFC3261. + */ +typedef enum pjsip_hdr_e +{ + /* + * These are the headers documented in RFC3261. Headers not documented + * there must have type PJSIP_H_OTHER, and the header type itself is + * recorded in the header name string. + * + * DO NOT CHANGE THE VALUE/ORDER OF THE HEADER IDs!!!. + */ + PJSIP_H_ACCEPT, + PJSIP_H_ACCEPT_ENCODING_UNIMP, + PJSIP_H_ACCEPT_LANGUAGE_UNIMP, + PJSIP_H_ALERT_INFO_UNIMP, + PJSIP_H_ALLOW, + PJSIP_H_AUTHENTICATION_INFO_UNIMP, + PJSIP_H_AUTHORIZATION, + PJSIP_H_CALL_ID, + PJSIP_H_CALL_INFO_UNIMP, + PJSIP_H_CONTACT, + PJSIP_H_CONTENT_DISPOSITION_UNIMP, + PJSIP_H_CONTENT_ENCODING_UNIMP, + PJSIP_H_CONTENT_LANGUAGE_UNIMP, + PJSIP_H_CONTENT_LENGTH, + PJSIP_H_CONTENT_TYPE, + PJSIP_H_CSEQ, + PJSIP_H_DATE_UNIMP, + PJSIP_H_ERROR_INFO_UNIMP, + PJSIP_H_EXPIRES, + PJSIP_H_FROM, + PJSIP_H_IN_REPLY_TO_UNIMP, + PJSIP_H_MAX_FORWARDS, + PJSIP_H_MIME_VERSION_UNIMP, + PJSIP_H_MIN_EXPIRES, + PJSIP_H_ORGANIZATION_UNIMP, + PJSIP_H_PRIORITY_UNIMP, + PJSIP_H_PROXY_AUTHENTICATE, + PJSIP_H_PROXY_AUTHORIZATION, + PJSIP_H_PROXY_REQUIRE_UNIMP, + PJSIP_H_RECORD_ROUTE, + PJSIP_H_REPLY_TO_UNIMP, + PJSIP_H_REQUIRE, + PJSIP_H_RETRY_AFTER, + PJSIP_H_ROUTE, + PJSIP_H_SERVER_UNIMP, + PJSIP_H_SUBJECT_UNIMP, + PJSIP_H_SUPPORTED, + PJSIP_H_TIMESTAMP_UNIMP, + PJSIP_H_TO, + PJSIP_H_UNSUPPORTED, + PJSIP_H_USER_AGENT_UNIMP, + PJSIP_H_VIA, + PJSIP_H_WARNING_UNIMP, + PJSIP_H_WWW_AUTHENTICATE, + + PJSIP_H_OTHER, + +} pjsip_hdr_e; + +/** + * This structure provides the pointer to basic functions that are needed + * for generic header operations. All header fields will have pointer to + * this structure, so that they can be manipulated uniformly. + */ +typedef struct pjsip_hdr_vptr +{ + /** + * Function to clone the header. + * + * @param pool Memory pool to allocate the new header. + * @param hdr Header to clone. + * + * @return A new instance of the header. + */ + void *(*clone)(pj_pool_t *pool, const void *hdr); + + /** + * Pointer to function to shallow clone the header. + * Shallow cloning will just make a memory copy of the original header, + * thus all pointers in original header will be kept intact. Because the + * function does not need to perform deep copy, the operation should be + * faster, but the application must make sure that the original header + * is still valid throughout the lifetime of new header. + * + * @param pool Memory pool to allocate the new header. + * @param hdr The header to clone. + */ + void *(*shallow_clone)(pj_pool_t *pool, const void *hdr); + + /** Pointer to function to print the header to the specified buffer. + * Returns the length of string written, or -1 if the remaining buffer + * is not enough to hold the header. + * + * @param hdr The header to print. + * @param buf The buffer. + * @param len The size of the buffer. + * + * @return The size copied to buffer, or -1 if there's not enough space. + */ + int (*print_on)(void *hdr, char *buf, pj_size_t len); + +} pjsip_hdr_vptr; + + +/** + * Generic fields for all SIP headers are declared using this macro, to make + * sure that all headers will have exactly the same layout in their start of + * the storage. This behaves like C++ inheritance actually. + */ +#define PJSIP_DECL_HDR_MEMBER(hdr) \ + /** List members. */ \ + PJ_DECL_LIST_MEMBER(hdr) \ + /** Header type */ \ + pjsip_hdr_e type; \ + /** Header name. */ \ + pj_str_t name; \ + /** Header short name version. */ \ + pj_str_t sname; \ + /** Virtual function table. */ \ + pjsip_hdr_vptr *vptr; + + +/** + * Generic SIP header structure, for generic manipulation for headers in the + * message. All header fields can be typecasted to this type. + */ +typedef struct pjsip_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_hdr) +} pjsip_hdr; + + +/** + * This generic function will clone any header, by calling "clone" function + * in header's virtual function table. + * + * @param pool The pool to allocate memory from. + * @param hdr The header to clone. + * + * @return A new instance copied from the original header. + */ +PJ_DECL(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr ); + + +/** + * This generic function will clone any header, by calling "shallow_clone" + * function in header's virtual function table. + * + * @param pool The pool to allocate memory from. + * @param hdr The header to clone. + * + * @return A new instance copied from the original header. + */ +PJ_DECL(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr ); + +/** + * This generic function will print any header, by calling "print" + * function in header's virtual function table. + * + * @param hdr The header to print. + * @param buf The buffer. + * @param len The size of the buffer. + * + * @return The size copied to buffer, or -1 if there's not enough space. + */ +PJ_DECL(int) pjsip_hdr_print_on( void *hdr, char *buf, pj_size_t len); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_LINE Request and Status Line. + * @brief Request and status line structures and manipulation. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * This structure describes SIP request line. + */ +typedef struct pjsip_request_line +{ + pjsip_method method; /**< Method for this request line. */ + pjsip_uri *uri; /**< URI for this request line. */ +} pjsip_request_line; + + +/** + * This structure describes SIP status line. + */ +typedef struct pjsip_status_line +{ + int code; /**< Status code. */ + pj_str_t reason; /**< Reason string. */ +} pjsip_status_line; + + +/** + * This enumeration lists standard SIP status codes according to RFC 3261. + * In addition, it also declares new status class 7xx for errors generated + * by the stack. This status class however should not get transmitted on the + * wire. + */ +typedef enum pjsip_status_code +{ + PJSIP_SC_TRYING = 100, + PJSIP_SC_RINGING = 180, + PJSIP_SC_CALL_BEING_FORWARDED = 181, + PJSIP_SC_QUEUED = 182, + PJSIP_SC_PROGRESS = 183, + + PJSIP_SC_OK = 200, + + PJSIP_SC_MULTIPLE_CHOICES = 300, + PJSIP_SC_MOVED_PERMANENTLY = 301, + PJSIP_SC_MOVED_TEMPORARILY = 302, + PJSIP_SC_USE_PROXY = 305, + PJSIP_SC_ALTERNATIVE_SERVICE = 380, + + PJSIP_SC_BAD_REQUEST = 400, + PJSIP_SC_UNAUTHORIZED = 401, + PJSIP_SC_PAYMENT_REQUIRED = 402, + PJSIP_SC_FORBIDDEN = 403, + PJSIP_SC_NOT_FOUND = 404, + PJSIP_SC_METHOD_NOT_ALLOWED = 405, + PJSIP_SC_NOT_ACCEPTABLE = 406, + PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED = 407, + PJSIP_SC_REQUEST_TIMEOUT = 408, + PJSIP_SC_GONE = 410, + PJSIP_SC_REQUEST_ENTITY_TOO_LARGE = 413, + PJSIP_SC_REQUEST_URI_TOO_LONG = 414, + PJSIP_SC_UNSUPPORTED_MEDIA_TYPE = 415, + PJSIP_SC_UNSUPPORTED_URI_SCHEME = 416, + PJSIP_SC_BAD_EXTENSION = 420, + PJSIP_SC_EXTENSION_REQUIRED = 421, + PJSIP_SC_INTERVAL_TOO_BRIEF = 423, + PJSIP_SC_TEMPORARILY_UNAVAILABLE = 480, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST = 481, + PJSIP_SC_LOOP_DETECTED = 482, + PJSIP_SC_TOO_MANY_HOPS = 483, + PJSIP_SC_ADDRESS_INCOMPLETE = 484, + PJSIP_AC_AMBIGUOUS = 485, + PJSIP_SC_BUSY_HERE = 486, + PJSIP_SC_REQUEST_TERMINATED = 487, + PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, + PJSIP_SC_REQUEST_PENDING = 491, + PJSIP_SC_UNDECIPHERABLE = 493, + + PJSIP_SC_INTERNAL_SERVER_ERROR = 500, + PJSIP_SC_NOT_IMPLEMENTED = 501, + PJSIP_SC_BAD_GATEWAY = 502, + PJSIP_SC_SERVICE_UNAVAILABLE = 503, + PJSIP_SC_SERVER_TIMEOUT = 504, + PJSIP_SC_VERSION_NOT_SUPPORTED = 505, + PJSIP_SC_MESSAGE_TOO_LARGE = 513, + + PJSIP_SC_BUSY_EVERYWHERE = 600, + PJSIP_SC_DECLINE = 603, + PJSIP_SC_DOES_NOT_EXIST_ANYWHERE = 604, + PJSIP_SC_NOT_ACCEPTABLE_ANYWHERE = 606, + + PJSIP_SC_TSX_TIMEOUT = 701, + PJSIP_SC_TSX_RESOLVE_ERROR = 702, + PJSIP_SC_TSX_TRANSPORT_ERROR = 703, + +} pjsip_status_code; + +/** + * Get the default status text for the status code. + * + * @param status_code SIP Status Code + * + * @return textual message for the status code. + */ +PJ_DECL(const pj_str_t*) pjsip_get_status_text(int status_code); + +/** + * This macro returns non-zero (TRUE) if the specified status_code is + * in the same class as the code_class. + * + * @param status_code The status code. + * @param code_class The status code in the class (for example 100, 200). + */ +#define PJSIP_IS_STATUS_IN_CLASS(status_code, code_class) \ + (status_code/100 == code_class/100) + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_MEDIA Media Type + * @brief Media type definitions and manipulations. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * This structure describes SIP media type, as used for example in + * Accept and Content-Type header.. + */ +typedef struct pjsip_media_type +{ + pj_str_t type; /**< Media type. */ + pj_str_t subtype; /**< Media subtype. */ + pj_str_t param; /**< Media type parameters (concatenated). */ +} pjsip_media_type; + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_BODY Message Body + * @brief SIP message body structures and manipulation. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Generic abstraction to message body. + * When an incoming message is parsed (pjsip_parse_msg()), the parser fills in + * all members with the appropriate value. The 'data' and 'len' member will + * describe portion of incoming packet which denotes the message body. + * When application needs to attach message body to outgoing SIP message, it + * must fill in all members of this structure. + */ +typedef struct pjsip_msg_body +{ + /** MIME content type. + * For incoming messages, the parser will fill in this member with the + * content type found in Content-Type header. + * + * For outgoing messages, application must fill in this member with + * appropriate value, because the stack will generate Content-Type header + * based on the value specified here. + */ + pjsip_media_type content_type; + + /** Pointer to buffer which holds the message body data. + * For incoming messages, the parser will fill in this member with the + * pointer to the body string. + * + * When sending outgoing message, this member doesn't need to point to the + * actual message body string. It can be assigned with arbitrary pointer, + * because the value will only need to be understood by the print_body() + * function. The stack itself will not try to interpret this value, but + * instead will always call the print_body() whenever it needs to get the + * actual body string. + */ + void *data; + + /** The length of the data. + * For incoming messages, the parser will fill in this member with the + * actual length of message body. + * + * When sending outgoing message, again just like the "data" member, the + * "len" member doesn't need to point to the actual length of the body + * string. + */ + unsigned len; + + /** Pointer to function to print this message body. + * Application must set a proper function here when sending outgoing + * message. + * + * @param msg_body This structure itself. + * @param buf The buffer. + * @param size The buffer size. + * + * @return The length of the string printed, or -1 if there is + * not enough space in the buffer to print the whole + * message body. + */ + int (*print_body)(struct pjsip_msg_body *msg_body, + char *buf, pj_size_t size); + +} pjsip_msg_body; + +/** + * General purpose function to textual data in a SIP body. Attach this function + * in a SIP message body only if the data in pjsip_msg_body is a textual + * message ready to be embedded in a SIP message. If the data in the message + * body is not a textual body, then application must supply a custom function + * to print that body. + * + * @param msg_body The message body. + * @param buf Buffer to copy the message body to. + * @param size The size of the buffer. + * + * @return The length copied to the buffer, or -1. + */ +PJ_DECL(int) pjsip_print_text_body( pjsip_msg_body *msg_body, + char *buf, pj_size_t size); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_MSG Message Structure + * @brief Message structure and operations. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Message type (request or response). + */ +typedef enum pjsip_msg_type_e +{ + PJSIP_REQUEST_MSG, /**< Indicates request message. */ + PJSIP_RESPONSE_MSG, /**< Indicates response message. */ +} pjsip_msg_type_e; + + +/** + * This structure describes a SIP message. + */ +struct pjsip_msg +{ + /** Message type (ie request or response). */ + pjsip_msg_type_e type; + + /** The first line of the message can be either request line for request + * messages, or status line for response messages. It is represented here + * as a union. + */ + union + { + /** Request Line. */ + struct pjsip_request_line req; + + /** Status Line. */ + struct pjsip_status_line status; + } line; + + /** List of message headers. */ + pjsip_hdr hdr; + + /** Pointer to message body, or NULL if no message body is attached to + * this mesage. + */ + pjsip_msg_body *body; +}; + + +/** + * Create new request or response message. + * + * @param pool The pool. + * @param type Message type. + * @return New message, or THROW exception if failed. + */ +PJ_DECL(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type); + +/** + * Find a header in the message by the header type. + * + * @param msg The message. + * @param type The header type to find. + * @param start The first header field where the search should begin. + * If NULL is specified, then the search will begin from the + * first header, otherwise the search will begin at the + * specified header. + * + * @return The header field, or NULL if no header with the specified + * type is found. + */ +PJ_DECL(void*) pjsip_msg_find_hdr( pjsip_msg *msg, + pjsip_hdr_e type, void *start); + +/** + * Find a header in the message by its name. + * + * @param msg The message. + * @param name The header name to find. + * @param start The first header field where the search should begin. + * If NULL is specified, then the search will begin from the + * first header, otherwise the search will begin at the + * specified header. + * + * @return The header field, or NULL if no header with the specified + * type is found. + */ +PJ_DECL(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg, + const pj_str_t *name, void *start); + +/** + * Find and remove a header in the message. + * + * @param msg The message. + * @param hdr The header type to find. + * @param start The first header field where the search should begin, + * or NULL to search from the first header in the message. + * + * @return The header field, or NULL if not found. + */ +PJ_DECL(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, + pjsip_hdr_e hdr, void *start); + +/** + * Add a header to the message, putting it last in the header list. + * + * @param msg The message. + * @param hdr The header to add. + * + * @bug Once the header is put in a list (or message), it can not be put in + * other list (or message). Otherwise Real Bad Thing will happen. + */ +PJ_IDECL(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr ); + +/** + * Add header field to the message, putting it in the front of the header list. + * + * @param msg The message. + * @param hdr The header to add. + * + * @bug Once the header is put in a list (or message), it can not be put in + * other list (or message). Otherwise Real Bad Thing will happen. + */ +PJ_IDECL(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr ); + +/** + * Print the message to the specified buffer. + * + * @param msg The message to print. + * @param buf The buffer + * @param size The size of the buffer. + * + * @return The length of the printed characters (in bytes), or NEGATIVE + * value if the message is too large for the specified buffer. + */ +PJ_DECL(int) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_HDR_GEN Header Field: Generic + * @brief Generic header field which contains header name and value. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Generic SIP header, which contains hname and a string hvalue. + * Note that this header is not supposed to be used as 'base' class for headers. + */ +typedef struct pjsip_generic_string_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_string_hdr) /**< Standard header field. */ + pj_str_t hvalue; /**< hvalue */ +} pjsip_generic_string_hdr; + + +/** + * Create a new instance of generic header. A generic header can have an + * arbitrary header name. + * + * @param pool The pool. + * @param hname The header name to be assigned to the header, or NULL to + * assign the header name with some string. + * + * @return The header, or THROW exception. + */ +PJ_DECL(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, + const pj_str_t *hname ); + +/** + * Create a generic header along with the text content. + * + * @param pool The pool. + * @param hname The header name. + * @param hvalue The header text content. + * + * @return The header instance. + */ +PJ_DECL(pjsip_generic_string_hdr*) +pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool, + const pj_str_t *hname, + const pj_str_t *hvalue); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @addtogroup PJSIP_MSG_HDR_GEN_INT Header Field: Generic Integer + * @brief Generic header field which contains header name and value. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * Generic SIP header, which contains hname and a string hvalue. + */ +typedef struct pjsip_generic_int_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_int_hdr) /**< Standard header field. */ + pj_int32_t ivalue; /**< ivalue */ +} pjsip_generic_int_hdr; + + +/** + * Create a new instance of generic header. A generic header can have an + * arbitrary header name. + * + * @param pool The pool. + * @param hname The header name to be assigned to the header, or NULL to + * assign the header name with some string. + * + * @return The header, or THROW exception. + */ +PJ_DECL(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, + const pj_str_t *hname ); + +/** + * Create a generic header along with the value. + * + * @param pool The pool. + * @param hname The header name. + * @param value The header value content. + * + * @return The header instance. + */ +PJ_DECL(pjsip_generic_int_hdr*) +pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool, + const pj_str_t *hname, + int value); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_GENERIC_LIST Header Field: Generic string list. + * @brief Header with list of strings separated with comma + * @ingroup PJSIP_MSG + * @{ + */ + +/** Maximum elements in the header array. */ +#define PJSIP_GENERIC_ARRAY_MAX_COUNT 32 + +typedef struct pjsip_generic_array_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_generic_array_hdr) + unsigned count; /**< Number of elements. */ + pj_str_t values[PJSIP_GENERIC_ARRAY_MAX_COUNT]; /**< Elements. */ +} pjsip_generic_array_hdr; + +/** + * Create generic array header. + * + * @param pool Pool to allocate memory from. + * + * @return New generic array header. + */ +PJ_DECL(pjsip_generic_array_hdr*) pjsip_generic_array_create(pj_pool_t *pool, + const pj_str_t *hnames); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_ACCEPT Header Field: Accept + * @brief Accept header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** Accept header. */ +typedef pjsip_generic_array_hdr pjsip_accept_hdr; + +/** Maximum fields in Accept header. */ +#define PJSIP_MAX_ACCEPT_COUNT PJSIP_GENERIC_ARRAY_MAX_COUNT + +/** + * Create new Accept header instance. + * + * @param pool The pool. + * + * @return New Accept header instance. + */ +PJ_DECL(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_ALLOW Header Field: Allow + * @brief Allow header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_allow_hdr; + +/** + * Create new Allow header instance. + * + * @param pool The pool. + * + * @return New Allow header instance. + */ +PJ_DECL(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CID Header Field: Call-ID + * @brief Call-ID header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Call-ID header. + */ +typedef struct pjsip_cid_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_cid_hdr) + pj_str_t id; /**< Call-ID string. */ +} pjsip_cid_hdr; + + +/** + * Create new Call-ID header. + * + * @param pool The pool. + * + * @return new Call-ID header. + */ +PJ_DECL(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CLEN Header Field: Content-Length + * @brief Content-Length header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Content-Length header. + */ +typedef struct pjsip_clen_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_clen_hdr) + int len; /**< Content length. */ +} pjsip_clen_hdr; + +/** + * Create new Content-Length header. + * + * @param pool the pool. + * @return A new Content-Length header instance. + */ +PJ_DECL(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CSEQ Header Field: CSeq + * @brief CSeq header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * CSeq header. + */ +typedef struct pjsip_cseq_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_cseq_hdr) + int cseq; /**< CSeq number. */ + pjsip_method method; /**< CSeq method. */ +} pjsip_cseq_hdr; + + +/** Create new CSeq header. + * + * @param pool The pool. + * @return A new CSeq header instance. + */ +PJ_DECL(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CONTACT Header Field: Contact + * @brief Contact header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Contact header. + * In this library, contact header only contains single URI. If a message has + * multiple URI in the Contact header, the URI will be put in separate Contact + * headers. + */ +typedef struct pjsip_contact_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_contact_hdr) + int star; /**< The contact contains only a '*' character */ + pjsip_uri *uri; /**< URI in the contact. */ + int q1000; /**< The "q" value times 1000 (to avoid float) */ + pj_int32_t expires; /**< Expires parameter, otherwise -1 if not present. */ + pj_str_t other_param; /**< Other parameters, concatenated in a single string. */ +} pjsip_contact_hdr; + + +/** + * Create a new Contact header. + * + * @param pool The pool. + * @return A new instance of Contact header. + */ +PJ_DECL(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_CTYPE Header Field: Content-Type + * @brief Content-Type header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Content-Type. + */ +typedef struct pjsip_ctype_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_ctype_hdr) + pjsip_media_type media; /**< Media type. */ +} pjsip_ctype_hdr; + + +/** + * Create a nwe Content Type header. + * + * @param pool The pool. + * @return A new Content-Type header. + */ +PJ_DECL(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_EXPIRES Header Field: Expires + * @brief Expires header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** Expires header. */ +typedef pjsip_generic_int_hdr pjsip_expires_hdr; + +/** + * Create a new Expires header. + * + * @param pool The pool. + * @return A new Expires header. + */ +PJ_DECL(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_FROMTO Header Field: From/To + * @brief From and To header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * To or From header. + */ +typedef struct pjsip_fromto_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_fromto_hdr) + pjsip_uri *uri; /**< URI in From/To header. */ + pj_str_t tag; /**< Header "tag" parameter. */ + pj_str_t other_param; /**< Other params, concatenated as a single string. */ +} pjsip_fromto_hdr; + +/** Alias for From header. */ +typedef pjsip_fromto_hdr pjsip_from_hdr; + +/** Alias for To header. */ +typedef pjsip_fromto_hdr pjsip_to_hdr; + +/** + * Create a From header. + * + * @param pool The pool. + * @return New instance of From header. + */ +PJ_DECL(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ); + +/** + * Create a To header. + * + * @param pool The pool. + * @return New instance of To header. + */ +PJ_DECL(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ); + +/** + * Convert the header to a From header. + * + * @param pool The pool. + * @return "From" header. + */ +PJ_DECL(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr ); + +/** + * Convert the header to a To header. + * + * @param pool The pool. + * @return "To" header. + */ +PJ_DECL(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr ); + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_MAX_FORWARDS Header Field: Max-Forwards + * @brief Max-Forwards header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_int_hdr pjsip_max_forwards_hdr; + +/** + * Create new Max-Forwards header instance. + * + * @param pool The pool. + * + * @return New Max-Forwards header instance. + */ +PJ_DECL(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_MIN_EXPIRES Header Field: Min-Expires + * @brief Min-Expires header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_int_hdr pjsip_min_expires_hdr; + +/** + * Create new Max-Forwards header instance. + * + * @param pool The pool. + * + * @return New Max-Forwards header instance. + */ +PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_ROUTING Header Field: Record-Route/Route + * @brief Record-Route and Route header fields. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * Record-Route and Route headers. + */ +typedef struct pjsip_routing_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_routing_hdr) /**< Generic header fields. */ + pjsip_name_addr name_addr; /**< The URL in the Route/Record-Route header. */ + pj_str_t other_param; /** Other parameter. */ +} pjsip_routing_hdr; + +/** Alias for Record-Route header. */ +typedef pjsip_routing_hdr pjsip_rr_hdr; + +/** Alias for Route header. */ +typedef pjsip_routing_hdr pjsip_route_hdr; + + +/** + * Create new Record-Route header from the pool. + * + * @param pool The pool. + * @return A new instance of Record-Route header. + */ +PJ_DECL(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ); + +/** + * Create new Route header from the pool. + * + * @param pool The pool. + * @return A new instance of "Route" header. + */ +PJ_DECL(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ); + +/** + * Convert generic routing header to Record-Route header. + * + * @param r The generic routing header, or a "Routing" header. + * @return Record-Route header. + */ +PJ_DECL(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *r ); + +/** + * Convert generic routing header to "Route" header. + * + * @param r The generic routing header, or a "Record-Route" header. + * @return "Route" header. + */ +PJ_DECL(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *r ); + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_REQUIRE Header Field: Require + * @brief Require header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_require_hdr; + +/** + * Create new Require header instance. + * + * @param pool The pool. + * + * @return New Require header instance. + */ +PJ_DECL(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_RETRY_AFTER Header Field: Retry-After + * @brief Retry-After header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_int_hdr pjsip_retry_after_hdr; + +/** + * Create new Retry-After header instance. + * + * @param pool The pool. + * + * @return New Retry-After header instance. + */ +PJ_DECL(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_SUPPORTED Header Field: Supported + * @brief Supported header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_supported_hdr; + +/** + * Create new Supported header instance. + * + * @param pool The pool. + * + * @return New Supported header instance. + */ +PJ_DECL(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_UNSUPPORTED Header Field: Unsupported + * @brief Unsupported header field. + * @ingroup PJSIP_MSG + * @{ + */ +typedef pjsip_generic_array_hdr pjsip_unsupported_hdr; + +/** + * Create new Unsupported header instance. + * + * @param pool The pool. + * + * @return New Unsupported header instance. + */ +PJ_DECL(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool); + + +/** + * @} + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_VIA Header Field: Via + * @brief Via header field. + * @ingroup PJSIP_MSG + * @{ + */ +/** + * SIP Via header. + * In this implementation, Via header can only have one element in each header. + * If a message arrives with multiple elements in a single Via, then they will + * be split up into multiple Via headers. + */ +typedef struct pjsip_via_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_via_hdr) + pj_str_t transport; /**< Transport type. */ + pjsip_host_port sent_by; /**< Host and optional port */ + int ttl_param; /**< TTL parameter, or -1 if it's not specified. */ + int rport_param; /**< "rport" parameter, 0 to specify without + port number, -1 means doesn't exist. */ + pj_str_t maddr_param; /**< "maddr" parameter. */ + pj_str_t recvd_param; /**< "received" parameter. */ + pj_str_t branch_param; /**< "branch" parameter. */ + pj_str_t other_param; /**< Other parameters, concatenated as single string. */ + pj_str_t comment; /**< Comment. */ +} pjsip_via_hdr; + +/** + * Create a new Via header. + * + * @param pool The pool. + * @return A new "Via" header instance. + */ +PJ_DECL(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ); + +/** + * @} + */ + +/** + * @bug Once a header is put in the message, the header CAN NOT be put in + * other list. Solution: + * - always clone header in the message. + * - create a list node for each header in the message. + */ + + +/////////////////////////////////////////////////////////////////////////////// +/** + * @defgroup PJSIP_MSG_HDR_UNIMP Unimplemented Header Fields + * @brief Unimplemented header fields. + * @ingroup PJSIP_MSG + * @{ + */ +/** Accept-Encoding header. */ +typedef pjsip_generic_string_hdr pjsip_accept_encoding_hdr; + +/** Create Accept-Encoding header. */ +#define pjsip_accept_encoding_hdr_create pjsip_generic_string_hdr_create + +/** Accept-Language header. */ +typedef pjsip_generic_string_hdr pjsip_accept_lang_hdr; + +/** Create Accept-Language header. */ +#define pjsip_accept_lang_hdr_create pjsip_generic_string_hdr_create + +/** Alert-Info header. */ +typedef pjsip_generic_string_hdr pjsip_alert_info_hdr; + +/** Create Alert-Info header. */ +#define pjsip_alert_info_hdr_create pjsip_generic_string_hdr_create + +/** Authentication-Info header. */ +typedef pjsip_generic_string_hdr pjsip_auth_info_hdr; + +/** Create Authentication-Info header. */ +#define pjsip_auth_info_hdr_create pjsip_generic_string_hdr_create + +/** Call-Info header. */ +typedef pjsip_generic_string_hdr pjsip_call_info_hdr; + +/** Create Call-Info header. */ +#define pjsip_call_info_hdr_create pjsip_generic_string_hdr_create + +/** Content-Disposition header. */ +typedef pjsip_generic_string_hdr pjsip_content_disposition_hdr; + +/** Create Content-Disposition header. */ +#define pjsip_content_disposition_hdr_create pjsip_generic_string_hdr_create + +/** Content-Encoding header. */ +typedef pjsip_generic_string_hdr pjsip_content_encoding_hdr; + +/** Create Content-Encoding header. */ +#define pjsip_content_encoding_hdr_create pjsip_generic_string_hdr_create + +/** Content-Language header. */ +typedef pjsip_generic_string_hdr pjsip_content_lang_hdr; + +/** Create Content-Language header. */ +#define pjsip_content_lang_hdr_create pjsip_generic_string_hdr_create + +/** Date header. */ +typedef pjsip_generic_string_hdr pjsip_date_hdr; + +/** Create Date header. */ +#define pjsip_date_hdr_create pjsip_generic_string_hdr_create + +/** Error-Info header. */ +typedef pjsip_generic_string_hdr pjsip_err_info_hdr; + +/** Create Error-Info header. */ +#define pjsip_err_info_hdr_create pjsip_generic_string_hdr_create + +/** In-Reply-To header. */ +typedef pjsip_generic_string_hdr pjsip_in_reply_to_hdr; + +/** Create In-Reply-To header. */ +#define pjsip_in_reply_to_hdr_create pjsip_generic_string_hdr_create + +/** MIME-Version header. */ +typedef pjsip_generic_string_hdr pjsip_mime_version_hdr; + +/** Create MIME-Version header. */ +#define pjsip_mime_version_hdr_create pjsip_generic_string_hdr_create + +/** Organization header. */ +typedef pjsip_generic_string_hdr pjsip_organization_hdr; + +/** Create Organization header. */ +#define pjsip_organization_hdr_create pjsip_genric_string_hdr_create + +/** Priority header. */ +typedef pjsip_generic_string_hdr pjsip_priority_hdr; + +/** Create Priority header. */ +#define pjsip_priority_hdr_create pjsip_generic_string_hdr_create + +/** Proxy-Require header. */ +typedef pjsip_generic_string_hdr pjsip_proxy_require_hdr; + +/** Reply-To header. */ +typedef pjsip_generic_string_hdr pjsip_reply_to_hdr; + +/** Create Reply-To header. */ +#define pjsip_reply_to_hdr_create pjsip_generic_string_hdr_create + +/** Server header. */ +typedef pjsip_generic_string_hdr pjsip_server_hdr; + +/** Create Server header. */ +#define pjsip_server_hdr_create pjsip_generic_string_hdr_create + +/** Subject header. */ +typedef pjsip_generic_string_hdr pjsip_subject_hdr; + +/** Create Subject header. */ +#define pjsip_subject_hdr_create pjsip_generic_string_hdr_create + +/** Timestamp header. */ +typedef pjsip_generic_string_hdr pjsip_timestamp_hdr; + +/** Create Timestamp header. */ +#define pjsip_timestamp_hdr_create pjsip_generic_string_hdr_create + +/** User-Agent header. */ +typedef pjsip_generic_string_hdr pjsip_user_agent_hdr; + +/** Create User-Agent header. */ +#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create + +/** Warning header. */ +typedef pjsip_generic_string_hdr pjsip_warning_hdr; + +/** Create Warning header. */ +#define pjsip_warning_hdr_create pjsip_generic_string_hdr_create + +/** + * @} + */ + +/** + * @} // PJSIP_MSG + */ + +/* + * Include inline definitions. + */ +#if PJ_FUNCTIONS_ARE_INLINED +# include +#endif + + +PJ_END_DECL + +#endif /* __PJSIP_SIP_MSG_H__ */ + diff --git a/pjsip/src/pjsip/sip_msg_i.h b/pjsip/src/pjsip/sip_msg_i.h index afe5c2dba..ff98c9ea7 100644 --- a/pjsip/src/pjsip/sip_msg_i.h +++ b/pjsip/src/pjsip/sip_msg_i.h @@ -1,12 +1,12 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_msg_i.h 2 2/24/05 10:46a Bennylp $ */ - -PJ_IDEF(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) -{ - pj_list_insert_before(&msg->hdr, hdr); -} - -PJ_IDEF(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) -{ - pj_list_insert_after(&msg->hdr, hdr); -} - +/* $Header: /pjproject/pjsip/src/pjsip/sip_msg_i.h 2 2/24/05 10:46a Bennylp $ */ + +PJ_IDEF(void) pjsip_msg_add_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) +{ + pj_list_insert_before(&msg->hdr, hdr); +} + +PJ_IDEF(void) pjsip_msg_insert_first_hdr( pjsip_msg *msg, pjsip_hdr *hdr ) +{ + pj_list_insert_after(&msg->hdr, hdr); +} + diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 34e6d4d91..211628366 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1,1551 +1,1551 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_parser.c 17 9/11/05 9:28a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* tolower() */ - -#define RESERVED ";/?:@&=+$," -#define MARK "-_.!~*'()" -#define ESCAPED "%" -#define USER "&=+$,;?/" -#define PASS "&=+$," -#define TOKEN "-.!%*_=`'~+" /* '+' is because of application/pidf+xml in Content-Type! */ -#define HOST "_-." -#define HEX_DIGIT "abcdefABCDEF" -#define PARAM_CHAR "[]/:&+$" MARK "%" - -#define PJSIP_VERSION "SIP/2.0" -#define PJSIP_SYN_ERR_EXCEPTION 1 - -#define UNREACHED(expr) - -typedef struct handler_rec -{ - char hname[PJSIP_MAX_HNAME_LEN+1]; - pj_size_t hname_len; - pj_uint32_t hname_hash; - pjsip_parse_hdr_func *handler; -} handler_rec; - -static handler_rec handler[127]; -static unsigned handler_count; -static int parser_is_initialized; - - -/* - * Global vars (also extern). - */ -const pj_str_t pjsip_USER_STR = { "user", 4}; -const pj_str_t pjsip_METHOD_STR = { "method", 6}; -const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9}; -const pj_str_t pjsip_MADDR_STR = { "maddr", 5 }; -const pj_str_t pjsip_LR_STR = { "lr", 2 }; -const pj_str_t pjsip_SIP_STR = { "sip", 3 }; -const pj_str_t pjsip_SIPS_STR = { "sips", 4 }; -const pj_str_t pjsip_TEL_STR = { "tel", 3 }; -const pj_str_t pjsip_BRANCH_STR = { "branch", 6 }; -const pj_str_t pjsip_TTL_STR = { "ttl", 3 }; -const pj_str_t pjsip_PNAME_STR = { "received", 8 }; -const pj_str_t pjsip_Q_STR = { "q", 1 }; -const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 }; -const pj_str_t pjsip_TAG_STR = { "tag", 3 }; -const pj_str_t pjsip_RPORT_STR = { "rport", 5}; - -pj_char_spec pjsip_HOST_SPEC, /* For scanning host part. */ - pjsip_DIGIT_SPEC, /* Decimal digits */ - pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */ - pjsip_ALNUM_SPEC, /* Decimal + Alpha. */ - pjsip_TOKEN_SPEC, /* Token. */ - pjsip_HEX_SPEC, /* Hexadecimal digits. */ - pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when it's not quoted.) */ - pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */ - pjsip_PASSWD_SPEC, /* Password. */ - pjsip_USER_SPEC, /* User */ - pjsip_ARRAY_ELEMENTS, /* Array separator. */ - pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/ - pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name in URL. */ - - -/* - * Forward decl. - */ -static pjsip_msg * int_parse_msg( pj_scanner *scanner, - pj_pool_t *pool, - pjsip_parser_err_report *err_list); -static void int_parse_param( pj_scanner *scanner, - pj_str_t *pname, - pj_str_t *pvalue); -static void int_parse_req_line( pj_scanner *scanner, - pj_pool_t *pool, - pjsip_request_line *req_line); -static int int_is_next_user( pj_scanner *scanner); -static void int_parse_status_line( pj_scanner *scanner, - pjsip_status_line *line); -static void int_parse_user_pass( pj_scanner *scanner, - pj_str_t *user, - pj_str_t *pass); -static void int_parse_uri_host_port( pj_scanner *scanner, - pj_str_t *p_host, - int *p_port); -static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner, - pj_pool_t *pool, unsigned option); -static pjsip_url * int_parse_sip_url( pj_scanner *scanner, - pj_pool_t *pool, - pj_bool_t parse_params); -static pjsip_name_addr* int_parse_name_addr( pj_scanner *scanner, - pj_pool_t *pool ); -static void parse_hdr_end( pj_scanner *scanner ); -static pjsip_accept_hdr* parse_hdr_accept( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_allow_hdr* parse_hdr_allow( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_cid_hdr* parse_hdr_call_id( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_contact_hdr* parse_hdr_contact( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_clen_hdr* parse_hdr_content_length( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_ctype_hdr* parse_hdr_content_type( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_cseq_hdr* parse_hdr_cseq( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_expires_hdr* parse_hdr_expires( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_from_hdr* parse_hdr_from( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_max_forwards_hdr* parse_hdr_max_forwards( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_min_expires_hdr* parse_hdr_min_expires( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_rr_hdr* parse_hdr_rr( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_route_hdr* parse_hdr_route( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_retry_after_hdr* parse_hdr_retry_after( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_supported_hdr* parse_hdr_supported( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_to_hdr* parse_hdr_to( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_unsupported_hdr* parse_hdr_unsupported( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_via_hdr* parse_hdr_via( pj_scanner *scanner, - pj_pool_t *pool); -static pjsip_generic_string_hdr* parse_hdr_generic_string( pj_scanner *scanner, - pj_pool_t *pool); - -/* Convert non NULL terminated string to integer. */ -static unsigned long pj_strtoul_mindigit(const pj_str_t *str, unsigned mindig) -{ - unsigned long value; - unsigned i; - - value = 0; - for (i=0; i<(unsigned)str->slen; ++i) { - value = value * 10 + (str->ptr[i] - '0'); - } - for (; islen + pname->slen + pvalue->slen + 3; - p = new_param = pj_pool_alloc(pool, len); - - if (param->slen) { - int old_len = param->slen; - pj_memcpy(p, param->ptr, old_len); - p += old_len; - } - *p++ = (char)sepchar; - pj_memcpy(p, pname->ptr, pname->slen); - p += pname->slen; - - if (pvalue->slen) { - *p++ = '='; - pj_memcpy(p, pvalue->ptr, pvalue->slen); - p += pvalue->slen; - } - - *p = '\0'; - - param->ptr = new_param; - param->slen = p - new_param; -} - -/* Concatenate unrecognized params into single string. */ -static void concat_param( pj_str_t *param, pj_pool_t *pool, - const pj_str_t *pname, const pj_str_t *pvalue ) -{ - pjsip_concat_param_imp(param, pool, pname, pvalue, ';'); -} - -/* Initialize static properties of the parser. */ -static void init_parser() -{ - static int initialized; - - if (initialized) - return; - - initialized = 1; - - pj_cs_add_num( pjsip_DIGIT_SPEC ); - pj_cs_add_alpha( pjsip_ALPHA_SPEC ); - - pj_cs_add_alpha( pjsip_ALNUM_SPEC ); - pj_cs_add_num( pjsip_ALNUM_SPEC ); - - pj_cs_add_str(pjsip_NEWLINE_OR_EOF_SPEC, "\r\n"); - //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0); - - pj_cs_add_str( pjsip_ARRAY_ELEMENTS, ",\r\n"); - - pj_memcpy(pjsip_TOKEN_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); - pj_cs_add_str( pjsip_TOKEN_SPEC, TOKEN); - - pj_memcpy(pjsip_HOST_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); - pj_cs_add_str( pjsip_HOST_SPEC, HOST); - - pj_memcpy(pjsip_HEX_SPEC, pjsip_DIGIT_SPEC, sizeof(pj_char_spec)); - pj_cs_add_str( pjsip_HEX_SPEC, HEX_DIGIT); - - pj_memcpy(pjsip_PARAM_CHAR_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); - pj_cs_add_str( pjsip_PARAM_CHAR_SPEC, PARAM_CHAR); - - pj_memcpy(pjsip_USER_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); - pj_cs_add_str( pjsip_USER_SPEC, MARK ESCAPED USER ); - - pj_memcpy(pjsip_PASSWD_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); - pj_cs_add_str( pjsip_PASSWD_SPEC, MARK ESCAPED PASS); - - pj_cs_add_str( pjsip_PROBE_USER_HOST_SPEC, "@ \n>"); - pj_cs_invert( pjsip_PROBE_USER_HOST_SPEC ); - - pj_cs_add_str( pjsip_DISPLAY_SCAN_SPEC, ":\r\n<"); - - pjsip_register_hdr_parser( "Accept", NULL, (pjsip_parse_hdr_func*) &parse_hdr_accept); - pjsip_register_hdr_parser( "Allow", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow); - pjsip_register_hdr_parser( "Call-ID", NULL, (pjsip_parse_hdr_func*) &parse_hdr_call_id); - pjsip_register_hdr_parser( "Contact", "m", (pjsip_parse_hdr_func*) &parse_hdr_contact); - pjsip_register_hdr_parser( "Content-Length", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_length); - pjsip_register_hdr_parser( "Content-Type", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_type); - pjsip_register_hdr_parser( "CSeq", NULL, (pjsip_parse_hdr_func*) &parse_hdr_cseq); - pjsip_register_hdr_parser( "Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_expires); - pjsip_register_hdr_parser( "From", "f", (pjsip_parse_hdr_func*) &parse_hdr_from); - pjsip_register_hdr_parser( "Max-Forwards", NULL, (pjsip_parse_hdr_func*) &parse_hdr_max_forwards); - pjsip_register_hdr_parser( "Min-Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_min_expires); - pjsip_register_hdr_parser( "Record-Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_rr); - pjsip_register_hdr_parser( "Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_route); - pjsip_register_hdr_parser( "Require", NULL, (pjsip_parse_hdr_func*) &parse_hdr_require); - pjsip_register_hdr_parser( "Retry-After", NULL, (pjsip_parse_hdr_func*) &parse_hdr_retry_after); - pjsip_register_hdr_parser( "Supported", "k", (pjsip_parse_hdr_func*) &parse_hdr_supported); - pjsip_register_hdr_parser( "To", "t", (pjsip_parse_hdr_func*) &parse_hdr_to); - pjsip_register_hdr_parser( "Unsupported", NULL, (pjsip_parse_hdr_func*) &parse_hdr_unsupported); - pjsip_register_hdr_parser( "Via", NULL, (pjsip_parse_hdr_func*) &parse_hdr_via); - - /* Register auth parser. */ - pjsip_auth_init_parser(); - -} - -static void init_sip_parser() -{ - if (!parser_is_initialized) { - /* Prevent race cond. */ - pj_enter_critical_section(); - if (!parser_is_initialized) { - init_parser(); - parser_is_initialized = 1; - } - pj_leave_critical_section(); - } -} - -/* Compare the handler record with header name, and return: - * - 0 if handler match. - * - <0 if handler is 'less' than the header name. - * - >0 if handler is 'greater' than header name. - */ -static int compare_handler( const handler_rec *r1, - const char *name, - pj_size_t name_len, - pj_uint32_t hash ) -{ - /* Compare length. */ - if (r1->hname_len < name_len) - return -1; - if (r1->hname_len > name_len) - return 1; - - /* Length is equal, compare hashed value. */ - if (r1->hname_hash < hash) - return -1; - if (r1->hname_hash > hash) - return 1; - - /* Equal length and equal hash. compare the strings. */ - return strcmp(r1->hname, name); -} - -/* Register one handler for one header name. */ -static int int_register_parser( const char *name, pjsip_parse_hdr_func *fptr ) -{ - unsigned pos; - handler_rec rec; - unsigned i; - - if (handler_count >= PJ_ARRAY_SIZE(handler)) { - return -1; - } - - /* Initialize temporary handler. */ - rec.handler = fptr; - rec.hname_len = strlen(name); - if (rec.hname_len >= sizeof(rec.hname)) { - return -1; - } - /* Name is copied in lowercase. */ - for (i=0; i 0) { - break; - } - } - - /* Shift handlers. */ - if (pos != handler_count) { - pj_memmove( &handler[pos+1], &handler[pos], (handler_count-pos)*sizeof(handler_rec)); - } - /* Add new handler. */ - pj_memcpy( &handler[pos], &rec, sizeof(handler_rec)); - ++handler_count; - - return 0; -} - -/* Register parser handler. If both header name and short name are valid, - * then two instances of handler will be registered. - */ -PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname, - const char *hshortname, - pjsip_parse_hdr_func *fptr) -{ - if (int_register_parser(hname, fptr)) { - return -1; - } - if (hshortname && int_register_parser(hshortname, fptr)) { - return -1; - } - return 0; -} - -/* Find handler to parse the header name. */ -static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname) -{ - handler_rec *first; - char hname_copy[PJSIP_MAX_HNAME_LEN]; - pj_uint32_t hash; - int comp; - unsigned i, n; - - /* Calculate hash value while converting the header to lowercase. - * Don't assume that 'hname' is NULL terminated. - */ - hash = 0; - for (i=0; i<(unsigned)hname->slen; ++i) { - hname_copy[i] = (char)tolower(hname->ptr[i]); - hash = hash * PJ_HASH_MULTIPLIER + hname_copy[i]; - } - hname_copy[i] = '\0'; - - /* Binary search for the handler. */ - comp = -1; - first = &handler[0]; - n = handler_count; - for (; n > 0; ) { - unsigned half = n / 2; - handler_rec *mid = first + half; - - comp = compare_handler(mid, hname_copy, hname->slen, hash); - if (comp < 0) { - first = ++mid; - n -= half + 1; - } else if (comp==0) { - first = mid; - break; - } else { - n = half; - } - } - - return comp==0 ? first->handler : NULL; -} - -/* Public function to parse SIP message. */ -PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool, char *buf, pj_size_t size, - pjsip_parser_err_report *err_list) -{ - pjsip_msg *msg = NULL; - pj_scanner scanner; - PJ_USE_EXCEPTION; - - init_sip_parser(); - - pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); - - PJ_TRY { - msg = int_parse_msg(&scanner, pool, err_list); - } - PJ_DEFAULT { - msg = NULL; - } - PJ_END - - pj_scan_fini(&scanner); - return msg; -} - -/* Determine if a message has been received. */ -PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, - pj_bool_t is_datagram, pj_size_t *msg_size) -{ -#if PJ_HAS_TCP - const char *hdr_end; - const char *body_start; - const char *pos; - const char *line; - int content_length = -1; - - *msg_size = size; - - /* For datagram, the whole datagram IS the message. */ - if (is_datagram) { - return PJ_TRUE; - } - - - /* Find the end of header area by finding an empty line. */ - if ((pos = strstr(buf, "\n\r\n")) == NULL) { - return PJ_FALSE; - } - - hdr_end = pos+1; - body_start = pos+3; - - /* Find "Content-Length" header the hard way. */ - line = strchr(buf, '\n'); - while (line && line < hdr_end-14) { - ++line; - if ( ((*line=='C' || *line=='c') && strncasecmp(line, "Content-Length", 14) == 0) || - ((*line=='l' || *line=='L') && (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':'))) - { - /* Try to parse the header. */ - pj_scanner scanner; - PJ_USE_EXCEPTION; - - init_sip_parser(); - - pj_scan_init(&scanner, (char*)line, hdr_end-line, - PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); - - PJ_TRY { - pj_str_t str_clen; - - /* Get "Content-Length" or "L" name */ - if (*line=='C' || *line=='c') - pj_scan_advance_n(&scanner, 14, PJ_TRUE); - else if (*line=='l' || *line=='L') - pj_scan_advance_n(&scanner, 1, PJ_TRUE); - - /* Get colon */ - if (pj_scan_get_char(&scanner) != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - /* Get number */ - pj_scan_get(&scanner, pjsip_DIGIT_SPEC, &str_clen); - - /* Get newline. */ - pj_scan_get_newline(&scanner); - - /* Found a valid Content-Length header. */ - content_length = pj_strtoul(&str_clen); - } - PJ_END - - pj_scan_fini(&scanner); - } - - /* Found valid Content-Length? */ - if (content_length != -1) - break; - - /* Go to next line. */ - line = strchr(line, '\n'); - } - - /* Found Content-Length? */ - if (content_length == -1) { - PJ_LOG(4, ("sipparser", "pjsip_find_msg: incoming TCP packet has missing " - "Content-Length header, treated as incomplete.")); - return PJ_FALSE; - } - - /* Enough packet received? */ - *msg_size = (body_start - buf) + content_length; - return (*msg_size) <= size; -#else - PJ_UNUSED_ARG(buf) - PJ_UNUSED_ARG(is_datagram) - *msg_size = size; - return 1; -#endif -} - -/* Public function to parse URI */ -PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, - char *buf, pj_size_t size, - unsigned option) -{ - PJ_USE_EXCEPTION; - pj_scanner scanner; - pjsip_uri *uri = NULL; - - if (!parser_is_initialized) { - init_parser(); - parser_is_initialized = 1; - } - - pj_scan_init(&scanner, buf, size, 0, &on_syntax_error); - - - PJ_TRY { - uri = int_parse_uri_or_name_addr(&scanner, pool, option); - } - PJ_END; - - /* Must have exhausted all inputs. */ - if (pj_scan_is_eof(&scanner) || *scanner.current=='\r' || *scanner.current=='\n') { - /* Success. */ - pj_scan_fini(&scanner); - return uri; - } - - /* Still have some characters unparsed. */ - pj_scan_fini(&scanner); - return NULL; -} - -/* Generic function to print message body. - * This assumes that the 'data' member points to a contigous memory where the - * actual body is laid. - */ -static int generic_print_body (pjsip_msg_body *msg_body, char *buf, pj_size_t size) -{ - pjsip_msg_body *body = msg_body; - if (size < body->len) - return 0; - - pj_memcpy (buf, body->data, body->len); - return body->len; -} - -/* Internal function to parse SIP message */ -static pjsip_msg *int_parse_msg( pj_scanner *scanner, pj_pool_t *pool, - pjsip_parser_err_report *err_list) -{ - PJ_USE_EXCEPTION; - int ch; - pjsip_msg *msg; - pjsip_ctype_hdr *ctype_hdr = NULL; - - /* Skip leading newlines. */ - ch = *scanner->current; - while (ch=='\r' || ch=='\n') { - pj_scan_get_char(scanner); - ch = *scanner->current; - } - - msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG); - - /* Parse request or status line */ - if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) { - msg->type = PJSIP_RESPONSE_MSG; - int_parse_status_line( scanner, &msg->line.status ); - } else { - msg->type = PJSIP_REQUEST_MSG; - int_parse_req_line(scanner, pool, &msg->line.req ); - } - - /* Parse headers. */ - do { - pj_str_t hname; - pjsip_parse_hdr_func * handler; - pjsip_hdr *hdr = NULL; - - /* Get hname. */ - pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hname); - ch = pj_scan_get_char( scanner ); - if (ch != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - /* Find handler. */ - handler = find_handler(&hname); - - PJ_TRY { - /* Call the handler if found. - * If no handler is found, then treat the header as generic - * hname/hvalue pair. - */ - if (handler) { - hdr = (*handler)(scanner, pool); - } else { - pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(scanner, pool); - ghdr->type = PJSIP_H_OTHER; - ghdr->name = ghdr->sname = hname; - hdr = (pjsip_hdr*)ghdr; - } - - /* Check if we've just parsed a Content-Type header. - * We will check for a message body if we've got Content-Type header. - */ - if (hdr->type == PJSIP_H_CONTENT_TYPE) { - ctype_hdr = (pjsip_ctype_hdr*)hdr; - } - - } - PJ_DEFAULT { - /* Exception was thrown during parsing. - * Skip until newline, and parse next header. - */ - pj_str_t token; - hdr = NULL; - - PJ_LOG(4,("sipparser", "Syntax error in line %d col %d (hname=%.*s)", - scanner->line, scanner->col, hname.slen, hname.ptr)); - - if (err_list) { - pjsip_parser_err_report *err_info; - - err_info = pj_pool_alloc(pool, sizeof(*err_info)); - err_info->exception_code = PJ_GET_EXCEPTION(); - err_info->line = scanner->line; - err_info->col = scanner->col; - err_info->hname = hname; - - pj_list_insert_before(err_list, err_info); - } - - if (!pj_scan_is_eof(scanner)) { - pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &token); - parse_hdr_end(scanner); - } - } - PJ_END; - - if (hdr) { - /* Single parse of header line can produce multiple headers. - * For example, if one Contact: header contains Contact list - * separated by comma, then these Contacts will be split into - * different Contact headers. - * So here we must insert list instead of just insert one header. - */ - pj_list_insert_nodes_before(&msg->hdr, hdr); - } - - /* Parse until EOF or an empty line is found. */ - } while (!pj_scan_is_eof(scanner) && - *scanner->current != '\r' && *scanner->current != '\n'); - - /* If empty line is found, eat it. */ - if (!pj_scan_is_eof(scanner)) { - if (*scanner->current=='\r' || *scanner->current=='\n') { - pj_scan_get_newline(scanner); - } - } - - /* If we have Content-Type header, treat the rest of the message as body. */ - if (ctype_hdr) { - pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); - pj_strdup (pool, &body->content_type.type, &ctype_hdr->media.type); - pj_strdup (pool, &body->content_type.subtype, &ctype_hdr->media.subtype); - pj_strdup (pool, &body->content_type.param, &ctype_hdr->media.param); - body->data = scanner->current; - body->len = scanner->end - scanner->current; - body->print_body = &generic_print_body; - - msg->body = body; - } - - return msg; -} - -/* Parse parameter (pname ["=" pvalue]). */ -void pjsip_parse_param_imp( pj_scanner *scanner, - pj_str_t *pname, pj_str_t *pvalue, - unsigned option) -{ - /* pname */ - pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pname); - - /* pvalue, if any */ - if (*scanner->current == '=') { - pj_scan_get_char(scanner); - /* pvalue can be a quoted string. */ - if (*scanner->current == '"') { - pj_scan_get_quote( scanner, '"', '"', pvalue); - if (option & PJSIP_PARSE_REMOVE_QUOTE) { - pvalue->ptr++; - pvalue->slen -= 2; - } - } else { - pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pvalue); - } - } else { - pvalue->ptr = NULL; - pvalue->slen = 0; - } -} - -/* Parse parameter (";" pname ["=" pvalue]). */ -static void int_parse_param( pj_scanner *scanner, - pj_str_t *pname, pj_str_t *pvalue) -{ - /* Get ';' character */ - pj_scan_get_char(scanner); - - /* Get pname and optionally pvalue */ - pjsip_parse_param_imp(scanner, pname, pvalue, 0); -} - -/* Parse host:port in URI. */ -static void int_parse_uri_host_port( pj_scanner *scanner, - pj_str_t *host, int *p_port) -{ - pj_scan_get( scanner, pjsip_HOST_SPEC, host); - if (*scanner->current == ':') { - pj_str_t port; - pj_scan_get_char(scanner); - pj_scan_get(scanner, pjsip_DIGIT_SPEC, &port); - *p_port = pj_strtoul(&port); - } else { - *p_port = 0; - } -} - -/* Determine if the next token in an URI is a user specification. */ -static int int_is_next_user(pj_scanner *scanner) -{ - pj_str_t dummy; - int is_user; - - /* Find character '@'. If this character exist, then the token - * must be a username. - */ - if (pj_scan_peek( scanner, pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@') - is_user = 1; - else - is_user = 0; - - return is_user; -} - -/* Parse user:pass tokens in an URI. */ -static void int_parse_user_pass( pj_scanner *scanner, - pj_str_t *user, pj_str_t *pass) -{ - pj_scan_get( scanner, pjsip_USER_SPEC, user); - if ( *scanner->current == ':') { - pj_scan_get_char( scanner ); - pj_scan_get( scanner, pjsip_PASSWD_SPEC, pass); - } else { - pass->ptr = NULL; - pass->slen = 0; - } - - /* Get the '@' */ - pj_scan_get_char( scanner ); -} - -/* Parse all types of URI. */ -static pjsip_uri *int_parse_uri_or_name_addr(pj_scanner *scanner, pj_pool_t *pool, - unsigned option) -{ - pjsip_uri *uri; - int is_name_addr = 0; - - if (*scanner->current=='"' || *scanner->current=='<') { - uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); - is_name_addr = 1; - } else { - pj_scan_state backtrack; - pj_str_t scheme; - int colon; - - pj_scan_save_state( scanner, &backtrack); - pj_scan_get( scanner, pjsip_TOKEN_SPEC, &scheme); - colon = pj_scan_get_char( scanner ); - pj_scan_restore_state( scanner, &backtrack); - - if (colon==':' && (parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0)) - { - uri = (pjsip_uri*)int_parse_sip_url( scanner, pool, - (option & PJSIP_PARSE_URI_IN_FROM_TO_HDR) == 0 ); - - } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) { - - /* Not supported. */ - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - UNREACHED({return NULL; /* Not reached. */}); - - } else { - uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); - is_name_addr = 1; - } - } - - /* Should we return the URI object as name address? */ - if (option & PJSIP_PARSE_URI_AS_NAMEADDR) { - if (is_name_addr == 0) { - pjsip_name_addr *name_addr; - - name_addr = pjsip_name_addr_create(pool); - name_addr->uri = uri; - - uri = (pjsip_uri*)name_addr; - } - } - - return uri; -} - -/* Parse URI. */ -static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool, - pj_bool_t parse_params) -{ - if (*scanner->current=='"' || *scanner->current=='<') { - return (pjsip_uri*)int_parse_name_addr( scanner, pool ); - } else { - pj_str_t scheme; - int colon; - - /* Get scheme. */ - colon = pj_scan_peek(scanner, pjsip_TOKEN_SPEC, &scheme); - if (colon != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0)) - { - return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params ); - - } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - UNREACHED({ return NULL; /* Not reached. */ }) - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - UNREACHED({ return NULL; /* Not reached. */ }) - } - } -} - -/* Parse "sip:" and "sips:" URI. */ -static pjsip_url *int_parse_sip_url( pj_scanner *scanner, - pj_pool_t *pool, - pj_bool_t parse_params) -{ - pj_str_t scheme; - pjsip_url *url; - int colon; - int skip_ws = scanner->skip_ws; - scanner->skip_ws = 0; - - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &scheme); - colon = pj_scan_get_char(scanner); - if (colon != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - if (parser_stricmp(scheme, pjsip_SIP_STR)==0) { - url = pjsip_url_create(pool, 0); - - } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) { - url = pjsip_url_create(pool, 1); - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - /* should not reach here */ - UNREACHED({ - pj_assert(0); - return 0; - }) - } - - if (int_is_next_user(scanner)) { - int_parse_user_pass(scanner, &url->user, &url->passwd); - } - - /* Get host:port */ - int_parse_uri_host_port(scanner, &url->host, &url->port); - - /* Get URL parameters. */ - while ( parse_params && *scanner->current == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - - if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) { - url->user_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) { - url->method_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_TRANSPORT_STR) && pvalue.slen) { - url->transport_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { - url->ttl_param = pj_strtoul(&pvalue); - - } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { - url->maddr_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_LR_STR)) { - url->lr_param = 1; - - } else { - concat_param(&url->other_param, pool, &pname, &pvalue); - } - } - - /* Get header params. */ - if (parse_params && *scanner->current == '?') { - pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &url->header_param); - } - - scanner->skip_ws = skip_ws; - pj_scan_skip_whitespace(scanner); - return url; -} - -/* Parse nameaddr. */ -static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner, - pj_pool_t *pool ) -{ - int has_bracket; - pjsip_name_addr *name_addr; - - name_addr = pjsip_name_addr_create(pool); - - if (*scanner->current == '"') { - pj_scan_get_quote( scanner, '"', '"', &name_addr->display); - - } else if (*scanner->current != '<') { - int next; - pj_str_t dummy; - - /* This can be either the start of display name, - * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char. - * We're only interested in display name, because SIP URL - * will be parser later. - */ - next = pj_scan_peek_until(scanner, pjsip_DISPLAY_SCAN_SPEC, &dummy); - if (next == '<') { - /* Ok, this is what we're looking for, a display name. */ - pj_scan_get_until_ch( scanner, '<', &name_addr->display); - pj_strtrim(&name_addr->display); - } - } - - /* Manually skip whitespace. */ - pj_scan_skip_whitespace(scanner); - - /* Get the SIP-URL */ - has_bracket = (*scanner->current == '<'); - if (has_bracket) - pj_scan_get_char(scanner); - name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE ); - if (has_bracket) - pj_scan_get_char(scanner); - - return name_addr; -} - - -/* Parse SIP request line. */ -static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool, - pjsip_request_line *req_line) -{ - pj_str_t token; - - pj_scan_get( scanner, pjsip_TOKEN_SPEC, &token); - pjsip_method_init_np( &req_line->method, &token); - - req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE); - if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0) - PJ_THROW( PJSIP_SYN_ERR_EXCEPTION); - pj_scan_advance_n (scanner, 7, 1); - pj_scan_get_newline( scanner ); -} - -/* Parse status line. */ -static void int_parse_status_line( pj_scanner *scanner, - pjsip_status_line *status_line) -{ - pj_str_t token; - - if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0) - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - pj_scan_advance_n( scanner, 7, 1); - - pj_scan_get( scanner, pjsip_DIGIT_SPEC, &token); - status_line->code = pj_strtoul(&token); - pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &status_line->reason); - pj_scan_get_newline( scanner ); -} - -/* Parse ending of header. */ -static void parse_hdr_end( pj_scanner *scanner ) -{ - if (pj_scan_is_eof(scanner)) { - ; /* Do nothing. */ - } else if (*scanner->current == '&') { - pj_scan_get_char(scanner); - } else { - pj_scan_get_newline(scanner); - } -} - -/* Parse ending of header. */ -void pjsip_parse_end_hdr_imp( pj_scanner *scanner ) -{ - parse_hdr_end(scanner); -} - -/* Parse generic array header. */ -static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr, - pj_scanner *scanner) -{ - pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[0]); - hdr->count++; - - while (*scanner->current == ',') { - pj_scan_get_char(scanner); - pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[hdr->count]); - hdr->count++; - } - parse_hdr_end(scanner); -} - -/* Parse generic string header. */ -static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr, - pj_scanner *scanner ) -{ - pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue); - parse_hdr_end(scanner); -} - -/* Parse generic integer header. */ -static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr, - pj_scanner *scanner ) -{ - pj_str_t tmp; - pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &tmp); - hdr->ivalue = pj_strtoul(&tmp); - parse_hdr_end(scanner); -} - - -/* Parse Accept header. */ -static pjsip_accept_hdr* parse_hdr_accept( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_accept_hdr *accept = pjsip_accept_hdr_create(pool); - parse_generic_array_hdr(accept, scanner); - return accept; -} - -/* Parse Allow header. */ -static pjsip_allow_hdr* parse_hdr_allow( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_allow_hdr *allow = pjsip_allow_hdr_create(pool); - parse_generic_array_hdr(allow, scanner); - return allow; -} - -/* Parse Call-ID header. */ -static pjsip_cid_hdr* parse_hdr_call_id( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(pool); - pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id); - parse_hdr_end(scanner); - return hdr; -} - -/* Parse and interpret Contact param. */ -static void int_parse_contact_param( pjsip_contact_hdr *hdr, - pj_scanner *scanner, - pj_pool_t *pool) -{ - while ( *scanner->current == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) { - char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen); - if (!dot_pos) { - hdr->q1000 = pj_strtoul(&pvalue); - } else { - pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1); - pvalue.ptr = dot_pos + 1; - hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3); - } - } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) { - hdr->expires = pj_strtoul(&pvalue); - - } else { - concat_param(&hdr->other_param, pool, &pname, &pvalue); - } - } -} - -/* Parse Contact header. */ -PJ_DEF(pjsip_contact_hdr*) parse_hdr_contact( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_contact_hdr *first = NULL; - - do { - pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool); - if (first == NULL) - first = hdr; - else - pj_list_insert_before(first, hdr); - - if (*scanner->current == '*') { - pj_scan_get_char(scanner); - hdr->star = 1; - - } else { - hdr->star = 0; - hdr->uri = int_parse_uri_or_name_addr(scanner, pool, PJSIP_PARSE_URI_AS_NAMEADDR); - - int_parse_contact_param(hdr, scanner, pool); - } - - if (*scanner->current != ',') - break; - - pj_scan_get_char(scanner); - - } while (1); - - parse_hdr_end(scanner); - return first; -} - -/* Parse Content-Length header. */ -PJ_DEF(pjsip_clen_hdr*) parse_hdr_content_length( pj_scanner *scanner, - pj_pool_t *pool) -{ - pj_str_t digit; - pjsip_clen_hdr *hdr; - - hdr = pjsip_clen_hdr_create(pool); - pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit); - hdr->len = pj_strtoul(&digit); - parse_hdr_end(scanner); - return hdr; -} - -/* Parse Content-Type header. */ -PJ_DEF(pjsip_ctype_hdr*) parse_hdr_content_type( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_ctype_hdr *hdr; - - hdr = pjsip_ctype_hdr_create(pool); - - /* Parse media type and subtype. */ - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.type); - pj_scan_get_char(scanner); - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.subtype); - - /* Parse media parameters */ - while (*scanner->current == ';') { - pj_str_t pname, pvalue; - int_parse_param(scanner, &pname, &pvalue); - concat_param(&hdr->media.param, pool, &pname, &pvalue); - } - - parse_hdr_end(scanner); - return hdr; -} - -/* Parse CSeq header. */ -PJ_DEF(pjsip_cseq_hdr*) parse_hdr_cseq( pj_scanner *scanner, - pj_pool_t *pool) -{ - pj_str_t cseq, method; - pjsip_cseq_hdr *hdr; - - hdr = pjsip_cseq_hdr_create(pool); - pj_scan_get( scanner, pjsip_DIGIT_SPEC, &cseq); - hdr->cseq = pj_strtoul(&cseq); - - pj_scan_get( scanner, pjsip_TOKEN_SPEC, &method); - pjsip_method_init_np(&hdr->method, &method); - - parse_hdr_end( scanner ); - return hdr; -} - -/* Parse Expires header. */ -static pjsip_expires_hdr* parse_hdr_expires(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(pool); - parse_generic_int_hdr(hdr, scanner); - return hdr; -} - -/* Parse From: or To: header. */ -static void parse_hdr_fromto( pj_scanner *scanner, - pj_pool_t *pool, - pjsip_from_hdr *hdr) -{ - hdr->uri = int_parse_uri_or_name_addr(scanner, pool, - PJSIP_PARSE_URI_AS_NAMEADDR | - PJSIP_PARSE_URI_IN_FROM_TO_HDR); - - while ( *scanner->current == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - - if (!parser_stricmp(pname, pjsip_TAG_STR)) { - hdr->tag = pvalue; - - } else { - concat_param(&hdr->other_param, pool, &pname, &pvalue); - } - } - - parse_hdr_end(scanner); -} - -/* Parse From: header. */ -PJ_DEF(pjsip_from_hdr*) parse_hdr_from( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_from_hdr *hdr = pjsip_from_hdr_create(pool); - parse_hdr_fromto(scanner, pool, hdr); - return hdr; -} - -/* Parse Require: header. */ -static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_require_hdr *hdr = pjsip_require_hdr_create(pool); - parse_generic_array_hdr(hdr, scanner); - return hdr; -} - -/* Parse Retry-After: header. */ -static pjsip_retry_after_hdr* parse_hdr_retry_after(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_retry_after_hdr *hdr; - hdr = pjsip_retry_after_hdr_create(pool); - parse_generic_int_hdr(hdr, scanner); - return hdr; -} - -/* Parse Supported: header. */ -static pjsip_supported_hdr* parse_hdr_supported(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(pool); - parse_generic_array_hdr(hdr, scanner); - return hdr; -} - - -/* Parse To: header. */ -PJ_DEF(pjsip_to_hdr*) parse_hdr_to( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_to_hdr *hdr = pjsip_to_hdr_create(pool); - parse_hdr_fromto(scanner, pool, hdr); - return hdr; -} - -/* Parse Unsupported: header. */ -static pjsip_unsupported_hdr* parse_hdr_unsupported(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(pool); - parse_generic_array_hdr(hdr, scanner); - return hdr; -} - -/* Parse and interpret Via parameters. */ -static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, - pj_pool_t *pool) -{ - while ( *scanner->current == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - - if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) { - hdr->branch_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { - hdr->ttl_param = pj_strtoul(&pvalue); - - } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { - hdr->maddr_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) { - hdr->recvd_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) { - if (pvalue.slen) - hdr->rport_param = pj_strtoul(&pvalue); - else - hdr->rport_param = 0; - } else { - concat_param( &hdr->other_param, pool, &pname, &pvalue); - } - } - -} - -/* Parse Max-Forwards header. */ -static pjsip_max_forwards_hdr* parse_hdr_max_forwards( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_max_forwards_hdr *hdr; - hdr = pjsip_max_forwards_hdr_create(pool); - parse_generic_int_hdr(hdr, scanner); - return hdr; -} - -/* Parse Min-Expires header. */ -static pjsip_min_expires_hdr* parse_hdr_min_expires(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_min_expires_hdr *hdr; - hdr = pjsip_min_expires_hdr_create(pool); - parse_generic_int_hdr(hdr, scanner); - return hdr; -} - - -/* Parse Route: or Record-Route: header. */ -static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool, - pjsip_routing_hdr *hdr ) -{ - pjsip_name_addr *temp=int_parse_name_addr(scanner, pool); - - pj_memcpy(&hdr->name_addr, temp, sizeof(*temp)); - if (*scanner->current == ';') { - pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->other_param); - } -} - -/* Parse Record-Route header. */ -PJ_DEF(pjsip_rr_hdr*) parse_hdr_rr( pj_scanner *scanner, pj_pool_t *pool) -{ - pjsip_rr_hdr *first = NULL; - - do { - pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(pool); - if (!first) { - first = hdr; - } else { - pj_list_insert_before(first, hdr); - } - parse_hdr_rr_route(scanner, pool, hdr); - if (*scanner->current == ',') { - pj_scan_get_char(scanner); - } else { - break; - } - } while (1); - parse_hdr_end(scanner); - return first; -} - -/* Parse Route: header. */ -PJ_DEF(pjsip_route_hdr*) parse_hdr_route( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_route_hdr *first = NULL; - - do { - pjsip_route_hdr *hdr = pjsip_route_hdr_create(pool); - if (!first) { - first = hdr; - } else { - pj_list_insert_before(first, hdr); - } - parse_hdr_rr_route(scanner, pool, hdr); - if (*scanner->current == ',') { - pj_scan_get_char(scanner); - } else { - break; - } - } while (1); - parse_hdr_end(scanner); - return first; -} - -/* Parse Via: header. */ -PJ_DEF(pjsip_via_hdr*) parse_hdr_via( pj_scanner *scanner, pj_pool_t *pool) -{ - pjsip_via_hdr *first = NULL; - - do { - pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool); - if (!first) - first = hdr; - else - pj_list_insert_before(first, hdr); - - if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0) - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - - pj_scan_advance_n( scanner, 8, 1); - - pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hdr->transport); - pj_scan_get( scanner, pjsip_HOST_SPEC, &hdr->sent_by.host); - - if (*scanner->current==':') { - pj_str_t digit; - pj_scan_get_char(scanner); - pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit); - hdr->sent_by.port = pj_strtoul(&digit); - } else { - hdr->sent_by.port = 5060; - } - - int_parse_via_param(hdr, scanner, pool); - - if (*scanner->current == '(') { - pj_scan_get_char(scanner); - pj_scan_get_until_ch( scanner, ')', &hdr->comment); - pj_scan_get_char( scanner ); - } - - if (*scanner->current != ',') - break; - - pj_scan_get_char(scanner); - - } while (1); - - parse_hdr_end(scanner); - return first; -} - -/* Parse generic header. */ -PJ_DEF(pjsip_generic_string_hdr*) parse_hdr_generic_string( pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_generic_string_hdr *hdr; - - hdr = pjsip_generic_string_hdr_create(pool, NULL); - parse_generic_string_hdr(hdr, scanner); - return hdr; - -} - -/* Public function to parse a header value. */ -PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, - char *buf, pj_size_t size, int *parsed_len ) -{ - pj_scanner scanner; - pjsip_hdr *hdr = NULL; - PJ_USE_EXCEPTION; - - init_sip_parser(); - - pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); - - PJ_TRY { - pjsip_parse_hdr_func *handler = find_handler(hname); - if (handler) { - hdr = (*handler)(&scanner, pool); - } else { - pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(&scanner, pool); - ghdr->type = PJSIP_H_OTHER; - pj_strdup(pool, &ghdr->name, hname); - ghdr->sname = ghdr->name; - hdr = (pjsip_hdr*)ghdr; - } - - } - PJ_DEFAULT { - hdr = NULL; - } - PJ_END - - if (parsed_len) { - *parsed_len = (scanner.current - scanner.begin); - } - - pj_scan_fini(&scanner); - - return hdr; -} - +/* $Header: /pjproject/pjsip/src/pjsip/sip_parser.c 17 9/11/05 9:28a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* tolower() */ + +#define RESERVED ";/?:@&=+$," +#define MARK "-_.!~*'()" +#define ESCAPED "%" +#define USER "&=+$,;?/" +#define PASS "&=+$," +#define TOKEN "-.!%*_=`'~+" /* '+' is because of application/pidf+xml in Content-Type! */ +#define HOST "_-." +#define HEX_DIGIT "abcdefABCDEF" +#define PARAM_CHAR "[]/:&+$" MARK "%" + +#define PJSIP_VERSION "SIP/2.0" +#define PJSIP_SYN_ERR_EXCEPTION 1 + +#define UNREACHED(expr) + +typedef struct handler_rec +{ + char hname[PJSIP_MAX_HNAME_LEN+1]; + pj_size_t hname_len; + pj_uint32_t hname_hash; + pjsip_parse_hdr_func *handler; +} handler_rec; + +static handler_rec handler[127]; +static unsigned handler_count; +static int parser_is_initialized; + + +/* + * Global vars (also extern). + */ +const pj_str_t pjsip_USER_STR = { "user", 4}; +const pj_str_t pjsip_METHOD_STR = { "method", 6}; +const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9}; +const pj_str_t pjsip_MADDR_STR = { "maddr", 5 }; +const pj_str_t pjsip_LR_STR = { "lr", 2 }; +const pj_str_t pjsip_SIP_STR = { "sip", 3 }; +const pj_str_t pjsip_SIPS_STR = { "sips", 4 }; +const pj_str_t pjsip_TEL_STR = { "tel", 3 }; +const pj_str_t pjsip_BRANCH_STR = { "branch", 6 }; +const pj_str_t pjsip_TTL_STR = { "ttl", 3 }; +const pj_str_t pjsip_PNAME_STR = { "received", 8 }; +const pj_str_t pjsip_Q_STR = { "q", 1 }; +const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 }; +const pj_str_t pjsip_TAG_STR = { "tag", 3 }; +const pj_str_t pjsip_RPORT_STR = { "rport", 5}; + +pj_char_spec pjsip_HOST_SPEC, /* For scanning host part. */ + pjsip_DIGIT_SPEC, /* Decimal digits */ + pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */ + pjsip_ALNUM_SPEC, /* Decimal + Alpha. */ + pjsip_TOKEN_SPEC, /* Token. */ + pjsip_HEX_SPEC, /* Hexadecimal digits. */ + pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when it's not quoted.) */ + pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */ + pjsip_PASSWD_SPEC, /* Password. */ + pjsip_USER_SPEC, /* User */ + pjsip_ARRAY_ELEMENTS, /* Array separator. */ + pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/ + pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name in URL. */ + + +/* + * Forward decl. + */ +static pjsip_msg * int_parse_msg( pj_scanner *scanner, + pj_pool_t *pool, + pjsip_parser_err_report *err_list); +static void int_parse_param( pj_scanner *scanner, + pj_str_t *pname, + pj_str_t *pvalue); +static void int_parse_req_line( pj_scanner *scanner, + pj_pool_t *pool, + pjsip_request_line *req_line); +static int int_is_next_user( pj_scanner *scanner); +static void int_parse_status_line( pj_scanner *scanner, + pjsip_status_line *line); +static void int_parse_user_pass( pj_scanner *scanner, + pj_str_t *user, + pj_str_t *pass); +static void int_parse_uri_host_port( pj_scanner *scanner, + pj_str_t *p_host, + int *p_port); +static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner, + pj_pool_t *pool, unsigned option); +static pjsip_url * int_parse_sip_url( pj_scanner *scanner, + pj_pool_t *pool, + pj_bool_t parse_params); +static pjsip_name_addr* int_parse_name_addr( pj_scanner *scanner, + pj_pool_t *pool ); +static void parse_hdr_end( pj_scanner *scanner ); +static pjsip_accept_hdr* parse_hdr_accept( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_allow_hdr* parse_hdr_allow( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_cid_hdr* parse_hdr_call_id( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_contact_hdr* parse_hdr_contact( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_clen_hdr* parse_hdr_content_length( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_ctype_hdr* parse_hdr_content_type( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_cseq_hdr* parse_hdr_cseq( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_expires_hdr* parse_hdr_expires( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_from_hdr* parse_hdr_from( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_max_forwards_hdr* parse_hdr_max_forwards( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_min_expires_hdr* parse_hdr_min_expires( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_rr_hdr* parse_hdr_rr( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_route_hdr* parse_hdr_route( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_retry_after_hdr* parse_hdr_retry_after( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_supported_hdr* parse_hdr_supported( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_to_hdr* parse_hdr_to( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_unsupported_hdr* parse_hdr_unsupported( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_via_hdr* parse_hdr_via( pj_scanner *scanner, + pj_pool_t *pool); +static pjsip_generic_string_hdr* parse_hdr_generic_string( pj_scanner *scanner, + pj_pool_t *pool); + +/* Convert non NULL terminated string to integer. */ +static unsigned long pj_strtoul_mindigit(const pj_str_t *str, unsigned mindig) +{ + unsigned long value; + unsigned i; + + value = 0; + for (i=0; i<(unsigned)str->slen; ++i) { + value = value * 10 + (str->ptr[i] - '0'); + } + for (; islen + pname->slen + pvalue->slen + 3; + p = new_param = pj_pool_alloc(pool, len); + + if (param->slen) { + int old_len = param->slen; + pj_memcpy(p, param->ptr, old_len); + p += old_len; + } + *p++ = (char)sepchar; + pj_memcpy(p, pname->ptr, pname->slen); + p += pname->slen; + + if (pvalue->slen) { + *p++ = '='; + pj_memcpy(p, pvalue->ptr, pvalue->slen); + p += pvalue->slen; + } + + *p = '\0'; + + param->ptr = new_param; + param->slen = p - new_param; +} + +/* Concatenate unrecognized params into single string. */ +static void concat_param( pj_str_t *param, pj_pool_t *pool, + const pj_str_t *pname, const pj_str_t *pvalue ) +{ + pjsip_concat_param_imp(param, pool, pname, pvalue, ';'); +} + +/* Initialize static properties of the parser. */ +static void init_parser() +{ + static int initialized; + + if (initialized) + return; + + initialized = 1; + + pj_cs_add_num( pjsip_DIGIT_SPEC ); + pj_cs_add_alpha( pjsip_ALPHA_SPEC ); + + pj_cs_add_alpha( pjsip_ALNUM_SPEC ); + pj_cs_add_num( pjsip_ALNUM_SPEC ); + + pj_cs_add_str(pjsip_NEWLINE_OR_EOF_SPEC, "\r\n"); + //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0); + + pj_cs_add_str( pjsip_ARRAY_ELEMENTS, ",\r\n"); + + pj_memcpy(pjsip_TOKEN_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); + pj_cs_add_str( pjsip_TOKEN_SPEC, TOKEN); + + pj_memcpy(pjsip_HOST_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); + pj_cs_add_str( pjsip_HOST_SPEC, HOST); + + pj_memcpy(pjsip_HEX_SPEC, pjsip_DIGIT_SPEC, sizeof(pj_char_spec)); + pj_cs_add_str( pjsip_HEX_SPEC, HEX_DIGIT); + + pj_memcpy(pjsip_PARAM_CHAR_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); + pj_cs_add_str( pjsip_PARAM_CHAR_SPEC, PARAM_CHAR); + + pj_memcpy(pjsip_USER_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); + pj_cs_add_str( pjsip_USER_SPEC, MARK ESCAPED USER ); + + pj_memcpy(pjsip_PASSWD_SPEC, pjsip_ALNUM_SPEC, sizeof(pj_char_spec)); + pj_cs_add_str( pjsip_PASSWD_SPEC, MARK ESCAPED PASS); + + pj_cs_add_str( pjsip_PROBE_USER_HOST_SPEC, "@ \n>"); + pj_cs_invert( pjsip_PROBE_USER_HOST_SPEC ); + + pj_cs_add_str( pjsip_DISPLAY_SCAN_SPEC, ":\r\n<"); + + pjsip_register_hdr_parser( "Accept", NULL, (pjsip_parse_hdr_func*) &parse_hdr_accept); + pjsip_register_hdr_parser( "Allow", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow); + pjsip_register_hdr_parser( "Call-ID", NULL, (pjsip_parse_hdr_func*) &parse_hdr_call_id); + pjsip_register_hdr_parser( "Contact", "m", (pjsip_parse_hdr_func*) &parse_hdr_contact); + pjsip_register_hdr_parser( "Content-Length", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_length); + pjsip_register_hdr_parser( "Content-Type", NULL, (pjsip_parse_hdr_func*) &parse_hdr_content_type); + pjsip_register_hdr_parser( "CSeq", NULL, (pjsip_parse_hdr_func*) &parse_hdr_cseq); + pjsip_register_hdr_parser( "Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_expires); + pjsip_register_hdr_parser( "From", "f", (pjsip_parse_hdr_func*) &parse_hdr_from); + pjsip_register_hdr_parser( "Max-Forwards", NULL, (pjsip_parse_hdr_func*) &parse_hdr_max_forwards); + pjsip_register_hdr_parser( "Min-Expires", NULL, (pjsip_parse_hdr_func*) &parse_hdr_min_expires); + pjsip_register_hdr_parser( "Record-Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_rr); + pjsip_register_hdr_parser( "Route", NULL, (pjsip_parse_hdr_func*) &parse_hdr_route); + pjsip_register_hdr_parser( "Require", NULL, (pjsip_parse_hdr_func*) &parse_hdr_require); + pjsip_register_hdr_parser( "Retry-After", NULL, (pjsip_parse_hdr_func*) &parse_hdr_retry_after); + pjsip_register_hdr_parser( "Supported", "k", (pjsip_parse_hdr_func*) &parse_hdr_supported); + pjsip_register_hdr_parser( "To", "t", (pjsip_parse_hdr_func*) &parse_hdr_to); + pjsip_register_hdr_parser( "Unsupported", NULL, (pjsip_parse_hdr_func*) &parse_hdr_unsupported); + pjsip_register_hdr_parser( "Via", NULL, (pjsip_parse_hdr_func*) &parse_hdr_via); + + /* Register auth parser. */ + pjsip_auth_init_parser(); + +} + +static void init_sip_parser() +{ + if (!parser_is_initialized) { + /* Prevent race cond. */ + pj_enter_critical_section(); + if (!parser_is_initialized) { + init_parser(); + parser_is_initialized = 1; + } + pj_leave_critical_section(); + } +} + +/* Compare the handler record with header name, and return: + * - 0 if handler match. + * - <0 if handler is 'less' than the header name. + * - >0 if handler is 'greater' than header name. + */ +static int compare_handler( const handler_rec *r1, + const char *name, + pj_size_t name_len, + pj_uint32_t hash ) +{ + /* Compare length. */ + if (r1->hname_len < name_len) + return -1; + if (r1->hname_len > name_len) + return 1; + + /* Length is equal, compare hashed value. */ + if (r1->hname_hash < hash) + return -1; + if (r1->hname_hash > hash) + return 1; + + /* Equal length and equal hash. compare the strings. */ + return strcmp(r1->hname, name); +} + +/* Register one handler for one header name. */ +static int int_register_parser( const char *name, pjsip_parse_hdr_func *fptr ) +{ + unsigned pos; + handler_rec rec; + unsigned i; + + if (handler_count >= PJ_ARRAY_SIZE(handler)) { + return -1; + } + + /* Initialize temporary handler. */ + rec.handler = fptr; + rec.hname_len = strlen(name); + if (rec.hname_len >= sizeof(rec.hname)) { + return -1; + } + /* Name is copied in lowercase. */ + for (i=0; i 0) { + break; + } + } + + /* Shift handlers. */ + if (pos != handler_count) { + pj_memmove( &handler[pos+1], &handler[pos], (handler_count-pos)*sizeof(handler_rec)); + } + /* Add new handler. */ + pj_memcpy( &handler[pos], &rec, sizeof(handler_rec)); + ++handler_count; + + return 0; +} + +/* Register parser handler. If both header name and short name are valid, + * then two instances of handler will be registered. + */ +PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname, + const char *hshortname, + pjsip_parse_hdr_func *fptr) +{ + if (int_register_parser(hname, fptr)) { + return -1; + } + if (hshortname && int_register_parser(hshortname, fptr)) { + return -1; + } + return 0; +} + +/* Find handler to parse the header name. */ +static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname) +{ + handler_rec *first; + char hname_copy[PJSIP_MAX_HNAME_LEN]; + pj_uint32_t hash; + int comp; + unsigned i, n; + + /* Calculate hash value while converting the header to lowercase. + * Don't assume that 'hname' is NULL terminated. + */ + hash = 0; + for (i=0; i<(unsigned)hname->slen; ++i) { + hname_copy[i] = (char)tolower(hname->ptr[i]); + hash = hash * PJ_HASH_MULTIPLIER + hname_copy[i]; + } + hname_copy[i] = '\0'; + + /* Binary search for the handler. */ + comp = -1; + first = &handler[0]; + n = handler_count; + for (; n > 0; ) { + unsigned half = n / 2; + handler_rec *mid = first + half; + + comp = compare_handler(mid, hname_copy, hname->slen, hash); + if (comp < 0) { + first = ++mid; + n -= half + 1; + } else if (comp==0) { + first = mid; + break; + } else { + n = half; + } + } + + return comp==0 ? first->handler : NULL; +} + +/* Public function to parse SIP message. */ +PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool, char *buf, pj_size_t size, + pjsip_parser_err_report *err_list) +{ + pjsip_msg *msg = NULL; + pj_scanner scanner; + PJ_USE_EXCEPTION; + + init_sip_parser(); + + pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); + + PJ_TRY { + msg = int_parse_msg(&scanner, pool, err_list); + } + PJ_DEFAULT { + msg = NULL; + } + PJ_END + + pj_scan_fini(&scanner); + return msg; +} + +/* Determine if a message has been received. */ +PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, + pj_bool_t is_datagram, pj_size_t *msg_size) +{ +#if PJ_HAS_TCP + const char *hdr_end; + const char *body_start; + const char *pos; + const char *line; + int content_length = -1; + + *msg_size = size; + + /* For datagram, the whole datagram IS the message. */ + if (is_datagram) { + return PJ_TRUE; + } + + + /* Find the end of header area by finding an empty line. */ + if ((pos = strstr(buf, "\n\r\n")) == NULL) { + return PJ_FALSE; + } + + hdr_end = pos+1; + body_start = pos+3; + + /* Find "Content-Length" header the hard way. */ + line = strchr(buf, '\n'); + while (line && line < hdr_end-14) { + ++line; + if ( ((*line=='C' || *line=='c') && strncasecmp(line, "Content-Length", 14) == 0) || + ((*line=='l' || *line=='L') && (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':'))) + { + /* Try to parse the header. */ + pj_scanner scanner; + PJ_USE_EXCEPTION; + + init_sip_parser(); + + pj_scan_init(&scanner, (char*)line, hdr_end-line, + PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); + + PJ_TRY { + pj_str_t str_clen; + + /* Get "Content-Length" or "L" name */ + if (*line=='C' || *line=='c') + pj_scan_advance_n(&scanner, 14, PJ_TRUE); + else if (*line=='l' || *line=='L') + pj_scan_advance_n(&scanner, 1, PJ_TRUE); + + /* Get colon */ + if (pj_scan_get_char(&scanner) != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + /* Get number */ + pj_scan_get(&scanner, pjsip_DIGIT_SPEC, &str_clen); + + /* Get newline. */ + pj_scan_get_newline(&scanner); + + /* Found a valid Content-Length header. */ + content_length = pj_strtoul(&str_clen); + } + PJ_END + + pj_scan_fini(&scanner); + } + + /* Found valid Content-Length? */ + if (content_length != -1) + break; + + /* Go to next line. */ + line = strchr(line, '\n'); + } + + /* Found Content-Length? */ + if (content_length == -1) { + PJ_LOG(4, ("sipparser", "pjsip_find_msg: incoming TCP packet has missing " + "Content-Length header, treated as incomplete.")); + return PJ_FALSE; + } + + /* Enough packet received? */ + *msg_size = (body_start - buf) + content_length; + return (*msg_size) <= size; +#else + PJ_UNUSED_ARG(buf) + PJ_UNUSED_ARG(is_datagram) + *msg_size = size; + return 1; +#endif +} + +/* Public function to parse URI */ +PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, + char *buf, pj_size_t size, + unsigned option) +{ + PJ_USE_EXCEPTION; + pj_scanner scanner; + pjsip_uri *uri = NULL; + + if (!parser_is_initialized) { + init_parser(); + parser_is_initialized = 1; + } + + pj_scan_init(&scanner, buf, size, 0, &on_syntax_error); + + + PJ_TRY { + uri = int_parse_uri_or_name_addr(&scanner, pool, option); + } + PJ_END; + + /* Must have exhausted all inputs. */ + if (pj_scan_is_eof(&scanner) || *scanner.current=='\r' || *scanner.current=='\n') { + /* Success. */ + pj_scan_fini(&scanner); + return uri; + } + + /* Still have some characters unparsed. */ + pj_scan_fini(&scanner); + return NULL; +} + +/* Generic function to print message body. + * This assumes that the 'data' member points to a contigous memory where the + * actual body is laid. + */ +static int generic_print_body (pjsip_msg_body *msg_body, char *buf, pj_size_t size) +{ + pjsip_msg_body *body = msg_body; + if (size < body->len) + return 0; + + pj_memcpy (buf, body->data, body->len); + return body->len; +} + +/* Internal function to parse SIP message */ +static pjsip_msg *int_parse_msg( pj_scanner *scanner, pj_pool_t *pool, + pjsip_parser_err_report *err_list) +{ + PJ_USE_EXCEPTION; + int ch; + pjsip_msg *msg; + pjsip_ctype_hdr *ctype_hdr = NULL; + + /* Skip leading newlines. */ + ch = *scanner->current; + while (ch=='\r' || ch=='\n') { + pj_scan_get_char(scanner); + ch = *scanner->current; + } + + msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG); + + /* Parse request or status line */ + if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) { + msg->type = PJSIP_RESPONSE_MSG; + int_parse_status_line( scanner, &msg->line.status ); + } else { + msg->type = PJSIP_REQUEST_MSG; + int_parse_req_line(scanner, pool, &msg->line.req ); + } + + /* Parse headers. */ + do { + pj_str_t hname; + pjsip_parse_hdr_func * handler; + pjsip_hdr *hdr = NULL; + + /* Get hname. */ + pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hname); + ch = pj_scan_get_char( scanner ); + if (ch != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + /* Find handler. */ + handler = find_handler(&hname); + + PJ_TRY { + /* Call the handler if found. + * If no handler is found, then treat the header as generic + * hname/hvalue pair. + */ + if (handler) { + hdr = (*handler)(scanner, pool); + } else { + pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(scanner, pool); + ghdr->type = PJSIP_H_OTHER; + ghdr->name = ghdr->sname = hname; + hdr = (pjsip_hdr*)ghdr; + } + + /* Check if we've just parsed a Content-Type header. + * We will check for a message body if we've got Content-Type header. + */ + if (hdr->type == PJSIP_H_CONTENT_TYPE) { + ctype_hdr = (pjsip_ctype_hdr*)hdr; + } + + } + PJ_DEFAULT { + /* Exception was thrown during parsing. + * Skip until newline, and parse next header. + */ + pj_str_t token; + hdr = NULL; + + PJ_LOG(4,("sipparser", "Syntax error in line %d col %d (hname=%.*s)", + scanner->line, scanner->col, hname.slen, hname.ptr)); + + if (err_list) { + pjsip_parser_err_report *err_info; + + err_info = pj_pool_alloc(pool, sizeof(*err_info)); + err_info->exception_code = PJ_GET_EXCEPTION(); + err_info->line = scanner->line; + err_info->col = scanner->col; + err_info->hname = hname; + + pj_list_insert_before(err_list, err_info); + } + + if (!pj_scan_is_eof(scanner)) { + pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &token); + parse_hdr_end(scanner); + } + } + PJ_END; + + if (hdr) { + /* Single parse of header line can produce multiple headers. + * For example, if one Contact: header contains Contact list + * separated by comma, then these Contacts will be split into + * different Contact headers. + * So here we must insert list instead of just insert one header. + */ + pj_list_insert_nodes_before(&msg->hdr, hdr); + } + + /* Parse until EOF or an empty line is found. */ + } while (!pj_scan_is_eof(scanner) && + *scanner->current != '\r' && *scanner->current != '\n'); + + /* If empty line is found, eat it. */ + if (!pj_scan_is_eof(scanner)) { + if (*scanner->current=='\r' || *scanner->current=='\n') { + pj_scan_get_newline(scanner); + } + } + + /* If we have Content-Type header, treat the rest of the message as body. */ + if (ctype_hdr) { + pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); + pj_strdup (pool, &body->content_type.type, &ctype_hdr->media.type); + pj_strdup (pool, &body->content_type.subtype, &ctype_hdr->media.subtype); + pj_strdup (pool, &body->content_type.param, &ctype_hdr->media.param); + body->data = scanner->current; + body->len = scanner->end - scanner->current; + body->print_body = &generic_print_body; + + msg->body = body; + } + + return msg; +} + +/* Parse parameter (pname ["=" pvalue]). */ +void pjsip_parse_param_imp( pj_scanner *scanner, + pj_str_t *pname, pj_str_t *pvalue, + unsigned option) +{ + /* pname */ + pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pname); + + /* pvalue, if any */ + if (*scanner->current == '=') { + pj_scan_get_char(scanner); + /* pvalue can be a quoted string. */ + if (*scanner->current == '"') { + pj_scan_get_quote( scanner, '"', '"', pvalue); + if (option & PJSIP_PARSE_REMOVE_QUOTE) { + pvalue->ptr++; + pvalue->slen -= 2; + } + } else { + pj_scan_get(scanner, pjsip_PARAM_CHAR_SPEC, pvalue); + } + } else { + pvalue->ptr = NULL; + pvalue->slen = 0; + } +} + +/* Parse parameter (";" pname ["=" pvalue]). */ +static void int_parse_param( pj_scanner *scanner, + pj_str_t *pname, pj_str_t *pvalue) +{ + /* Get ';' character */ + pj_scan_get_char(scanner); + + /* Get pname and optionally pvalue */ + pjsip_parse_param_imp(scanner, pname, pvalue, 0); +} + +/* Parse host:port in URI. */ +static void int_parse_uri_host_port( pj_scanner *scanner, + pj_str_t *host, int *p_port) +{ + pj_scan_get( scanner, pjsip_HOST_SPEC, host); + if (*scanner->current == ':') { + pj_str_t port; + pj_scan_get_char(scanner); + pj_scan_get(scanner, pjsip_DIGIT_SPEC, &port); + *p_port = pj_strtoul(&port); + } else { + *p_port = 0; + } +} + +/* Determine if the next token in an URI is a user specification. */ +static int int_is_next_user(pj_scanner *scanner) +{ + pj_str_t dummy; + int is_user; + + /* Find character '@'. If this character exist, then the token + * must be a username. + */ + if (pj_scan_peek( scanner, pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@') + is_user = 1; + else + is_user = 0; + + return is_user; +} + +/* Parse user:pass tokens in an URI. */ +static void int_parse_user_pass( pj_scanner *scanner, + pj_str_t *user, pj_str_t *pass) +{ + pj_scan_get( scanner, pjsip_USER_SPEC, user); + if ( *scanner->current == ':') { + pj_scan_get_char( scanner ); + pj_scan_get( scanner, pjsip_PASSWD_SPEC, pass); + } else { + pass->ptr = NULL; + pass->slen = 0; + } + + /* Get the '@' */ + pj_scan_get_char( scanner ); +} + +/* Parse all types of URI. */ +static pjsip_uri *int_parse_uri_or_name_addr(pj_scanner *scanner, pj_pool_t *pool, + unsigned option) +{ + pjsip_uri *uri; + int is_name_addr = 0; + + if (*scanner->current=='"' || *scanner->current=='<') { + uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); + is_name_addr = 1; + } else { + pj_scan_state backtrack; + pj_str_t scheme; + int colon; + + pj_scan_save_state( scanner, &backtrack); + pj_scan_get( scanner, pjsip_TOKEN_SPEC, &scheme); + colon = pj_scan_get_char( scanner ); + pj_scan_restore_state( scanner, &backtrack); + + if (colon==':' && (parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0)) + { + uri = (pjsip_uri*)int_parse_sip_url( scanner, pool, + (option & PJSIP_PARSE_URI_IN_FROM_TO_HDR) == 0 ); + + } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) { + + /* Not supported. */ + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + UNREACHED({return NULL; /* Not reached. */}); + + } else { + uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); + is_name_addr = 1; + } + } + + /* Should we return the URI object as name address? */ + if (option & PJSIP_PARSE_URI_AS_NAMEADDR) { + if (is_name_addr == 0) { + pjsip_name_addr *name_addr; + + name_addr = pjsip_name_addr_create(pool); + name_addr->uri = uri; + + uri = (pjsip_uri*)name_addr; + } + } + + return uri; +} + +/* Parse URI. */ +static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool, + pj_bool_t parse_params) +{ + if (*scanner->current=='"' || *scanner->current=='<') { + return (pjsip_uri*)int_parse_name_addr( scanner, pool ); + } else { + pj_str_t scheme; + int colon; + + /* Get scheme. */ + colon = pj_scan_peek(scanner, pjsip_TOKEN_SPEC, &scheme); + if (colon != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 || parser_stricmp(scheme, pjsip_SIPS_STR)==0)) + { + return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params ); + + } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + UNREACHED({ return NULL; /* Not reached. */ }) + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + UNREACHED({ return NULL; /* Not reached. */ }) + } + } +} + +/* Parse "sip:" and "sips:" URI. */ +static pjsip_url *int_parse_sip_url( pj_scanner *scanner, + pj_pool_t *pool, + pj_bool_t parse_params) +{ + pj_str_t scheme; + pjsip_url *url; + int colon; + int skip_ws = scanner->skip_ws; + scanner->skip_ws = 0; + + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &scheme); + colon = pj_scan_get_char(scanner); + if (colon != ':') { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + + if (parser_stricmp(scheme, pjsip_SIP_STR)==0) { + url = pjsip_url_create(pool, 0); + + } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) { + url = pjsip_url_create(pool, 1); + + } else { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + /* should not reach here */ + UNREACHED({ + pj_assert(0); + return 0; + }) + } + + if (int_is_next_user(scanner)) { + int_parse_user_pass(scanner, &url->user, &url->passwd); + } + + /* Get host:port */ + int_parse_uri_host_port(scanner, &url->host, &url->port); + + /* Get URL parameters. */ + while ( parse_params && *scanner->current == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, &pname, &pvalue); + + if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) { + url->user_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) { + url->method_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_TRANSPORT_STR) && pvalue.slen) { + url->transport_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { + url->ttl_param = pj_strtoul(&pvalue); + + } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { + url->maddr_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_LR_STR)) { + url->lr_param = 1; + + } else { + concat_param(&url->other_param, pool, &pname, &pvalue); + } + } + + /* Get header params. */ + if (parse_params && *scanner->current == '?') { + pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &url->header_param); + } + + scanner->skip_ws = skip_ws; + pj_scan_skip_whitespace(scanner); + return url; +} + +/* Parse nameaddr. */ +static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner, + pj_pool_t *pool ) +{ + int has_bracket; + pjsip_name_addr *name_addr; + + name_addr = pjsip_name_addr_create(pool); + + if (*scanner->current == '"') { + pj_scan_get_quote( scanner, '"', '"', &name_addr->display); + + } else if (*scanner->current != '<') { + int next; + pj_str_t dummy; + + /* This can be either the start of display name, + * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char. + * We're only interested in display name, because SIP URL + * will be parser later. + */ + next = pj_scan_peek_until(scanner, pjsip_DISPLAY_SCAN_SPEC, &dummy); + if (next == '<') { + /* Ok, this is what we're looking for, a display name. */ + pj_scan_get_until_ch( scanner, '<', &name_addr->display); + pj_strtrim(&name_addr->display); + } + } + + /* Manually skip whitespace. */ + pj_scan_skip_whitespace(scanner); + + /* Get the SIP-URL */ + has_bracket = (*scanner->current == '<'); + if (has_bracket) + pj_scan_get_char(scanner); + name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE ); + if (has_bracket) + pj_scan_get_char(scanner); + + return name_addr; +} + + +/* Parse SIP request line. */ +static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool, + pjsip_request_line *req_line) +{ + pj_str_t token; + + pj_scan_get( scanner, pjsip_TOKEN_SPEC, &token); + pjsip_method_init_np( &req_line->method, &token); + + req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE); + if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0) + PJ_THROW( PJSIP_SYN_ERR_EXCEPTION); + pj_scan_advance_n (scanner, 7, 1); + pj_scan_get_newline( scanner ); +} + +/* Parse status line. */ +static void int_parse_status_line( pj_scanner *scanner, + pjsip_status_line *status_line) +{ + pj_str_t token; + + if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0) + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + pj_scan_advance_n( scanner, 7, 1); + + pj_scan_get( scanner, pjsip_DIGIT_SPEC, &token); + status_line->code = pj_strtoul(&token); + pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &status_line->reason); + pj_scan_get_newline( scanner ); +} + +/* Parse ending of header. */ +static void parse_hdr_end( pj_scanner *scanner ) +{ + if (pj_scan_is_eof(scanner)) { + ; /* Do nothing. */ + } else if (*scanner->current == '&') { + pj_scan_get_char(scanner); + } else { + pj_scan_get_newline(scanner); + } +} + +/* Parse ending of header. */ +void pjsip_parse_end_hdr_imp( pj_scanner *scanner ) +{ + parse_hdr_end(scanner); +} + +/* Parse generic array header. */ +static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr, + pj_scanner *scanner) +{ + pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[0]); + hdr->count++; + + while (*scanner->current == ',') { + pj_scan_get_char(scanner); + pj_scan_get_until( scanner, pjsip_ARRAY_ELEMENTS, &hdr->values[hdr->count]); + hdr->count++; + } + parse_hdr_end(scanner); +} + +/* Parse generic string header. */ +static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr, + pj_scanner *scanner ) +{ + pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue); + parse_hdr_end(scanner); +} + +/* Parse generic integer header. */ +static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr, + pj_scanner *scanner ) +{ + pj_str_t tmp; + pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &tmp); + hdr->ivalue = pj_strtoul(&tmp); + parse_hdr_end(scanner); +} + + +/* Parse Accept header. */ +static pjsip_accept_hdr* parse_hdr_accept( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_accept_hdr *accept = pjsip_accept_hdr_create(pool); + parse_generic_array_hdr(accept, scanner); + return accept; +} + +/* Parse Allow header. */ +static pjsip_allow_hdr* parse_hdr_allow( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_allow_hdr *allow = pjsip_allow_hdr_create(pool); + parse_generic_array_hdr(allow, scanner); + return allow; +} + +/* Parse Call-ID header. */ +static pjsip_cid_hdr* parse_hdr_call_id( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(pool); + pj_scan_get_until( scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id); + parse_hdr_end(scanner); + return hdr; +} + +/* Parse and interpret Contact param. */ +static void int_parse_contact_param( pjsip_contact_hdr *hdr, + pj_scanner *scanner, + pj_pool_t *pool) +{ + while ( *scanner->current == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, &pname, &pvalue); + if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) { + char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen); + if (!dot_pos) { + hdr->q1000 = pj_strtoul(&pvalue); + } else { + pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1); + pvalue.ptr = dot_pos + 1; + hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3); + } + } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) { + hdr->expires = pj_strtoul(&pvalue); + + } else { + concat_param(&hdr->other_param, pool, &pname, &pvalue); + } + } +} + +/* Parse Contact header. */ +PJ_DEF(pjsip_contact_hdr*) parse_hdr_contact( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_contact_hdr *first = NULL; + + do { + pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool); + if (first == NULL) + first = hdr; + else + pj_list_insert_before(first, hdr); + + if (*scanner->current == '*') { + pj_scan_get_char(scanner); + hdr->star = 1; + + } else { + hdr->star = 0; + hdr->uri = int_parse_uri_or_name_addr(scanner, pool, PJSIP_PARSE_URI_AS_NAMEADDR); + + int_parse_contact_param(hdr, scanner, pool); + } + + if (*scanner->current != ',') + break; + + pj_scan_get_char(scanner); + + } while (1); + + parse_hdr_end(scanner); + return first; +} + +/* Parse Content-Length header. */ +PJ_DEF(pjsip_clen_hdr*) parse_hdr_content_length( pj_scanner *scanner, + pj_pool_t *pool) +{ + pj_str_t digit; + pjsip_clen_hdr *hdr; + + hdr = pjsip_clen_hdr_create(pool); + pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit); + hdr->len = pj_strtoul(&digit); + parse_hdr_end(scanner); + return hdr; +} + +/* Parse Content-Type header. */ +PJ_DEF(pjsip_ctype_hdr*) parse_hdr_content_type( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_ctype_hdr *hdr; + + hdr = pjsip_ctype_hdr_create(pool); + + /* Parse media type and subtype. */ + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.type); + pj_scan_get_char(scanner); + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->media.subtype); + + /* Parse media parameters */ + while (*scanner->current == ';') { + pj_str_t pname, pvalue; + int_parse_param(scanner, &pname, &pvalue); + concat_param(&hdr->media.param, pool, &pname, &pvalue); + } + + parse_hdr_end(scanner); + return hdr; +} + +/* Parse CSeq header. */ +PJ_DEF(pjsip_cseq_hdr*) parse_hdr_cseq( pj_scanner *scanner, + pj_pool_t *pool) +{ + pj_str_t cseq, method; + pjsip_cseq_hdr *hdr; + + hdr = pjsip_cseq_hdr_create(pool); + pj_scan_get( scanner, pjsip_DIGIT_SPEC, &cseq); + hdr->cseq = pj_strtoul(&cseq); + + pj_scan_get( scanner, pjsip_TOKEN_SPEC, &method); + pjsip_method_init_np(&hdr->method, &method); + + parse_hdr_end( scanner ); + return hdr; +} + +/* Parse Expires header. */ +static pjsip_expires_hdr* parse_hdr_expires(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(pool); + parse_generic_int_hdr(hdr, scanner); + return hdr; +} + +/* Parse From: or To: header. */ +static void parse_hdr_fromto( pj_scanner *scanner, + pj_pool_t *pool, + pjsip_from_hdr *hdr) +{ + hdr->uri = int_parse_uri_or_name_addr(scanner, pool, + PJSIP_PARSE_URI_AS_NAMEADDR | + PJSIP_PARSE_URI_IN_FROM_TO_HDR); + + while ( *scanner->current == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, &pname, &pvalue); + + if (!parser_stricmp(pname, pjsip_TAG_STR)) { + hdr->tag = pvalue; + + } else { + concat_param(&hdr->other_param, pool, &pname, &pvalue); + } + } + + parse_hdr_end(scanner); +} + +/* Parse From: header. */ +PJ_DEF(pjsip_from_hdr*) parse_hdr_from( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_from_hdr *hdr = pjsip_from_hdr_create(pool); + parse_hdr_fromto(scanner, pool, hdr); + return hdr; +} + +/* Parse Require: header. */ +static pjsip_require_hdr* parse_hdr_require( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_require_hdr *hdr = pjsip_require_hdr_create(pool); + parse_generic_array_hdr(hdr, scanner); + return hdr; +} + +/* Parse Retry-After: header. */ +static pjsip_retry_after_hdr* parse_hdr_retry_after(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_retry_after_hdr *hdr; + hdr = pjsip_retry_after_hdr_create(pool); + parse_generic_int_hdr(hdr, scanner); + return hdr; +} + +/* Parse Supported: header. */ +static pjsip_supported_hdr* parse_hdr_supported(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(pool); + parse_generic_array_hdr(hdr, scanner); + return hdr; +} + + +/* Parse To: header. */ +PJ_DEF(pjsip_to_hdr*) parse_hdr_to( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_to_hdr *hdr = pjsip_to_hdr_create(pool); + parse_hdr_fromto(scanner, pool, hdr); + return hdr; +} + +/* Parse Unsupported: header. */ +static pjsip_unsupported_hdr* parse_hdr_unsupported(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(pool); + parse_generic_array_hdr(hdr, scanner); + return hdr; +} + +/* Parse and interpret Via parameters. */ +static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, + pj_pool_t *pool) +{ + while ( *scanner->current == ';' ) { + pj_str_t pname, pvalue; + + int_parse_param( scanner, &pname, &pvalue); + + if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) { + hdr->branch_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { + hdr->ttl_param = pj_strtoul(&pvalue); + + } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { + hdr->maddr_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) { + hdr->recvd_param = pvalue; + + } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) { + if (pvalue.slen) + hdr->rport_param = pj_strtoul(&pvalue); + else + hdr->rport_param = 0; + } else { + concat_param( &hdr->other_param, pool, &pname, &pvalue); + } + } + +} + +/* Parse Max-Forwards header. */ +static pjsip_max_forwards_hdr* parse_hdr_max_forwards( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_max_forwards_hdr *hdr; + hdr = pjsip_max_forwards_hdr_create(pool); + parse_generic_int_hdr(hdr, scanner); + return hdr; +} + +/* Parse Min-Expires header. */ +static pjsip_min_expires_hdr* parse_hdr_min_expires(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_min_expires_hdr *hdr; + hdr = pjsip_min_expires_hdr_create(pool); + parse_generic_int_hdr(hdr, scanner); + return hdr; +} + + +/* Parse Route: or Record-Route: header. */ +static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool, + pjsip_routing_hdr *hdr ) +{ + pjsip_name_addr *temp=int_parse_name_addr(scanner, pool); + + pj_memcpy(&hdr->name_addr, temp, sizeof(*temp)); + if (*scanner->current == ';') { + pj_scan_get_until(scanner, pjsip_NEWLINE_OR_EOF_SPEC, &hdr->other_param); + } +} + +/* Parse Record-Route header. */ +PJ_DEF(pjsip_rr_hdr*) parse_hdr_rr( pj_scanner *scanner, pj_pool_t *pool) +{ + pjsip_rr_hdr *first = NULL; + + do { + pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(pool); + if (!first) { + first = hdr; + } else { + pj_list_insert_before(first, hdr); + } + parse_hdr_rr_route(scanner, pool, hdr); + if (*scanner->current == ',') { + pj_scan_get_char(scanner); + } else { + break; + } + } while (1); + parse_hdr_end(scanner); + return first; +} + +/* Parse Route: header. */ +PJ_DEF(pjsip_route_hdr*) parse_hdr_route( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_route_hdr *first = NULL; + + do { + pjsip_route_hdr *hdr = pjsip_route_hdr_create(pool); + if (!first) { + first = hdr; + } else { + pj_list_insert_before(first, hdr); + } + parse_hdr_rr_route(scanner, pool, hdr); + if (*scanner->current == ',') { + pj_scan_get_char(scanner); + } else { + break; + } + } while (1); + parse_hdr_end(scanner); + return first; +} + +/* Parse Via: header. */ +PJ_DEF(pjsip_via_hdr*) parse_hdr_via( pj_scanner *scanner, pj_pool_t *pool) +{ + pjsip_via_hdr *first = NULL; + + do { + pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool); + if (!first) + first = hdr; + else + pj_list_insert_before(first, hdr); + + if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0) + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + + pj_scan_advance_n( scanner, 8, 1); + + pj_scan_get( scanner, pjsip_TOKEN_SPEC, &hdr->transport); + pj_scan_get( scanner, pjsip_HOST_SPEC, &hdr->sent_by.host); + + if (*scanner->current==':') { + pj_str_t digit; + pj_scan_get_char(scanner); + pj_scan_get(scanner, pjsip_DIGIT_SPEC, &digit); + hdr->sent_by.port = pj_strtoul(&digit); + } else { + hdr->sent_by.port = 5060; + } + + int_parse_via_param(hdr, scanner, pool); + + if (*scanner->current == '(') { + pj_scan_get_char(scanner); + pj_scan_get_until_ch( scanner, ')', &hdr->comment); + pj_scan_get_char( scanner ); + } + + if (*scanner->current != ',') + break; + + pj_scan_get_char(scanner); + + } while (1); + + parse_hdr_end(scanner); + return first; +} + +/* Parse generic header. */ +PJ_DEF(pjsip_generic_string_hdr*) parse_hdr_generic_string( pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_generic_string_hdr *hdr; + + hdr = pjsip_generic_string_hdr_create(pool, NULL); + parse_generic_string_hdr(hdr, scanner); + return hdr; + +} + +/* Public function to parse a header value. */ +PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, + char *buf, pj_size_t size, int *parsed_len ) +{ + pj_scanner scanner; + pjsip_hdr *hdr = NULL; + PJ_USE_EXCEPTION; + + init_sip_parser(); + + pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); + + PJ_TRY { + pjsip_parse_hdr_func *handler = find_handler(hname); + if (handler) { + hdr = (*handler)(&scanner, pool); + } else { + pjsip_generic_string_hdr *ghdr = parse_hdr_generic_string(&scanner, pool); + ghdr->type = PJSIP_H_OTHER; + pj_strdup(pool, &ghdr->name, hname); + ghdr->sname = ghdr->name; + hdr = (pjsip_hdr*)ghdr; + } + + } + PJ_DEFAULT { + hdr = NULL; + } + PJ_END + + if (parsed_len) { + *parsed_len = (scanner.current - scanner.begin); + } + + pj_scan_fini(&scanner); + + return hdr; +} + diff --git a/pjsip/src/pjsip/sip_parser.h b/pjsip/src/pjsip/sip_parser.h index d5cd53955..7cca2673b 100644 --- a/pjsip/src/pjsip/sip_parser.h +++ b/pjsip/src/pjsip/sip_parser.h @@ -1,301 +1,301 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_parser.h 10 9/11/05 9:28a Bennylp $ */ -#ifndef __PJSIP_SIP_PARSER_H__ -#define __PJSIP_SIP_PARSER_H__ - -/** - * @file sip_parser.h - * @brief SIP Message Parser - */ - -#include -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_PARSER SIP Message Parser - * @ingroup PJSIP - * @{ - */ - -/** - * URI Parsing options. - */ -enum -{ - /** If this option is specified, function #pjsip_parse_uri will return - * the URI object as pjsip_name_addr instead of the corresponding - * URI object. - */ - PJSIP_PARSE_URI_AS_NAMEADDR = 1, - - /** If this option is specified, function #pjsip_parse_uri and other - * internal functions that this function calls will parse URI according - * to convention for parsing From/To header. For example, when the URI - * is not enclosed in brackets ("<" and ">"), all parameters will not - * be stored to the URI (it will be stored to the header). - */ - PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2, -}; - -/** - * Parser syntax error exception value. - */ -#define PJSIP_SYN_ERR_EXCEPTION 1 - -/** - * This structure is used to get error reporting from parser. - */ -typedef struct pjsip_parser_err_report -{ - PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report) - int exception_code; /**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */ - int line; /**< Line number. */ - int col; /**< Column number. */ - pj_str_t hname; /**< Header name, if any. */ -} pjsip_parser_err_report; - - -/** - * Type of function to parse header. The parsing function must follow these - * specification: - * - It must not modify the input text. - * - The hname and HCOLON has been parsed prior to invoking the handler. - * - It returns the header instance on success. - * - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception - * instead of just returning NULL. - * When exception is thrown, the return value is ignored. - * - It must read the header separator after finished reading the header - * body. The separator types are described below, and if they don't exist, - * exception must be thrown. Header separator can be a: - * - newline, such as when the header is part of a SIP message. - * - ampersand, such as when the header is part of an URI. - * - for the last header, these separator is optional since parsing - * can be terminated when seeing EOF. - */ -typedef void* (pjsip_parse_hdr_func)(pj_scanner *scanner, pj_pool_t *pool); - -/** - * Type of function to parse URI scheme. - * Most of the specification of header parser handler (pjsip_parse_hdr_func) - * also applies here (except the separator part). - */ -typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool); - -/** - * Register header parser handler. The parser handler MUST follow the - * specification of header parser handler function. New registration - * overwrites previous registration with the same name. - * - * @param hname The header name. - * @param hshortname The short header name or NULL. - * @param fptr The pointer to function to parser the header. - * - * @return zero if success. - * @see pjsip_parse_hdr_func - */ -PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname, - const char *hshortname, - pjsip_parse_hdr_func *fptr); - -/** - * Unregister previously registered header parser handler. - * All the arguments MUST exactly equal to the value specified upon - * registration of the handler. - * - * @param hname The header name registered. - * @param hshortname The short header name registered, or NULL. - * - * @return zero if unregistration was successfull. - */ -PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname, - const char *hshortname, - pjsip_parse_hdr_func *fptr); - -/** - * Register URI scheme parser handler. - * - * @param scheme The URI scheme registered. - * @param func The URI parser function. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme, - pjsip_parse_uri_func *func); - -/** - * Unregister URI scheme parser handler. - * All the arguments MUST exactly equal to the value specified upon - * registration of the handler. - * - * @param scheme The URI scheme as registered previously. - * @param func The function handler as registered previously. - * - * @return zero if the registration was successfull. - */ -PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme, - pjsip_parse_uri_func *func); - -/** - * Parse an URI in the input and return the correct instance of URI. - * - * @param pool The pool to get memory allocations. - * @param buf The input buffer, which size must be at least (size+1) - * because the function will temporarily put NULL - * termination at the end of the buffer during parsing. - * @param size The length of the string (not counting NULL terminator). - * @param options If no options are given (value is zero), the object - * returned is dependent on the syntax of the URI, - * eg. basic SIP URL, TEL URL, or name address. - * If option PJSIP_PARSE_URI_AS_NAMEADDR is given, - * then the returned object is always name address object, - * with the relevant URI object contained in the name - * address object. - * @return The URI or NULL when failed. No exception is thrown by - * this function (or any public parser functions). - */ -PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, - char *buf, pj_size_t size, - unsigned option); - -/** - * Parse a packet buffer and build a full SIP message from the packet. This - * function parses all parts of the message, including request/status line, - * all headers, and the message body. The message body however is only - * treated as a text block, ie. the function will not try to parse the content - * of the body. - * - * @param pool The pool to allocate memory. - * @param buf The input buffer, which size must be at least (size+1) - * because the function will temporarily put NULL - * termination at the end of the buffer during parsing. - * @param size The length of the string (not counting NULL terminator). - * @param err_list If this parameter is not NULL, then the parser will - * put error messages during parsing in this list. - * - * @return The message or NULL when failed. No exception is thrown - * by this function (or any public parser functions). - */ -PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool, - char *buf, pj_size_t size, - pjsip_parser_err_report *err_list); - - -/** - * Check incoming packet to see if a (probably) valid SIP message has been - * received. - * - * @param buf The input buffer, which must be NULL terminated. - * @param size The buffer size. - * @param msg_size [out] If message is valid, this parameter will contain - * the size of the SIP message (including body, if any). - * - * @return PJ_TRUE (1) if a message is found. - */ -PJ_DECL(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, - pj_bool_t is_datagram, pj_size_t *msg_size); - -/** - * Parse the content of a header and return the header instance. - * This function parses the content of a header (ie. part after colon) according - * to the expected name, and will return the correct instance of header. - * - * @param pool Pool to allocate memory for the header. - * @param hname Header name which is used to find the correct function - * to parse the header. - * @param line Header content, which size must be at least size+1. - * @param size The length of the string (not counting NULL terminator, - * if any). - * @param parsed_len If the value is not NULL, then upon return the function - * will fill the pointer with the length of the string - * that has been parsed. This is usefull for two purposes, - * one is when the string may contain more than one header - * lines, and two when an error happen the value can - * pinpoint the location of the error in the buffer. - * - * @return The instance of the header if parsing was successfull, - * or otherwise a NULL pointer will be returned. - */ -PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, - char *line, pj_size_t size, - int *parsed_len); - -/** - * Parse header line(s). Multiple headers can be parsed by this function. - * When there are multiple headers, the headers MUST be separated by either - * a newline (as in SIP message) or ampersand mark (as in URI). This separator - * however is optional for the last header. - * - * @param pool the pool. - * @param buf the input text to parse. - * @param size the text length. - * @param hlist the header list to store the parsed headers. This list must - * have been initialized before calling this function. - * @return zero if successfull, or -1 if error is encountered. Upon error, - * the \a hlist argument MAY contain successfully parsed headers. - */ -PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, - char *input, pj_size_t size, - pj_list *hlist ); - - -/* - * Various specification used in parsing, exported here as extern for other - * parsers. - */ -extern -pj_char_spec pjsip_HOST_SPEC, /* For scanning host part. */ - pjsip_DIGIT_SPEC, /* Decimal digits */ - pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */ - pjsip_ALNUM_SPEC, /* Decimal + Alpha. */ - pjsip_TOKEN_SPEC, /* Token. */ - pjsip_HEX_SPEC, /* Hexadecimal digits. */ - pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when it's not quoted.) */ - pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */ - pjsip_PASSWD_SPEC, /* Password. */ - pjsip_USER_SPEC, /* User */ - pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/ - pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name in URL. */ - -/* - * Various string constants. - */ -extern const pj_str_t pjsip_USER_STR, - pjsip_METHOD_STR, - pjsip_TRANSPORT_STR, - pjsip_MADDR_STR, - pjsip_LR_STR, - pjsip_SIP_STR, - pjsip_SIPS_STR, - pjsip_TEL_STR, - pjsip_BRANCH_STR, - pjsip_TTL_STR, - pjsip_PNAME_STR, - pjsip_Q_STR, - pjsip_EXPIRES_STR, - pjsip_TAG_STR; - -/* - * Parser utilities. - */ -enum -{ - PJSIP_PARSE_REMOVE_QUOTE = 1, -}; - -void pjsip_parse_param_imp( pj_scanner *scanner, - pj_str_t *pname, pj_str_t *pvalue, - unsigned opt); -void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool, - const pj_str_t *pname, const pj_str_t *pvalue, int sepchar); -void pjsip_parse_end_hdr_imp ( pj_scanner *scanner ); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_SIP_PARSER_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_parser.h 10 9/11/05 9:28a Bennylp $ */ +#ifndef __PJSIP_SIP_PARSER_H__ +#define __PJSIP_SIP_PARSER_H__ + +/** + * @file sip_parser.h + * @brief SIP Message Parser + */ + +#include +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_PARSER SIP Message Parser + * @ingroup PJSIP + * @{ + */ + +/** + * URI Parsing options. + */ +enum +{ + /** If this option is specified, function #pjsip_parse_uri will return + * the URI object as pjsip_name_addr instead of the corresponding + * URI object. + */ + PJSIP_PARSE_URI_AS_NAMEADDR = 1, + + /** If this option is specified, function #pjsip_parse_uri and other + * internal functions that this function calls will parse URI according + * to convention for parsing From/To header. For example, when the URI + * is not enclosed in brackets ("<" and ">"), all parameters will not + * be stored to the URI (it will be stored to the header). + */ + PJSIP_PARSE_URI_IN_FROM_TO_HDR = 2, +}; + +/** + * Parser syntax error exception value. + */ +#define PJSIP_SYN_ERR_EXCEPTION 1 + +/** + * This structure is used to get error reporting from parser. + */ +typedef struct pjsip_parser_err_report +{ + PJ_DECL_LIST_MEMBER(struct pjsip_parser_err_report) + int exception_code; /**< Error exception (e.g. PJSIP_SYN_ERR_EXCEPTION) */ + int line; /**< Line number. */ + int col; /**< Column number. */ + pj_str_t hname; /**< Header name, if any. */ +} pjsip_parser_err_report; + + +/** + * Type of function to parse header. The parsing function must follow these + * specification: + * - It must not modify the input text. + * - The hname and HCOLON has been parsed prior to invoking the handler. + * - It returns the header instance on success. + * - For error reporting, it must throw PJSIP_SYN_ERR_EXCEPTION exception + * instead of just returning NULL. + * When exception is thrown, the return value is ignored. + * - It must read the header separator after finished reading the header + * body. The separator types are described below, and if they don't exist, + * exception must be thrown. Header separator can be a: + * - newline, such as when the header is part of a SIP message. + * - ampersand, such as when the header is part of an URI. + * - for the last header, these separator is optional since parsing + * can be terminated when seeing EOF. + */ +typedef void* (pjsip_parse_hdr_func)(pj_scanner *scanner, pj_pool_t *pool); + +/** + * Type of function to parse URI scheme. + * Most of the specification of header parser handler (pjsip_parse_hdr_func) + * also applies here (except the separator part). + */ +typedef void* (pjsip_parse_uri_func)(pj_scanner *scanner, pj_pool_t *pool); + +/** + * Register header parser handler. The parser handler MUST follow the + * specification of header parser handler function. New registration + * overwrites previous registration with the same name. + * + * @param hname The header name. + * @param hshortname The short header name or NULL. + * @param fptr The pointer to function to parser the header. + * + * @return zero if success. + * @see pjsip_parse_hdr_func + */ +PJ_DECL(pj_status_t) pjsip_register_hdr_parser( const char *hname, + const char *hshortname, + pjsip_parse_hdr_func *fptr); + +/** + * Unregister previously registered header parser handler. + * All the arguments MUST exactly equal to the value specified upon + * registration of the handler. + * + * @param hname The header name registered. + * @param hshortname The short header name registered, or NULL. + * + * @return zero if unregistration was successfull. + */ +PJ_DECL(pj_status_t) pjsip_unregister_hdr_parser( const char *hname, + const char *hshortname, + pjsip_parse_hdr_func *fptr); + +/** + * Register URI scheme parser handler. + * + * @param scheme The URI scheme registered. + * @param func The URI parser function. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_register_uri_parser( const char *scheme, + pjsip_parse_uri_func *func); + +/** + * Unregister URI scheme parser handler. + * All the arguments MUST exactly equal to the value specified upon + * registration of the handler. + * + * @param scheme The URI scheme as registered previously. + * @param func The function handler as registered previously. + * + * @return zero if the registration was successfull. + */ +PJ_DECL(pj_status_t) pjsip_unregister_uri_parser( const char *scheme, + pjsip_parse_uri_func *func); + +/** + * Parse an URI in the input and return the correct instance of URI. + * + * @param pool The pool to get memory allocations. + * @param buf The input buffer, which size must be at least (size+1) + * because the function will temporarily put NULL + * termination at the end of the buffer during parsing. + * @param size The length of the string (not counting NULL terminator). + * @param options If no options are given (value is zero), the object + * returned is dependent on the syntax of the URI, + * eg. basic SIP URL, TEL URL, or name address. + * If option PJSIP_PARSE_URI_AS_NAMEADDR is given, + * then the returned object is always name address object, + * with the relevant URI object contained in the name + * address object. + * @return The URI or NULL when failed. No exception is thrown by + * this function (or any public parser functions). + */ +PJ_DECL(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, + char *buf, pj_size_t size, + unsigned option); + +/** + * Parse a packet buffer and build a full SIP message from the packet. This + * function parses all parts of the message, including request/status line, + * all headers, and the message body. The message body however is only + * treated as a text block, ie. the function will not try to parse the content + * of the body. + * + * @param pool The pool to allocate memory. + * @param buf The input buffer, which size must be at least (size+1) + * because the function will temporarily put NULL + * termination at the end of the buffer during parsing. + * @param size The length of the string (not counting NULL terminator). + * @param err_list If this parameter is not NULL, then the parser will + * put error messages during parsing in this list. + * + * @return The message or NULL when failed. No exception is thrown + * by this function (or any public parser functions). + */ +PJ_DECL(pjsip_msg *) pjsip_parse_msg( pj_pool_t *pool, + char *buf, pj_size_t size, + pjsip_parser_err_report *err_list); + + +/** + * Check incoming packet to see if a (probably) valid SIP message has been + * received. + * + * @param buf The input buffer, which must be NULL terminated. + * @param size The buffer size. + * @param msg_size [out] If message is valid, this parameter will contain + * the size of the SIP message (including body, if any). + * + * @return PJ_TRUE (1) if a message is found. + */ +PJ_DECL(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, + pj_bool_t is_datagram, pj_size_t *msg_size); + +/** + * Parse the content of a header and return the header instance. + * This function parses the content of a header (ie. part after colon) according + * to the expected name, and will return the correct instance of header. + * + * @param pool Pool to allocate memory for the header. + * @param hname Header name which is used to find the correct function + * to parse the header. + * @param line Header content, which size must be at least size+1. + * @param size The length of the string (not counting NULL terminator, + * if any). + * @param parsed_len If the value is not NULL, then upon return the function + * will fill the pointer with the length of the string + * that has been parsed. This is usefull for two purposes, + * one is when the string may contain more than one header + * lines, and two when an error happen the value can + * pinpoint the location of the error in the buffer. + * + * @return The instance of the header if parsing was successfull, + * or otherwise a NULL pointer will be returned. + */ +PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, + char *line, pj_size_t size, + int *parsed_len); + +/** + * Parse header line(s). Multiple headers can be parsed by this function. + * When there are multiple headers, the headers MUST be separated by either + * a newline (as in SIP message) or ampersand mark (as in URI). This separator + * however is optional for the last header. + * + * @param pool the pool. + * @param buf the input text to parse. + * @param size the text length. + * @param hlist the header list to store the parsed headers. This list must + * have been initialized before calling this function. + * @return zero if successfull, or -1 if error is encountered. Upon error, + * the \a hlist argument MAY contain successfully parsed headers. + */ +PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, + char *input, pj_size_t size, + pj_list *hlist ); + + +/* + * Various specification used in parsing, exported here as extern for other + * parsers. + */ +extern +pj_char_spec pjsip_HOST_SPEC, /* For scanning host part. */ + pjsip_DIGIT_SPEC, /* Decimal digits */ + pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */ + pjsip_ALNUM_SPEC, /* Decimal + Alpha. */ + pjsip_TOKEN_SPEC, /* Token. */ + pjsip_HEX_SPEC, /* Hexadecimal digits. */ + pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when it's not quoted.) */ + pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */ + pjsip_PASSWD_SPEC, /* Password. */ + pjsip_USER_SPEC, /* User */ + pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/ + pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name in URL. */ + +/* + * Various string constants. + */ +extern const pj_str_t pjsip_USER_STR, + pjsip_METHOD_STR, + pjsip_TRANSPORT_STR, + pjsip_MADDR_STR, + pjsip_LR_STR, + pjsip_SIP_STR, + pjsip_SIPS_STR, + pjsip_TEL_STR, + pjsip_BRANCH_STR, + pjsip_TTL_STR, + pjsip_PNAME_STR, + pjsip_Q_STR, + pjsip_EXPIRES_STR, + pjsip_TAG_STR; + +/* + * Parser utilities. + */ +enum +{ + PJSIP_PARSE_REMOVE_QUOTE = 1, +}; + +void pjsip_parse_param_imp( pj_scanner *scanner, + pj_str_t *pname, pj_str_t *pvalue, + unsigned opt); +void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool, + const pj_str_t *pname, const pj_str_t *pvalue, int sepchar); +void pjsip_parse_end_hdr_imp ( pj_scanner *scanner ); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_PARSER_H__ */ + diff --git a/pjsip/src/pjsip/sip_private.h b/pjsip/src/pjsip/sip_private.h index 3d94b1d42..b256a4607 100644 --- a/pjsip/src/pjsip/sip_private.h +++ b/pjsip/src/pjsip/sip_private.h @@ -1,82 +1,82 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_private.h 5 6/17/05 11:16p Bennylp $ */ -#ifndef __PJSIP_SIP_PRIVATE_H__ -#define __PJSIP_SIP_PRIVATE_H__ - -/** - * @file sip_private.h - * @brief Private structures and functions for PJSIP Library. - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_PRIVATE Private structures and functions (PJSIP internals) - * @ingroup PJSIP - * @{ - */ - - -/** - * Create a new transport manager. - * @param pool The pool - * @param endpt The endpoint - * @param cb Callback to be called to receive messages from transport. - */ -PJ_DECL(pjsip_transport_mgr*) pjsip_transport_mgr_create( pj_pool_t *pool, - pjsip_endpoint *endpt, - void (*cb)(pjsip_endpoint *,pjsip_rx_data *)); - - -/** - * Destroy transport manager and release all transports. - * @param mgr Transport manager to be destroyed. - */ -PJ_DECL(void) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr ); - -/** - * Poll for transport events. - * Incoming messages will be parsed by the transport manager, and the callback - * will be called for each of this message. - * @param endpt The endpoint. - * @param timeout Timeout value, or NULL to wait forever. - */ -PJ_DECL(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr, - const pj_time_val *timeout ); - -/** - * Get the pointer to the first transport iterator. - * @param mgr The transport manager. - * @param it The iterator used for iterating the hash element. - * @return the iterator to the first transport, or NULL. - */ -PJ_DECL(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *it ); - - -/** - * Get the next transport iterator. - * @param itr the iterator to the transport. - * @return the iterator pointed to the next transport, or NULL. - */ -PJ_DECL(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *itr ); - -/** - * Get the value of transport iterator. - * @param mgr the transport manager. - * @param itr the transport iterator. - * @return the transport associated with the iterator. - */ -PJ_DECL(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *itr ); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_PRIVATE_I_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_private.h 5 6/17/05 11:16p Bennylp $ */ +#ifndef __PJSIP_SIP_PRIVATE_H__ +#define __PJSIP_SIP_PRIVATE_H__ + +/** + * @file sip_private.h + * @brief Private structures and functions for PJSIP Library. + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_PRIVATE Private structures and functions (PJSIP internals) + * @ingroup PJSIP + * @{ + */ + + +/** + * Create a new transport manager. + * @param pool The pool + * @param endpt The endpoint + * @param cb Callback to be called to receive messages from transport. + */ +PJ_DECL(pjsip_transport_mgr*) pjsip_transport_mgr_create( pj_pool_t *pool, + pjsip_endpoint *endpt, + void (*cb)(pjsip_endpoint *,pjsip_rx_data *)); + + +/** + * Destroy transport manager and release all transports. + * @param mgr Transport manager to be destroyed. + */ +PJ_DECL(void) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr ); + +/** + * Poll for transport events. + * Incoming messages will be parsed by the transport manager, and the callback + * will be called for each of this message. + * @param endpt The endpoint. + * @param timeout Timeout value, or NULL to wait forever. + */ +PJ_DECL(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr, + const pj_time_val *timeout ); + +/** + * Get the pointer to the first transport iterator. + * @param mgr The transport manager. + * @param it The iterator used for iterating the hash element. + * @return the iterator to the first transport, or NULL. + */ +PJ_DECL(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr, + pj_hash_iterator_t *it ); + + +/** + * Get the next transport iterator. + * @param itr the iterator to the transport. + * @return the iterator pointed to the next transport, or NULL. + */ +PJ_DECL(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr, + pj_hash_iterator_t *itr ); + +/** + * Get the value of transport iterator. + * @param mgr the transport manager. + * @param itr the transport iterator. + * @return the transport associated with the iterator. + */ +PJ_DECL(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr, + pj_hash_iterator_t *itr ); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_PRIVATE_I_H__ */ + diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index f62f965f4..e9a2ce54f 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -1,106 +1,106 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_resolve.c 5 10/14/05 12:23a Bennylp $ */ - -#include -#include -#include -#include - -struct pjsip_resolver_t -{ - void *dummy; -}; - -PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool) -{ - pjsip_resolver_t *resolver; - resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver)); - return resolver; -} - -PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver) -{ - PJ_UNUSED_ARG(resolver) -} - -static int is_str_ip(const pj_str_t *host) -{ - const char *p = host->ptr; - const char *end = ((const char*)host->ptr) + host->slen; - - while (p != end) { - if (isdigit(*p) || *p=='.') { - ++p; - } else { - return 0; - } - } - return 1; -} - -PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, - pj_pool_t *pool, - pjsip_host_port *target, - void *token, - pjsip_resolver_callback *cb) -{ - struct pjsip_server_addresses svr_addr; - pj_status_t status; - int is_ip_addr; - pjsip_transport_type_e type = target->type; - - PJ_UNUSED_ARG(resolver) - PJ_UNUSED_ARG(pool) - - /* We only do synchronous resolving at this moment. */ - PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION) - - /* Is it IP address or hostname?. */ - is_ip_addr = is_str_ip(&target->host); - - /* Set the transport type if not explicitly specified. - * RFC 3263 section 4.1 specify rules to set up this. - */ - if (type == PJSIP_TRANSPORT_UNSPECIFIED) { - if (is_ip_addr || (target->port != 0)) { -#if PJ_HAS_TCP - if (target->flag & PJSIP_TRANSPORT_SECURE) - { - type = PJSIP_TRANSPORT_TLS; - } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) - { - type = PJSIP_TRANSPORT_TCP; - } else -#endif - { - type = PJSIP_TRANSPORT_UDP; - } - } else { - /* No type or explicit port is specified, and the address is - * not IP address. - * In this case, full resolution must be performed. - * But we don't support it (yet). - */ - type = PJSIP_TRANSPORT_UDP; - } - - } - - /* Set the port number if not specified. */ - if (target->port == 0) { - target->port = pjsip_transport_get_default_port_for_type(type); - } - - /* Resolve hostname. */ - if (!is_ip_addr) { - status = pj_sockaddr_init(&svr_addr.entry[0].addr, &target->host, target->port); - } else { - status = pj_sockaddr_init(&svr_addr.entry[0].addr, &target->host, target->port); - pj_assert(status == PJ_SUCCESS); - } - - /* Call the callback. */ - svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0; - svr_addr.entry[0].type = type; - (*cb)(status, token, &svr_addr); -} - +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_resolve.c 5 10/14/05 12:23a Bennylp $ */ + +#include +#include +#include +#include + +struct pjsip_resolver_t +{ + void *dummy; +}; + +PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool) +{ + pjsip_resolver_t *resolver; + resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver)); + return resolver; +} + +PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver) +{ + PJ_UNUSED_ARG(resolver) +} + +static int is_str_ip(const pj_str_t *host) +{ + const char *p = host->ptr; + const char *end = ((const char*)host->ptr) + host->slen; + + while (p != end) { + if (isdigit(*p) || *p=='.') { + ++p; + } else { + return 0; + } + } + return 1; +} + +PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb) +{ + struct pjsip_server_addresses svr_addr; + pj_status_t status; + int is_ip_addr; + pjsip_transport_type_e type = target->type; + + PJ_UNUSED_ARG(resolver) + PJ_UNUSED_ARG(pool) + + /* We only do synchronous resolving at this moment. */ + PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION) + + /* Is it IP address or hostname?. */ + is_ip_addr = is_str_ip(&target->host); + + /* Set the transport type if not explicitly specified. + * RFC 3263 section 4.1 specify rules to set up this. + */ + if (type == PJSIP_TRANSPORT_UNSPECIFIED) { + if (is_ip_addr || (target->port != 0)) { +#if PJ_HAS_TCP + if (target->flag & PJSIP_TRANSPORT_SECURE) + { + type = PJSIP_TRANSPORT_TLS; + } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) + { + type = PJSIP_TRANSPORT_TCP; + } else +#endif + { + type = PJSIP_TRANSPORT_UDP; + } + } else { + /* No type or explicit port is specified, and the address is + * not IP address. + * In this case, full resolution must be performed. + * But we don't support it (yet). + */ + type = PJSIP_TRANSPORT_UDP; + } + + } + + /* Set the port number if not specified. */ + if (target->port == 0) { + target->port = pjsip_transport_get_default_port_for_type(type); + } + + /* Resolve hostname. */ + if (!is_ip_addr) { + status = pj_sockaddr_init(&svr_addr.entry[0].addr, &target->host, target->port); + } else { + status = pj_sockaddr_init(&svr_addr.entry[0].addr, &target->host, target->port); + pj_assert(status == PJ_SUCCESS); + } + + /* Call the callback. */ + svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0; + svr_addr.entry[0].type = type; + (*cb)(status, token, &svr_addr); +} + diff --git a/pjsip/src/pjsip/sip_resolve.h b/pjsip/src/pjsip/sip_resolve.h index d2ce117a2..f8e1dff11 100644 --- a/pjsip/src/pjsip/sip_resolve.h +++ b/pjsip/src/pjsip/sip_resolve.h @@ -1,103 +1,103 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_resolve.h 5 6/17/05 11:16p Bennylp $ */ -#ifndef __PJSIP_SIP_RESOLVE_H__ -#define __PJSIP_SIP_RESOLVE_H__ - -/** - * @file sip_resolve.h - * @brief - * This module contains the mechanism to resolve server address as specified by - * RFC 3263 - Locating SIP Servers - */ - -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_RESOLVE SIP Server Resolver - * @ingroup PJSIP - * @{ - */ - -/** - * Maximum number of addresses returned by the resolver. - */ -#define PJSIP_MAX_RESOLVED_ADDRESSES 8 - -typedef struct pjsip_server_addresses pjsip_server_addresses; - -/** - * The server addresses returned by the resolver. - */ -struct pjsip_server_addresses -{ - /** Number of address records. */ - unsigned count; - - /** Address records. */ - struct - { - /** Preferable transport to be used to contact this address. */ - pjsip_transport_type_e type; - - /** The server's address. */ - pj_sockaddr_in addr; - - } entry[PJSIP_MAX_RESOLVED_ADDRESSES]; - -}; - -/** - * The type of callback function to be called when resolver finishes the job. - * - * @param status The status of the operation, which is zero on success. - * @param token The token that was associated with the job when application - * call the resolve function. - * @param addr The addresses resolved by the operation. - */ -typedef void pjsip_resolver_callback(pj_status_t status, - void *token, - const struct pjsip_server_addresses *addr); - -/** - * Create resolver engine. - * - * @param pool The Pool. - * @return The resolver engine. - */ -PJ_DECL(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool); - -/** - * Destroy resolver engine. - * - * @param resolver The resolver. - */ -PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver); - -/** - * Asynchronously resolve a SIP target host or domain according to rule - * specified in RFC 3263 (Locating SIP Servers). When the resolving operation - * has completed, the callback will be called. - * - * Note: at the moment we don't have implementation of RFC 3263 yet! - * - * @param resolver The resolver engine. - * @param pool The pool to allocate resolver job. - * @param target The target specification to be resolved. - * @param token A user defined token to be passed back to callback function. - * @param cb The callback function. - */ -PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver, - pj_pool_t *pool, - pjsip_host_port *target, - void *token, - pjsip_resolver_callback *cb); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_SIP_RESOLVE_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip/sip_resolve.h 5 6/17/05 11:16p Bennylp $ */ +#ifndef __PJSIP_SIP_RESOLVE_H__ +#define __PJSIP_SIP_RESOLVE_H__ + +/** + * @file sip_resolve.h + * @brief + * This module contains the mechanism to resolve server address as specified by + * RFC 3263 - Locating SIP Servers + */ + +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_RESOLVE SIP Server Resolver + * @ingroup PJSIP + * @{ + */ + +/** + * Maximum number of addresses returned by the resolver. + */ +#define PJSIP_MAX_RESOLVED_ADDRESSES 8 + +typedef struct pjsip_server_addresses pjsip_server_addresses; + +/** + * The server addresses returned by the resolver. + */ +struct pjsip_server_addresses +{ + /** Number of address records. */ + unsigned count; + + /** Address records. */ + struct + { + /** Preferable transport to be used to contact this address. */ + pjsip_transport_type_e type; + + /** The server's address. */ + pj_sockaddr_in addr; + + } entry[PJSIP_MAX_RESOLVED_ADDRESSES]; + +}; + +/** + * The type of callback function to be called when resolver finishes the job. + * + * @param status The status of the operation, which is zero on success. + * @param token The token that was associated with the job when application + * call the resolve function. + * @param addr The addresses resolved by the operation. + */ +typedef void pjsip_resolver_callback(pj_status_t status, + void *token, + const struct pjsip_server_addresses *addr); + +/** + * Create resolver engine. + * + * @param pool The Pool. + * @return The resolver engine. + */ +PJ_DECL(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool); + +/** + * Destroy resolver engine. + * + * @param resolver The resolver. + */ +PJ_DECL(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver); + +/** + * Asynchronously resolve a SIP target host or domain according to rule + * specified in RFC 3263 (Locating SIP Servers). When the resolving operation + * has completed, the callback will be called. + * + * Note: at the moment we don't have implementation of RFC 3263 yet! + * + * @param resolver The resolver engine. + * @param pool The pool to allocate resolver job. + * @param target The target specification to be resolved. + * @param token A user defined token to be passed back to callback function. + * @param cb The callback function. + */ +PJ_DECL(void) pjsip_resolve( pjsip_resolver_t *resolver, + pj_pool_t *pool, + pjsip_host_port *target, + void *token, + pjsip_resolver_callback *cb); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_SIP_RESOLVE_H__ */ diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index 66182572f..8a89f763b 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -1,1882 +1,1882 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transaction.c 21 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Thread Local Storage ID for transaction lock (initialized by endpoint) */ -int pjsip_tsx_lock_tls_id; - -/* State names */ -static const char *state_str[] = -{ - "Null", - "Calling", - "Trying", - "Proceeding", - "Completed", - "Confirmed", - "Terminated", - "Destroyed", -}; - -/* Role names */ -static const char *role_name[] = -{ - "Client", - "Server" -}; - -/* Transaction lock. */ -typedef struct tsx_lock_data { - struct tsx_lock_data *prev; - pjsip_transaction *tsx; - int is_alive; -} tsx_lock_data; - -/* Timer timeout value constants */ -static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, PJSIP_T1_TIMEOUT%1000 }; -static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000, PJSIP_T4_TIMEOUT%1000 }; -static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000, PJSIP_TD_TIMEOUT%1000 }; -static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, - (64*PJSIP_T1_TIMEOUT)%1000 }; - -/* Internal timer IDs */ -enum Transaction_Timer_Id -{ - TSX_TIMER_RETRANSMISSION, - TSX_TIMER_TIMEOUT, -}; - -/* Function Prototypes */ -static int pjsip_tsx_on_state_null( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_calling( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_trying( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, - pjsip_event *event); -static int pjsip_tsx_on_state_destroyed( pjsip_transaction *tsx, - pjsip_event *event); -static void tsx_timer_callback( pj_timer_heap_t *theap, - pj_timer_entry *entry); -static int tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata); -static void lock_tsx( pjsip_transaction *tsx, struct tsx_lock_data *lck ); -static pj_status_t unlock_tsx( pjsip_transaction *tsx, struct tsx_lock_data *lck ); - -/* State handlers for UAC, indexed by state */ -static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *tsx, - pjsip_event *event ) = -{ - &pjsip_tsx_on_state_null, - &pjsip_tsx_on_state_calling, - &pjsip_tsx_on_state_trying, - &pjsip_tsx_on_state_proceeding_uac, - &pjsip_tsx_on_state_completed_uac, - &pjsip_tsx_on_state_confirmed, - &pjsip_tsx_on_state_terminated, - &pjsip_tsx_on_state_destroyed, -}; - -/* State handlers for UAS */ -static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *tsx, - pjsip_event *event ) = -{ - &pjsip_tsx_on_state_null, - &pjsip_tsx_on_state_calling, - &pjsip_tsx_on_state_trying, - &pjsip_tsx_on_state_proceeding_uas, - &pjsip_tsx_on_state_completed_uas, - &pjsip_tsx_on_state_confirmed, - &pjsip_tsx_on_state_terminated, - &pjsip_tsx_on_state_destroyed, -}; - -/* - * Get transaction state name. - */ -PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state) -{ - return state_str[state]; -} - -/* - * Get the role name. - */ -PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role) -{ - return role_name[role]; -} - - -/* - * Create transaction key for RFC2543 compliant messages, which don't have - * unique branch parameter in the top most Via header. - * - * INVITE requests matches a transaction if the following attributes - * match the original request: - * - Request-URI - * - To tag - * - From tag - * - Call-ID - * - CSeq - * - top Via header - * - * CANCEL matching is done similarly as INVITE, except: - * - CSeq method will differ - * - To tag is not matched. - * - * ACK matching is done similarly, except that: - * - method of the CSeq will differ, - * - To tag is matched to the response sent by the server transaction. - * - * The transaction key is constructed from the common components of above - * components. Additional comparison is needed to fully match a transaction. - */ -void create_tsx_key_2543( pj_pool_t *pool, - pj_str_t *str, - pjsip_role_e role, - const pjsip_method *method, - const pjsip_rx_data *rdata ) -{ -#define SEPARATOR '$' - char *key, *p, *end; - int len; - pj_size_t len_required; - pjsip_uri *req_uri; - pj_str_t *host; - - host = &rdata->via->sent_by.host; - req_uri = (pjsip_uri*)rdata->msg->line.req.uri; - - /* Calculate length required. */ - len_required = PJSIP_MAX_URL_SIZE + /* URI */ - 9 + /* CSeq number */ - rdata->from_tag.slen + /* From tag. */ - rdata->call_id.slen + /* Call-ID */ - host->slen + /* Via host. */ - 9 + /* Via port. */ - 32; /* Separator+Allowance. */ - key = p = pj_pool_alloc(pool, len_required); - end = p + len_required; - - /* Add role. */ - *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); - *p++ = SEPARATOR; - - /* Add Request-URI */ - /* This is BUG! - * Response doesn't have Request-URI! - * - len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p ); - p += len; - *p++ = SEPARATOR; - */ - - /* Add method, except when method is INVITE or ACK. */ - if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { - pj_memcpy(p, method->name.ptr, method->name.slen); - p += method->name.slen; - *p++ = '$'; - } - - /* Add CSeq (only the number). */ - len = pj_utoa(rdata->cseq->cseq, p); - p += len; - *p++ = SEPARATOR; - - /* Add From tag. */ - len = rdata->from->tag.slen; - pj_memcpy( p, rdata->from->tag.ptr, len); - p += len; - *p++ = SEPARATOR; - - /* Add Call-ID. */ - len = rdata->call_id.slen; - pj_memcpy( p, rdata->call_id.ptr, len ); - p += len; - *p++ = SEPARATOR; - - /* Add top Via header. - * We don't really care whether the port contains the real port (because - * it can be omited if default port is used). Anyway this function is - * only used to match request retransmission, and we expect that the - * request retransmissions will contain the same port. - */ - if ((end-p) < host->slen + 12) { - goto on_error; - } - pj_memcpy(p, host->ptr, host->slen); - p += host->slen; - *p++ = ':'; - - len = pj_utoa(rdata->via->sent_by.port, p); - p += len; - *p++ = SEPARATOR; - - *p++ = '\0'; - - /* Done. */ - str->ptr = key; - str->slen = p-key; - - return; - -on_error: - PJ_LOG(2,("tsx........", "Not enough buffer (%d) for transaction key", - len_required)); - pj_assert(0); - str->ptr = NULL; - str->slen = 0; -} - -/* - * Create transaction key for RFC3161 compliant system. - */ -void create_tsx_key_3261( pj_pool_t *pool, - pj_str_t *key, - pjsip_role_e role, - const pjsip_method *method, - const pj_str_t *branch ) -{ - char *p; - - p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 ); - - /* Add role. */ - *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); - *p++ = SEPARATOR; - - /* Add method, except when method is INVITE or ACK. */ - if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { - pj_memcpy(p, method->name.ptr, method->name.slen); - p += method->name.slen; - *p++ = '$'; - } - - /* Add branch ID. */ - pj_memcpy(p, branch->ptr, branch->slen); - p += branch->slen; - - /* Set length */ - key->slen = p - key->ptr; -} - -/* - * Create key from the incoming data, to be used to search the transaction - * in the transaction hash table. - */ -PJ_DEF(void) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, - pjsip_role_e role, - const pjsip_method *method, - const pjsip_rx_data *rdata ) -{ - pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN}; - - /* Get the branch parameter in the top-most Via. - * If branch parameter is started with "z9hG4bK", then the message was - * generated by agent compliant with RFC3261. Otherwise, it will be - * handled as RFC2543. - */ - const pj_str_t *branch = &rdata->via->branch_param; - - if (pj_strncmp(branch, &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) == 0) { - - /* Create transaction key. */ - create_tsx_key_3261(pool, key, role, method, branch); - - } else { - /* Create the key for the message. This key will be matched up with the - * transaction key. For RFC2563 transactions, the transaction key - * was created by the same function, so it will match the message. - */ - create_tsx_key_2543( pool, key, role, method, rdata ); - } -} - - -/* - * Create new transaction. - */ -PJ_DEF(pjsip_transaction *) pjsip_tsx_create(pj_pool_t *pool, - pjsip_endpoint *endpt) -{ - pjsip_transaction *tsx; - - tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction)); - - tsx->pool = pool; - tsx->endpt = endpt; - tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION; - tsx->retransmit_timer._timer_id = -1; - tsx->retransmit_timer.user_data = tsx; - tsx->retransmit_timer.cb = &tsx_timer_callback; - tsx->timeout_timer.id = TSX_TIMER_TIMEOUT; - tsx->timeout_timer._timer_id = -1; - tsx->timeout_timer.user_data = tsx; - tsx->timeout_timer.cb = &tsx_timer_callback; - sprintf(tsx->obj_name, "tsx%p", tsx); - tsx->mutex = pj_mutex_create(pool, "mtsx%p", 0); - if (!tsx->mutex) { - return NULL; - } - - return tsx; -} - -/* - * Lock transaction and set the value of Thread Local Storage. - */ -static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck) -{ - struct tsx_lock_data *prev_data; - - pj_mutex_lock(tsx->mutex); - prev_data = (struct tsx_lock_data *) pj_thread_local_get(pjsip_tsx_lock_tls_id); - lck->prev = prev_data; - lck->tsx = tsx; - lck->is_alive = 1; - pj_thread_local_set(pjsip_tsx_lock_tls_id, lck); -} - - -/* - * Unlock transaction. - * This will selectively unlock the mutex ONLY IF the transaction has not been - * destroyed. The function knows whether the transaction has been destroyed - * because when transaction is destroyed the is_alive flag for the transaction - * will be set to zero. - */ -static pj_status_t unlock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck) -{ - pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck); - pj_assert( lck->tsx == tsx ); - pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev); - if (lck->is_alive) - pj_mutex_unlock(tsx->mutex); - - return lck->is_alive ? 0 : -1; -} - -/* - * Set transaction state, and inform TU about the transaction state change. - */ -static void tsx_set_state( pjsip_transaction *tsx, - pjsip_tsx_state_e state, - const pjsip_event *event ) -{ - pjsip_event e; - - PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, ev=%s (src:%s)", - state_str[tsx->state], state_str[state], pjsip_event_str(event->type), - pjsip_event_str(event->src_type))); - - /* Change state. */ - tsx->state = state; - - /* Update the state handlers. */ - if (tsx->role == PJSIP_ROLE_UAC) { - tsx->state_handler = tsx_state_handler_uac[state]; - } else { - tsx->state_handler = tsx_state_handler_uas[state]; - } - - /* Inform TU */ - pj_memcpy(&e, event, sizeof(*event)); - e.type = PJSIP_EVENT_TSX_STATE_CHANGED; - e.obj.tsx = tsx; - pjsip_endpt_send_tsx_event( tsx->endpt, &e ); - - /* When the transaction is terminated, release transport, and free the - * saved last transmitted message. - */ - if (state == PJSIP_TSX_STATE_TERMINATED) { - - /* Decrement transport reference counter. */ - if (tsx->transport && tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { - pjsip_transport_dec_ref( tsx->transport ); - tsx->transport = NULL; - } - /* Free last transmitted message. */ - if (tsx->last_tx) { - pjsip_tx_data_dec_ref( tsx->last_tx ); - tsx->last_tx = NULL; - } - /* Cancel timeout timer. */ - if (tsx->timeout_timer._timer_id != -1) { - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); - tsx->timeout_timer._timer_id = -1; - } - /* Cancel retransmission timer. */ - if (tsx->retransmit_timer._timer_id != -1) { - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); - tsx->retransmit_timer._timer_id = -1; - } - - /* If transport is not pending, reschedule timeout timer to - * destroy this transaction. - */ - if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { - pj_time_val timeout = {0, 0}; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout); - } - - } else if (state == PJSIP_TSX_STATE_DESTROYED) { - - /* Clear TLS, so that mutex will not be unlocked */ - struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id); - while (lck) { - if (lck->tsx == tsx) { - lck->is_alive = 0; - } - lck = lck->prev; - } - } -} - -/* - * Look-up destination address and select which transport to be used to send - * the request message. The procedure used here follows the guidelines on - * sending the request in RFC3261 chapter 8.1.2. - * - * This function also modifies the message (request line and Route headers) - * accordingly. - */ -static pj_status_t tsx_process_route( pjsip_transaction *tsx, - pjsip_tx_data *tdata, - pjsip_host_port *send_addr ) -{ - const pjsip_uri *new_request_uri, *target_uri; - const pjsip_name_addr *topmost_route_uri; - pjsip_route_hdr *first_route_hdr, *last_route_hdr; - - pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); - - /* Get the first "Route" header from the message. If the message doesn't - * have any "Route" headers but the endpoint has, then copy the "Route" - * headers from the endpoint first. - */ - last_route_hdr = first_route_hdr = - pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); - if (first_route_hdr) { - topmost_route_uri = &first_route_hdr->name_addr; - while (last_route_hdr->next != (void*)&tdata->msg->hdr) { - pjsip_route_hdr *hdr; - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, last_route_hdr->next); - if (!hdr) - break; - last_route_hdr = hdr; - } - } else { - const pjsip_route_hdr *hdr_list; - hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt); - if (hdr_list->next != hdr_list) { - const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next; - first_route_hdr = NULL; - topmost_route_uri = &hdr->name_addr; - do { - last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr); - if (first_route_hdr == NULL) - first_route_hdr = last_route_hdr; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr); - hdr = hdr->next; - } while (hdr != hdr_list); - } else { - topmost_route_uri = NULL; - } - } - - /* If Route headers exist, and the first element indicates loose-route, - * the URI is taken from the Request-URI, and we keep all existing Route - * headers intact. - * If Route headers exist, and the first element DOESN'T indicate loose - * route, the URI is taken from the first Route header, and remove the - * first Route header from the message. - * Otherwise if there's no Route headers, the URI is taken from the - * Request-URI. - */ - if (topmost_route_uri) { - pj_bool_t has_lr_param; - - if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) || - PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri)) - { - const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri); - has_lr_param = url->lr_param; - } else { - has_lr_param = 0; - } - - if (has_lr_param) { - new_request_uri = tdata->msg->line.req.uri; - /* We shouldn't need to delete topmost Route if it has lr param. - * But seems like it breaks some proxy implementation, so we - * delete it anyway. - */ - /* - pj_list_erase(first_route_hdr); - if (first_route_hdr == last_route_hdr) - last_route_hdr = NULL; - */ - } else { - new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri); - pj_list_erase(first_route_hdr); - if (first_route_hdr == last_route_hdr) - last_route_hdr = NULL; - } - - target_uri = (pjsip_uri*)topmost_route_uri; - - } else { - target_uri = new_request_uri = tdata->msg->line.req.uri; - } - - /* The target URI must be a SIP/SIPS URL so we can resolve it's address. - * Otherwise we're in trouble (i.e. there's no host part in tel: URL). - */ - pj_memset(send_addr, 0, sizeof(*send_addr)); - - if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) { - pjsip_uri *uri = (pjsip_uri*) target_uri; - const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); - send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE); - pj_strdup(tdata->pool, &send_addr->host, &url->host); - send_addr->port = url->port; - send_addr->type = pjsip_transport_get_type_from_name(&url->transport_param); - - } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) { - pjsip_uri *uri = (pjsip_uri*) target_uri; - const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); - pj_strdup(tdata->pool, &send_addr->host, &url->host); - send_addr->port = url->port; - send_addr->type = pjsip_transport_get_type_from_name(&url->transport_param); -#if PJ_HAS_TCP - if (send_addr->type == PJSIP_TRANSPORT_TCP || - send_addr->type == PJSIP_TRANSPORT_SCTP) - { - send_addr->flag |= PJSIP_TRANSPORT_RELIABLE; - } -#endif - } else { - PJ_LOG(2, (tsx->obj_name, "Unable to lookup destination address for " - "non SIP-URL")); - return -1; - } - - /* If target URI is different than request URI, replace - * request URI add put the original URI in the last Route header. - */ - if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) { - pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool); - route->name_addr.uri = tdata->msg->line.req.uri; - if (last_route_hdr) - pj_list_insert_after(last_route_hdr, route); - else - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route); - tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri; - } - - /* Success. */ - return 0; -} - - -/* - * Callback from the transport job. - * This callback is called when asychronous transport connect() operation - * has completed, with or without error. - */ -static void tsx_transport_callback(pjsip_transport_t *tr, - void *token, - pj_status_t status) -{ - char addr[PJ_MAX_HOSTNAME]; - pjsip_transaction *tsx = token; - struct tsx_lock_data lck; - - pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen); - addr[tsx->dest_name.host.slen] = '\0'; - - - if (status == PJ_SUCCESS) { - PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d", - pjsip_transport_get_type_name(tr), - addr, tsx->dest_name.port)); - } else { - PJ_LOG(3, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d", - pjsip_transport_get_type_name(tr), - addr, tsx->dest_name.port, status)); - } - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - if (status != PJ_SUCCESS) { - pjsip_event event; - - event.type = PJSIP_EVENT_TRANSPORT_ERROR; - event.src_type = PJSIP_EVENT_TX_MSG; - event.src.tdata = tsx->last_tx; - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); - - /* Unlock transaction. */ - unlock_tsx(tsx, &lck); - return; - } - - /* See if transaction has already been terminated. If so, schedule to destroy - * the transaction. - */ - if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - pj_time_val timeout = {0, 0}; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout); - - /* Unlock transaction. */ - unlock_tsx(tsx, &lck); - return; - } - - /* Add reference counter to the transport. */ - pjsip_transport_add_ref(tr); - - /* Mark transport as ready. */ - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->transport = tr; - - /* If there's a pending message to send, send it now. */ - if (tsx->has_unsent_msg) { - tsx_send_msg( tsx, tsx->last_tx ); - } - - /* Unlock transaction. */ - unlock_tsx(tsx, &lck); -} - -/* - * Callback from the resolver job. - */ -static void tsx_resolver_callback(pj_status_t status, - void *token, - const struct pjsip_server_addresses *addr) -{ - pjsip_transaction *tsx = token; - struct tsx_lock_data lck; - - PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status)); - - if (status != PJ_SUCCESS || addr->count == 0) { - pjsip_event event; - - event.type = PJSIP_EVENT_TRANSPORT_ERROR; - event.src_type = PJSIP_EVENT_TX_MSG; - event.src.tdata = tsx->last_tx; - - lock_tsx(tsx, &lck); - tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR; - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); - unlock_tsx(tsx, &lck); - return; - } - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - /* Copy server addresses. */ - pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr)); - - /* Create/find the transport for the remote address. */ - PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d", - pj_sockaddr_get_str_addr(&addr->entry[0].addr), - pj_sockaddr_get_port(&addr->entry[0].addr))); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING; - pjsip_endpt_get_transport(tsx->endpt, tsx->pool, - addr->entry[0].type, &addr->entry[0].addr, - tsx, - &tsx_transport_callback); - - /* Unlock transaction */ - unlock_tsx(tsx, &lck); - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the callback for the - * transport is called. - */ -} - -/* - * Initialize the transaction as UAC transaction. - */ -PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, - pjsip_tx_data *tdata) -{ - pjsip_msg *msg; - pjsip_cseq_hdr *cseq; - pjsip_via_hdr *via; - struct tsx_lock_data lck; - const pjsip_hdr *endpt_hdr; - - PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata)); - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - /* Keep shortcut */ - msg = tdata->msg; - - /* Role is UAC. */ - tsx->role = PJSIP_ROLE_UAC; - - /* Save method. */ - pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); - - /* Generate branch parameter if it doesn't exist. */ - via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); - if (via == NULL) { - via = pjsip_via_hdr_create(tdata->pool); - pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via); - } - - if (via->branch_param.slen == 0) { - pj_str_t tmp; - via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN); - via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; - pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, - PJSIP_RFC3261_BRANCH_LEN); - - tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN; - pj_generate_unique_string( &tmp ); - } - - /* Copy branch parameter. */ - tsx->branch = via->branch_param; - - /* Add additional request headers from endpoint. */ - endpt_hdr = pjsip_endpt_get_request_headers(tsx->endpt)->next; - while (endpt_hdr != pjsip_endpt_get_request_headers(tsx->endpt)) { - pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr); - pjsip_msg_add_hdr( tdata->msg, hdr ); - endpt_hdr = endpt_hdr->next; - } - - /* Generate transaction key. */ - create_tsx_key_3261( tsx->pool, &tsx->transaction_key, - PJSIP_ROLE_UAC, &tsx->method, - &via->branch_param); - - PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, - tsx->transaction_key.ptr)); - - /* Save CSeq. */ - cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL); - if (!cseq) { - PJ_LOG(4,(tsx->obj_name, "CSeq header not present in outgoing message!")); - return -1; - } - tsx->cseq = cseq->cseq; - - - /* Begin with State_Null. - * Manually set-up the state becase we don't want to call the callback. - */ - tsx->state = PJSIP_TSX_STATE_NULL; - tsx->state_handler = pjsip_tsx_on_state_null; - - /* Get destination name from the message. */ - if (tsx_process_route(tsx, tdata, &tsx->dest_name) != 0) { - pjsip_event event; - PJ_LOG(3,(tsx->obj_name, "Error: unable to get destination address for request")); - - event.type = PJSIP_EVENT_TRANSPORT_ERROR; - event.src_type = PJSIP_EVENT_TX_MSG; - event.src.tdata = tsx->last_tx; - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); - unlock_tsx(tsx, &lck); - return -1; - } - - /* Resolve destination. - * This will start asynchronous resolver job, and when it finishes, - * the callback will be called. - */ - PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", - tsx->dest_name.host.slen, - tsx->dest_name.host.ptr, - tsx->dest_name.port)); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, - tsx, &tsx_resolver_callback); - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the resolver callback is - * called. - */ - - /* Unlock transaction and return. - * If transaction has been destroyed WITHIN the current thread, the - * unlock_tsx() function will return -1. - */ - return unlock_tsx(tsx, &lck); -} - - -/* - * Initialize the transaction as UAS transaction. - */ -PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - pjsip_msg *msg = rdata->msg; - pj_str_t *branch; - pjsip_cseq_hdr *cseq; - struct tsx_lock_data lck; - - PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata)); - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - /* Keep shortcut to message */ - msg = rdata->msg; - - /* Role is UAS */ - tsx->role = PJSIP_ROLE_UAS; - - /* Save method. */ - pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); - - /* Get transaction key either from branch for RFC3261 message, or - * create transaction key. - */ - pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, PJSIP_ROLE_UAS, - &tsx->method, rdata); - - /* Duplicate branch parameter for transaction. */ - branch = &rdata->via->branch_param; - pj_strdup(tsx->pool, &tsx->branch, branch); - - PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, - tsx->transaction_key.ptr)); - - /* Save CSeq */ - cseq = rdata->cseq; - tsx->cseq = cseq->cseq; - - /* Begin with state NULL - * Manually set-up the state becase we don't want to call the callback. - */ - tsx->state = PJSIP_TSX_STATE_NULL; - tsx->state_handler = &pjsip_tsx_on_state_null; - - /* Get the transport to send the response. - * According to section 18.2.2 of RFC3261, if the transport is reliable - * then the response must be sent using that transport. - */ - /* In addition, RFC 3581 says, if Via has "rport" parameter specified, - * then return the response using the same transport. - */ - if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->transport) || - rdata->via->rport_param >= 0) - { - tsx->transport = rdata->transport; - pjsip_transport_add_ref(tsx->transport); - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - - tsx->current_addr = 0; - tsx->remote_addr.count = 1; - tsx->remote_addr.entry[0].type = pjsip_transport_get_type(tsx->transport); - pj_memcpy(&tsx->remote_addr.entry[0].addr, - &rdata->addr, rdata->addr_len); - - } else { - pj_status_t status; - - status = pjsip_get_response_addr(tsx->pool, rdata->transport, - rdata->via, &tsx->dest_name); - if (status != PJ_SUCCESS) { - pjsip_event event; - PJ_LOG(2,(tsx->obj_name, "Unable to get destination address " - "for response")); - - event.type = PJSIP_EVENT_TRANSPORT_ERROR; - event.src_type = PJSIP_EVENT_TX_MSG; - event.src.tdata = tsx->last_tx; - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); - unlock_tsx(tsx, &lck); - return status; - } - - /* Resolve destination. - * This will start asynchronous resolver job, and when it finishes, - * the callback will be called. - */ - PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", - tsx->dest_name.host.slen, tsx->dest_name.host.ptr, - tsx->dest_name.port)); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, - tsx, &tsx_resolver_callback); - } - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the resolver callback is - * called. - */ - - /* Unlock transaction and return. - * If transaction has been destroyed WITHIN the current thread, the - * unlock_tsx() function will return -1. - */ - return unlock_tsx(tsx, &lck); -} - -/* - * Callback when timer expires. - */ -static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) -{ - pjsip_event event; - pjsip_transaction *tsx = entry->user_data; - struct tsx_lock_data lck; - - PJ_UNUSED_ARG(theap); - - PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)", - (entry->id == TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout"))); - - event.type = event.src_type = PJSIP_EVENT_TIMER; - event.src.timer = (entry->id == TSX_TIMER_RETRANSMISSION ? - &tsx->retransmit_timer : &tsx->timeout_timer); - - /* Dispatch event to transaction. */ - lock_tsx(tsx, &lck); - (*tsx->state_handler)(tsx, &event); - unlock_tsx(tsx, &lck); -} - -/* - * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for - * non-2xx/INVITE is automatically sent by the transaction. - * This operation is only valid if the transaction is configured to handle ACK - * (tsx->handle_ack is non-zero). If this attribute is not set, then the - * transaction will comply with RFC-3261, i.e. it will set itself to - * TERMINATED state when it receives 2xx/INVITE. - */ -PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata) -{ - pjsip_msg *msg; - pjsip_host_port dest_addr; - pjsip_event event; - pjsip_via_hdr *via; - struct tsx_lock_data lck; - - lock_tsx(tsx, &lck); - - pj_assert(tsx->handle_ack != 0); - - msg = tdata->msg; - - /* Generate branch parameter if it doesn't exist. */ - via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); - if (via == NULL) { - via = pjsip_via_hdr_create(tdata->pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) via); - } - - if (via->branch_param.slen == 0) { - via->branch_param = tsx->branch; - } else { - pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 ); - } - - /* Get destination name from the message. */ - if (tsx_process_route(tsx, tdata, &dest_addr) != 0){ - PJ_LOG(2,(tsx->obj_name, "Unable to get destination address for request")); - goto on_error; - } - - /* Compare message's destination name with transaction's destination name. - * If NOT equal, then we'll have to resolve the destination. - */ - if (dest_addr.type == tsx->dest_name.type && - dest_addr.flag == tsx->dest_name.flag && - dest_addr.port == tsx->dest_name.port && - pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0) - { - /* Equal destination. We can use current transport. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - unlock_tsx(tsx, &lck); - return; - - } - - /* New destination; we'll have to resolve host and create new transport. */ - pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr)); - pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host); - - PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", - tsx->dest_name.host.slen, - tsx->dest_name.host.ptr, - tsx->dest_name.port)); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_transport_dec_ref(tsx->transport); - tsx->transport = NULL; - - /* Put the message in queue. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - /* This is a bug! - * We shouldn't change transaction's state before actually sending the - * message. Otherwise transaction will terminate before message is sent, - * and timeout timer will be scheduled. - */ - PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK) - - /* - * This will start asynchronous resolver job, and when it finishes, - * the callback will be called. - */ - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, - tsx, &tsx_resolver_callback); - - unlock_tsx(tsx, &lck); - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the resolver callback is - * called. - */ - return; - -on_error: - /* Failure condition. - * Send TERMINATED event. - */ - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - event.type = PJSIP_EVENT_TRANSPORT_ERROR; - event.src_type = PJSIP_EVENT_TX_MSG; - event.src.tdata = tdata; - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event); - - unlock_tsx(tsx, &lck); -} - - -/* - * This function is called by TU to send a message. - */ -PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx, - pjsip_tx_data *tdata ) -{ - pjsip_event event; - struct tsx_lock_data lck; - - PJ_LOG(5,(tsx->obj_name, "on transmit msg (tdata=%p)", tdata)); - - event.type = event.src_type = PJSIP_EVENT_TX_MSG; - event.src.tdata = tdata; - - PJ_LOG(5,(tsx->obj_name, "on state %s (ev=%s, src=%s, data=%p)", - state_str[tsx->state], pjsip_event_str(event.type), - pjsip_event_str(event.src_type), event.src.data)); - - /* Dispatch to transaction. */ - lock_tsx(tsx, &lck); - (*tsx->state_handler)(tsx, &event); - unlock_tsx(tsx, &lck); -} - -/* - * This function is called by endpoint when incoming message for the - * transaction is received. - */ -PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - pjsip_event event; - struct tsx_lock_data lck; - - event.type = event.src_type = PJSIP_EVENT_RX_MSG; - event.src.rdata = rdata; - - PJ_LOG(5,(tsx->obj_name, "on state %s (ev=%s, src=%s, data=%p)", - state_str[tsx->state], pjsip_event_str(event.type), - pjsip_event_str(event.src_type), event.src.data)); - - /* Dispatch to transaction. */ - lock_tsx(tsx, &lck); - (*tsx->state_handler)(tsx, &event); - unlock_tsx(tsx, &lck); -} - -/* - * Forcely terminate transaction. - */ -PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code ) -{ - pjsip_event event; - struct tsx_lock_data lck; - - lock_tsx(tsx, &lck); - - tsx->status_code = code; - event.type = PJSIP_EVENT_USER; - event.src_type = PJSIP_EVENT_USER; - event.src.data = 0; - event.obj.tsx = tsx; - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event); - - unlock_tsx(tsx, &lck); -} - -/* - * Send message to the transport. - * If transport is not yet available, then do nothing. The message will be - * transmitted when transport connection completion callback is called. - */ -static int tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata) -{ - pjsip_event event; - - PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata)); - - if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { - int sent; - pjsip_event before_tx_event; - - pj_assert(tsx->transport != NULL); - - /* Make sure Via transport info is filled up properly for - * requests. - */ - if (tdata->msg->type == PJSIP_REQUEST_MSG) { - const pj_sockaddr_in *addr_name; - pjsip_via_hdr *via = (pjsip_via_hdr*) - pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - - /* For request message, set "rport" parameter by default. */ - if (tdata->msg->type == PJSIP_REQUEST_MSG) - via->rport_param = 0; - - /* Don't update Via sent-by on retransmission. */ - if (via->sent_by.host.slen == 0) { - addr_name = pjsip_transport_get_addr_name(tsx->transport); - pj_strdup2(tdata->pool, &via->transport, - pjsip_transport_get_type_name(tsx->transport)); - pj_strdup2(tdata->pool, &via->sent_by.host, - pj_sockaddr_get_str_addr(addr_name)); - via->sent_by.port = pj_sockaddr_get_port(addr_name); - } - } - - /* Notify everybody we're about to send message. */ - before_tx_event.type = PJSIP_EVENT_BEFORE_TX; - before_tx_event.src_type = PJSIP_EVENT_TX_MSG; - before_tx_event.obj.tsx = tsx; - before_tx_event.src.tdata = tdata; - before_tx_event.data.long_data = tsx->retransmit_count; - pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event ); - - tsx->has_unsent_msg = 0; - sent = pjsip_transport_send_msg( - tsx->transport, tdata, - &tsx->remote_addr.entry[tsx->current_addr].addr - ); - if (sent < 1) { - goto on_error; - } - } else { - tsx->has_unsent_msg = 1; - } - - return 0; - -on_error: - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - event.type = PJSIP_EVENT_TRANSPORT_ERROR; - event.src_type = PJSIP_EVENT_TX_MSG; - event.src.tdata = tdata; - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event); - return -1; -} - -/* - * Retransmit last message sent. - */ -static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx, - int should_restart_timer) -{ - PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)", - tsx->last_tx, tsx->retransmit_count, should_restart_timer)); - - pj_assert(tsx->last_tx != NULL); - - ++tsx->retransmit_count; - - if (tsx_send_msg( tsx, tsx->last_tx) != 0) { - return -1; - } - - /* Restart timer T1. */ - if (should_restart_timer) { - pj_time_val timeout; - int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT; - - if (tsx->method.id != PJSIP_INVITE_METHOD && msec_time > PJSIP_T2_TIMEOUT) - msec_time = PJSIP_T2_TIMEOUT; - - timeout.sec = msec_time / 1000; - timeout.msec = msec_time % 1000; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, &timeout); - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in state Null. - */ -static int pjsip_tsx_on_state_null( pjsip_transaction *tsx, pjsip_event *event ) -{ - pj_assert( tsx->state == PJSIP_TSX_STATE_NULL); - pj_assert( tsx->last_tx == NULL ); - pj_assert( tsx->has_unsent_msg == 0); - - if (tsx->role == PJSIP_ROLE_UAS) { - - /* Set state to Trying. */ - pj_assert(event->type == PJSIP_EVENT_RX_MSG); - tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, event); - - } else { - pjsip_tx_data *tdata = event->src.tdata; - - /* Save the message for retransmission. */ - tsx->last_tx = tdata; - pjsip_tx_data_add_ref(tdata); - - /* Send the message. */ - if (tsx_send_msg( tsx, tdata) != 0) { - return -1; - } - - /* Start Timer B (or called timer F for non-INVITE) for transaction - * timeout. - */ - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout_timer_val); - - /* Start Timer A (or timer E) for retransmission only if unreliable - * transport is being used. - */ - if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL && - PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) - { - pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, &t1_timer_val); - tsx->retransmit_count = 0; - } - - /* Move state. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, event); - } - - return PJ_SUCCESS; -} - -/* - * State Calling is for UAC after it sends request but before any responses - * is received. - */ -static int pjsip_tsx_on_state_calling( pjsip_transaction *tsx, - pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING); - pj_assert(tsx->role == PJSIP_ROLE_UAC); - - if (event->type == PJSIP_EVENT_TIMER && - event->src.timer == &tsx->retransmit_timer) - { - - /* Retransmit the request. */ - if (pjsip_tsx_retransmit( tsx, 1 ) != 0) { - return -1; - } - - } else if (event->type == PJSIP_EVENT_TIMER && - event->src.timer == &tsx->timeout_timer) - { - - /* Cancel retransmission timer. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); - } - - /* Set status code */ - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - - /* Inform TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event ); - - /* Transaction is destroyed */ - return -1; - - } else if (event->type == PJSIP_EVENT_RX_MSG) { - int code; - - /* Cancel retransmission timer A. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); - - /* Cancel timer B (transaction timeout) */ - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); - - /* Discard retransmission message if it is not INVITE. - * The INVITE tdata is needed in case we have to generate ACK for - * the final response. - */ - /* Keep last_tx for authorization. */ - code = event->src.rdata->msg->line.status.code; - if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) { - pjsip_tx_data_dec_ref(tsx->last_tx); - tsx->last_tx = NULL; - } - - /* Processing is similar to state Proceeding. */ - pjsip_tsx_on_state_proceeding_uac( tsx, event); - - } else { - pj_assert(0); - } - - return PJ_SUCCESS; -} - -/* - * State Trying is for UAS after it received request but before any responses - * is sent. - * Note: this is different than RFC3261, which can use Trying state for - * non-INVITE client transaction (bug in RFC?). - */ -static int pjsip_tsx_on_state_trying( pjsip_transaction *tsx, pjsip_event *event) -{ - int result; - - pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING); - - /* This state is only for UAS */ - pj_assert(tsx->role == PJSIP_ROLE_UAS); - - /* Better be transmission of response message. - * If we've got request retransmission, this means that the TU hasn't - * transmitted any responses within 500 ms, which is not allowed. If - * this happens, just ignore the event (we couldn't retransmit last - * response because we haven't sent any!). - */ - //pj_assert(event->type == PJSIP_EVENT_TX_MSG); - if (event->type != PJSIP_EVENT_TX_MSG) { - return PJ_SUCCESS; - } - - /* The rest of the processing of the event is exactly the same as in - * "Proceeding" state. - */ - result = pjsip_tsx_on_state_proceeding_uas( tsx, event); - - /* Inform the TU of the state transision if state is still State_Trying */ - if (result==0 && tsx->state == PJSIP_TSX_STATE_TRYING) { - tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event); - } - - return result; -} - -/* - * Handler for events in Proceeding for UAS - * This state happens after the TU sends provisional response. - */ -static int pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || - tsx->state == PJSIP_TSX_STATE_TRYING); - - /* This state is only for UAS. */ - pj_assert(tsx->role == PJSIP_ROLE_UAS); - - /* Receive request retransmission. */ - if (event->type == PJSIP_EVENT_RX_MSG) { - - /* Send last response. */ - if (pjsip_tsx_retransmit( tsx, 0 ) != 0) { - return -1; - } - - } else if (event->type == PJSIP_EVENT_TX_MSG ) { - pjsip_tx_data *tdata = event->src.tdata; - - /* The TU sends response message to the request. Save this message so - * that we can retransmit the last response in case we receive request - * retransmission. - */ - pjsip_msg *msg = tdata->msg; - - /* This can only be a response message. */ - pj_assert(msg->type == PJSIP_RESPONSE_MSG); - - /* Status code must be higher than last sent. */ - pj_assert(msg->line.status.code >= tsx->status_code); - - /* Update last status */ - tsx->status_code = msg->line.status.code; - - /* Discard the saved last response (it will be updated later as - * necessary). - */ - if (tsx->last_tx && tsx->last_tx != tdata) { - pjsip_tx_data_dec_ref( tsx->last_tx ); - tsx->last_tx = NULL; - } - - /* Send the message. */ - if (tsx_send_msg(tsx, tdata) != 0) { - return -1; - } - - // Update To tag header for RFC2543 transaction. - // TODO: - - /* Update transaction state */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { - - if (tsx->last_tx != tdata) { - tsx->last_tx = tdata; - pjsip_tx_data_add_ref( tdata ); - } - tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event); - - } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - - if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) { - - /* 2xx class message is not saved, because retransmission is handled - * by the TU. - */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); - - /* Transaction is destroyed. */ - return -1; - - } else { - pj_time_val timeout; - - if (tsx->method.id == PJSIP_INVITE_METHOD) { - tsx->retransmit_count = 0; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, - &t1_timer_val); - } - - /* Save last response sent for retransmission when request - * retransmission is received. - */ - if (tsx->last_tx != tdata) { - tsx->last_tx = tdata; - pjsip_tx_data_add_ref(tdata); - } - - /* Start timer J at 64*T1 for unreliable transport or zero for - * reliable transport. - */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { - timeout = timeout_timer_val; - } else { - timeout.sec = timeout.msec = 0; - } - - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); - - /* Set state to "Completed" */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); - } - - } else if (tsx->status_code >= 300) { - - /* 3xx-6xx class message causes transaction to move to "Completed" state. */ - if (tsx->last_tx != tdata) { - tsx->last_tx = tdata; - pjsip_tx_data_add_ref( tdata ); - } - - /* Start timer H for transaction termination */ - pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,&timeout_timer_val); - - /* For INVITE, if unreliable transport is used, retransmission - * timer G will be scheduled (retransmission). - */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { - pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL); - if (cseq->method.id == PJSIP_INVITE_METHOD) { - tsx->retransmit_count = 0; - pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, - &t1_timer_val); - } - } - - /* Inform TU */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); - - } else { - pj_assert(0); - } - - - } else if (event->type == PJSIP_EVENT_TIMER && - event->src.timer == &tsx->retransmit_timer) { - /* Retransmission timer elapsed. */ - - /* Must have last response to retransmit. */ - pj_assert(tsx->last_tx != NULL); - - /* Retransmit the last response. */ - if (pjsip_tsx_retransmit( tsx, 1 ) != 0) { - return -1; - } - - } else if (event->type == PJSIP_EVENT_TIMER && - event->src.timer == &tsx->timeout_timer) { - - /* Timeout timer. should not happen? */ - pj_assert(0); - - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); - - return -1; - - } else { - pj_assert(0); - } - - return 0; -} - -/* - * Handler for events in Proceeding for UAC - * This state happens after provisional response(s) has been received from - * UAS. - */ -static int pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, pjsip_event *event) -{ - - pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || - tsx->state == PJSIP_TSX_STATE_CALLING); - - if (event->type != PJSIP_EVENT_TIMER) { - /* Must be incoming response, because we should not retransmit - * request once response has been received. - */ - pj_assert(event->type == PJSIP_EVENT_RX_MSG); - if (event->type != PJSIP_EVENT_RX_MSG) { - return 0; - } - - tsx->status_code = event->src.rdata->msg->line.status.code; - } else { - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - } - - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { - - /* Inform the message to TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event); - - } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { - - /* Stop timeout timer B/F. */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); - - /* For INVITE, the state moves to Terminated state (because ACK is - * handled in TU). For non-INVITE, state moves to Completed. - */ - if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) { - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); - return -1; - - } else { - pj_time_val timeout; - - /* For unreliable transport, start timer D (for INVITE) or - * timer K for non-INVITE. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - timeout = td_timer_val; - } else { - timeout = t4_timer_val; - } - } else { - timeout.sec = timeout.msec = 0; - } - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); - - /* Move state to Completed, inform TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); - } - - } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { - pj_time_val timeout; - - /* Stop timer B. */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); - - /* Generate and send ACK for INVITE. */ - if (tsx->method.id == PJSIP_INVITE_METHOD) { - pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, event->src.rdata ); - if (tsx_send_msg( tsx, tsx->last_tx) != 0) { - return -1; - } - } - - /* Start Timer D with TD/T4 timer if unreliable transport is used. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - timeout = td_timer_val; - } else { - timeout = t4_timer_val; - } - } else { - timeout.sec = timeout.msec = 0; - } - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); - - /* Inform TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); - - } else { - // Shouldn't happen because there's no timer for this state. - pj_assert(0); - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in Completed state for UAS - */ -static int pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); - - if (event->type == PJSIP_EVENT_RX_MSG) { - pjsip_msg *msg = event->src.rdata->msg; - pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL ); - - /* On receive request retransmission, retransmit last response. */ - if (cseq->method.id != PJSIP_ACK_METHOD) { - if (pjsip_tsx_retransmit( tsx, 0 ) != 0) { - return -1; - } - - } else { - /* Process incoming ACK request. */ - - /* Cease retransmission. */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer ); - - /* Start timer I in T4 interval (transaction termination). */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &t4_timer_val); - - /* Move state to "Confirmed" */ - tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, event); - } - - } else if (event->type == PJSIP_EVENT_TIMER) { - - if (event->src.timer == &tsx->retransmit_timer) { - /* Retransmit message. */ - if (pjsip_tsx_retransmit( tsx, 1 ) != 0) { - return -1; - } - - } else { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - - /* For INVITE, this means that ACK was never received. - * Set state to Terminated, and inform TU. - */ - - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); - - return -1; - - } else { - /* Transaction terminated, it can now be deleted. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); - return -1; - } - } - - } else { - /* Ignore request to transmit. */ - pj_assert(event->src.tdata == tsx->last_tx); - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in Completed state for UAC transaction. - */ -static int pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); - - if (event->type == PJSIP_EVENT_TIMER) { - /* Must be the timeout timer. */ - pj_assert(event->src.timer == &tsx->timeout_timer); - - /* Move to Terminated state. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); - - /* Transaction has been destroyed. */ - return -1; - - } else if (event->type == PJSIP_EVENT_RX_MSG) { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - /* On received of final response retransmission, retransmit the ACK. - * TU doesn't need to be informed. - */ - pjsip_msg *msg = event->src.rdata->msg; - pj_assert(msg->type == PJSIP_RESPONSE_MSG); - if (msg->type==PJSIP_RESPONSE_MSG && - msg->line.status.code >= 200) - { - if (pjsip_tsx_retransmit( tsx, 0 ) != 0) { - return -1; - } - } else { - /* Very late retransmission of privisional response. */ - pj_assert( msg->type == PJSIP_RESPONSE_MSG ); - } - } else { - /* Just drop the response. */ - } - } else if (tsx->method.id == PJSIP_INVITE_METHOD && - event->type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) { - - /* Set last transmitted message. */ - if (tsx->last_tx != event->src.tdata) { - pjsip_tx_data_dec_ref( tsx->last_tx ); - tsx->last_tx = event->src.tdata; - pjsip_tx_data_add_ref( tsx->last_tx ); - } - - /* No state changed, but notify app. - * Must notify now, so app has chance to put SDP in outgoing ACK msg. - */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event ); - - /* Send msg */ - tsx_send_msg(tsx, event->src.tdata); - - } else { - pj_assert(0); - } - - return 0; -} - -/* - * Handler for events in state Confirmed. - */ -static int pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED); - - /* This state is only for UAS for INVITE. */ - pj_assert(tsx->role == PJSIP_ROLE_UAS); - pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); - - /* Absorb any ACK received. */ - if (event->type == PJSIP_EVENT_RX_MSG) { - /* Must be a request message. */ - pj_assert(event->src.rdata->msg->type == PJSIP_REQUEST_MSG); - - /* Must be an ACK request or a late INVITE retransmission. */ - pj_assert(event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD || - event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD); - - } else if (event->type == PJSIP_EVENT_TIMER) { - /* Must be from timeout_timer_. */ - pj_assert(event->src.timer == &tsx->timeout_timer); - - /* Move to Terminated state. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); - - /* Transaction has been destroyed. */ - return -1; - - } else { - pj_assert(0); - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in state Terminated. - */ -static int pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED); - - PJ_UNUSED_ARG(event) - - /* Destroy this transaction */ - tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, event); - - return PJ_SUCCESS; -} - - -static int pjsip_tsx_on_state_destroyed( pjsip_transaction *tsx, pjsip_event *event) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(event) - return PJ_SUCCESS; -} - +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transaction.c 21 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Thread Local Storage ID for transaction lock (initialized by endpoint) */ +int pjsip_tsx_lock_tls_id; + +/* State names */ +static const char *state_str[] = +{ + "Null", + "Calling", + "Trying", + "Proceeding", + "Completed", + "Confirmed", + "Terminated", + "Destroyed", +}; + +/* Role names */ +static const char *role_name[] = +{ + "Client", + "Server" +}; + +/* Transaction lock. */ +typedef struct tsx_lock_data { + struct tsx_lock_data *prev; + pjsip_transaction *tsx; + int is_alive; +} tsx_lock_data; + +/* Timer timeout value constants */ +static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, PJSIP_T1_TIMEOUT%1000 }; +static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000, PJSIP_T4_TIMEOUT%1000 }; +static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000, PJSIP_TD_TIMEOUT%1000 }; +static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, + (64*PJSIP_T1_TIMEOUT)%1000 }; + +/* Internal timer IDs */ +enum Transaction_Timer_Id +{ + TSX_TIMER_RETRANSMISSION, + TSX_TIMER_TIMEOUT, +}; + +/* Function Prototypes */ +static int pjsip_tsx_on_state_null( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_calling( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_trying( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, + pjsip_event *event); +static int pjsip_tsx_on_state_destroyed( pjsip_transaction *tsx, + pjsip_event *event); +static void tsx_timer_callback( pj_timer_heap_t *theap, + pj_timer_entry *entry); +static int tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata); +static void lock_tsx( pjsip_transaction *tsx, struct tsx_lock_data *lck ); +static pj_status_t unlock_tsx( pjsip_transaction *tsx, struct tsx_lock_data *lck ); + +/* State handlers for UAC, indexed by state */ +static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *tsx, + pjsip_event *event ) = +{ + &pjsip_tsx_on_state_null, + &pjsip_tsx_on_state_calling, + &pjsip_tsx_on_state_trying, + &pjsip_tsx_on_state_proceeding_uac, + &pjsip_tsx_on_state_completed_uac, + &pjsip_tsx_on_state_confirmed, + &pjsip_tsx_on_state_terminated, + &pjsip_tsx_on_state_destroyed, +}; + +/* State handlers for UAS */ +static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *tsx, + pjsip_event *event ) = +{ + &pjsip_tsx_on_state_null, + &pjsip_tsx_on_state_calling, + &pjsip_tsx_on_state_trying, + &pjsip_tsx_on_state_proceeding_uas, + &pjsip_tsx_on_state_completed_uas, + &pjsip_tsx_on_state_confirmed, + &pjsip_tsx_on_state_terminated, + &pjsip_tsx_on_state_destroyed, +}; + +/* + * Get transaction state name. + */ +PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state) +{ + return state_str[state]; +} + +/* + * Get the role name. + */ +PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role) +{ + return role_name[role]; +} + + +/* + * Create transaction key for RFC2543 compliant messages, which don't have + * unique branch parameter in the top most Via header. + * + * INVITE requests matches a transaction if the following attributes + * match the original request: + * - Request-URI + * - To tag + * - From tag + * - Call-ID + * - CSeq + * - top Via header + * + * CANCEL matching is done similarly as INVITE, except: + * - CSeq method will differ + * - To tag is not matched. + * + * ACK matching is done similarly, except that: + * - method of the CSeq will differ, + * - To tag is matched to the response sent by the server transaction. + * + * The transaction key is constructed from the common components of above + * components. Additional comparison is needed to fully match a transaction. + */ +void create_tsx_key_2543( pj_pool_t *pool, + pj_str_t *str, + pjsip_role_e role, + const pjsip_method *method, + const pjsip_rx_data *rdata ) +{ +#define SEPARATOR '$' + char *key, *p, *end; + int len; + pj_size_t len_required; + pjsip_uri *req_uri; + pj_str_t *host; + + host = &rdata->via->sent_by.host; + req_uri = (pjsip_uri*)rdata->msg->line.req.uri; + + /* Calculate length required. */ + len_required = PJSIP_MAX_URL_SIZE + /* URI */ + 9 + /* CSeq number */ + rdata->from_tag.slen + /* From tag. */ + rdata->call_id.slen + /* Call-ID */ + host->slen + /* Via host. */ + 9 + /* Via port. */ + 32; /* Separator+Allowance. */ + key = p = pj_pool_alloc(pool, len_required); + end = p + len_required; + + /* Add role. */ + *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); + *p++ = SEPARATOR; + + /* Add Request-URI */ + /* This is BUG! + * Response doesn't have Request-URI! + * + len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p ); + p += len; + *p++ = SEPARATOR; + */ + + /* Add method, except when method is INVITE or ACK. */ + if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { + pj_memcpy(p, method->name.ptr, method->name.slen); + p += method->name.slen; + *p++ = '$'; + } + + /* Add CSeq (only the number). */ + len = pj_utoa(rdata->cseq->cseq, p); + p += len; + *p++ = SEPARATOR; + + /* Add From tag. */ + len = rdata->from->tag.slen; + pj_memcpy( p, rdata->from->tag.ptr, len); + p += len; + *p++ = SEPARATOR; + + /* Add Call-ID. */ + len = rdata->call_id.slen; + pj_memcpy( p, rdata->call_id.ptr, len ); + p += len; + *p++ = SEPARATOR; + + /* Add top Via header. + * We don't really care whether the port contains the real port (because + * it can be omited if default port is used). Anyway this function is + * only used to match request retransmission, and we expect that the + * request retransmissions will contain the same port. + */ + if ((end-p) < host->slen + 12) { + goto on_error; + } + pj_memcpy(p, host->ptr, host->slen); + p += host->slen; + *p++ = ':'; + + len = pj_utoa(rdata->via->sent_by.port, p); + p += len; + *p++ = SEPARATOR; + + *p++ = '\0'; + + /* Done. */ + str->ptr = key; + str->slen = p-key; + + return; + +on_error: + PJ_LOG(2,("tsx........", "Not enough buffer (%d) for transaction key", + len_required)); + pj_assert(0); + str->ptr = NULL; + str->slen = 0; +} + +/* + * Create transaction key for RFC3161 compliant system. + */ +void create_tsx_key_3261( pj_pool_t *pool, + pj_str_t *key, + pjsip_role_e role, + const pjsip_method *method, + const pj_str_t *branch ) +{ + char *p; + + p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 ); + + /* Add role. */ + *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); + *p++ = SEPARATOR; + + /* Add method, except when method is INVITE or ACK. */ + if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { + pj_memcpy(p, method->name.ptr, method->name.slen); + p += method->name.slen; + *p++ = '$'; + } + + /* Add branch ID. */ + pj_memcpy(p, branch->ptr, branch->slen); + p += branch->slen; + + /* Set length */ + key->slen = p - key->ptr; +} + +/* + * Create key from the incoming data, to be used to search the transaction + * in the transaction hash table. + */ +PJ_DEF(void) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, + pjsip_role_e role, + const pjsip_method *method, + const pjsip_rx_data *rdata ) +{ + pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, PJSIP_RFC3261_BRANCH_LEN}; + + /* Get the branch parameter in the top-most Via. + * If branch parameter is started with "z9hG4bK", then the message was + * generated by agent compliant with RFC3261. Otherwise, it will be + * handled as RFC2543. + */ + const pj_str_t *branch = &rdata->via->branch_param; + + if (pj_strncmp(branch, &rfc3261_branch, PJSIP_RFC3261_BRANCH_LEN) == 0) { + + /* Create transaction key. */ + create_tsx_key_3261(pool, key, role, method, branch); + + } else { + /* Create the key for the message. This key will be matched up with the + * transaction key. For RFC2563 transactions, the transaction key + * was created by the same function, so it will match the message. + */ + create_tsx_key_2543( pool, key, role, method, rdata ); + } +} + + +/* + * Create new transaction. + */ +PJ_DEF(pjsip_transaction *) pjsip_tsx_create(pj_pool_t *pool, + pjsip_endpoint *endpt) +{ + pjsip_transaction *tsx; + + tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction)); + + tsx->pool = pool; + tsx->endpt = endpt; + tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION; + tsx->retransmit_timer._timer_id = -1; + tsx->retransmit_timer.user_data = tsx; + tsx->retransmit_timer.cb = &tsx_timer_callback; + tsx->timeout_timer.id = TSX_TIMER_TIMEOUT; + tsx->timeout_timer._timer_id = -1; + tsx->timeout_timer.user_data = tsx; + tsx->timeout_timer.cb = &tsx_timer_callback; + sprintf(tsx->obj_name, "tsx%p", tsx); + tsx->mutex = pj_mutex_create(pool, "mtsx%p", 0); + if (!tsx->mutex) { + return NULL; + } + + return tsx; +} + +/* + * Lock transaction and set the value of Thread Local Storage. + */ +static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck) +{ + struct tsx_lock_data *prev_data; + + pj_mutex_lock(tsx->mutex); + prev_data = (struct tsx_lock_data *) pj_thread_local_get(pjsip_tsx_lock_tls_id); + lck->prev = prev_data; + lck->tsx = tsx; + lck->is_alive = 1; + pj_thread_local_set(pjsip_tsx_lock_tls_id, lck); +} + + +/* + * Unlock transaction. + * This will selectively unlock the mutex ONLY IF the transaction has not been + * destroyed. The function knows whether the transaction has been destroyed + * because when transaction is destroyed the is_alive flag for the transaction + * will be set to zero. + */ +static pj_status_t unlock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck) +{ + pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck); + pj_assert( lck->tsx == tsx ); + pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev); + if (lck->is_alive) + pj_mutex_unlock(tsx->mutex); + + return lck->is_alive ? 0 : -1; +} + +/* + * Set transaction state, and inform TU about the transaction state change. + */ +static void tsx_set_state( pjsip_transaction *tsx, + pjsip_tsx_state_e state, + const pjsip_event *event ) +{ + pjsip_event e; + + PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, ev=%s (src:%s)", + state_str[tsx->state], state_str[state], pjsip_event_str(event->type), + pjsip_event_str(event->src_type))); + + /* Change state. */ + tsx->state = state; + + /* Update the state handlers. */ + if (tsx->role == PJSIP_ROLE_UAC) { + tsx->state_handler = tsx_state_handler_uac[state]; + } else { + tsx->state_handler = tsx_state_handler_uas[state]; + } + + /* Inform TU */ + pj_memcpy(&e, event, sizeof(*event)); + e.type = PJSIP_EVENT_TSX_STATE_CHANGED; + e.obj.tsx = tsx; + pjsip_endpt_send_tsx_event( tsx->endpt, &e ); + + /* When the transaction is terminated, release transport, and free the + * saved last transmitted message. + */ + if (state == PJSIP_TSX_STATE_TERMINATED) { + + /* Decrement transport reference counter. */ + if (tsx->transport && tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { + pjsip_transport_dec_ref( tsx->transport ); + tsx->transport = NULL; + } + /* Free last transmitted message. */ + if (tsx->last_tx) { + pjsip_tx_data_dec_ref( tsx->last_tx ); + tsx->last_tx = NULL; + } + /* Cancel timeout timer. */ + if (tsx->timeout_timer._timer_id != -1) { + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); + tsx->timeout_timer._timer_id = -1; + } + /* Cancel retransmission timer. */ + if (tsx->retransmit_timer._timer_id != -1) { + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); + tsx->retransmit_timer._timer_id = -1; + } + + /* If transport is not pending, reschedule timeout timer to + * destroy this transaction. + */ + if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { + pj_time_val timeout = {0, 0}; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout); + } + + } else if (state == PJSIP_TSX_STATE_DESTROYED) { + + /* Clear TLS, so that mutex will not be unlocked */ + struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id); + while (lck) { + if (lck->tsx == tsx) { + lck->is_alive = 0; + } + lck = lck->prev; + } + } +} + +/* + * Look-up destination address and select which transport to be used to send + * the request message. The procedure used here follows the guidelines on + * sending the request in RFC3261 chapter 8.1.2. + * + * This function also modifies the message (request line and Route headers) + * accordingly. + */ +static pj_status_t tsx_process_route( pjsip_transaction *tsx, + pjsip_tx_data *tdata, + pjsip_host_port *send_addr ) +{ + const pjsip_uri *new_request_uri, *target_uri; + const pjsip_name_addr *topmost_route_uri; + pjsip_route_hdr *first_route_hdr, *last_route_hdr; + + pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); + + /* Get the first "Route" header from the message. If the message doesn't + * have any "Route" headers but the endpoint has, then copy the "Route" + * headers from the endpoint first. + */ + last_route_hdr = first_route_hdr = + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); + if (first_route_hdr) { + topmost_route_uri = &first_route_hdr->name_addr; + while (last_route_hdr->next != (void*)&tdata->msg->hdr) { + pjsip_route_hdr *hdr; + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, last_route_hdr->next); + if (!hdr) + break; + last_route_hdr = hdr; + } + } else { + const pjsip_route_hdr *hdr_list; + hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt); + if (hdr_list->next != hdr_list) { + const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next; + first_route_hdr = NULL; + topmost_route_uri = &hdr->name_addr; + do { + last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr); + if (first_route_hdr == NULL) + first_route_hdr = last_route_hdr; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr); + hdr = hdr->next; + } while (hdr != hdr_list); + } else { + topmost_route_uri = NULL; + } + } + + /* If Route headers exist, and the first element indicates loose-route, + * the URI is taken from the Request-URI, and we keep all existing Route + * headers intact. + * If Route headers exist, and the first element DOESN'T indicate loose + * route, the URI is taken from the first Route header, and remove the + * first Route header from the message. + * Otherwise if there's no Route headers, the URI is taken from the + * Request-URI. + */ + if (topmost_route_uri) { + pj_bool_t has_lr_param; + + if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) || + PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri)) + { + const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri); + has_lr_param = url->lr_param; + } else { + has_lr_param = 0; + } + + if (has_lr_param) { + new_request_uri = tdata->msg->line.req.uri; + /* We shouldn't need to delete topmost Route if it has lr param. + * But seems like it breaks some proxy implementation, so we + * delete it anyway. + */ + /* + pj_list_erase(first_route_hdr); + if (first_route_hdr == last_route_hdr) + last_route_hdr = NULL; + */ + } else { + new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri); + pj_list_erase(first_route_hdr); + if (first_route_hdr == last_route_hdr) + last_route_hdr = NULL; + } + + target_uri = (pjsip_uri*)topmost_route_uri; + + } else { + target_uri = new_request_uri = tdata->msg->line.req.uri; + } + + /* The target URI must be a SIP/SIPS URL so we can resolve it's address. + * Otherwise we're in trouble (i.e. there's no host part in tel: URL). + */ + pj_memset(send_addr, 0, sizeof(*send_addr)); + + if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) { + pjsip_uri *uri = (pjsip_uri*) target_uri; + const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); + send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE); + pj_strdup(tdata->pool, &send_addr->host, &url->host); + send_addr->port = url->port; + send_addr->type = pjsip_transport_get_type_from_name(&url->transport_param); + + } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) { + pjsip_uri *uri = (pjsip_uri*) target_uri; + const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); + pj_strdup(tdata->pool, &send_addr->host, &url->host); + send_addr->port = url->port; + send_addr->type = pjsip_transport_get_type_from_name(&url->transport_param); +#if PJ_HAS_TCP + if (send_addr->type == PJSIP_TRANSPORT_TCP || + send_addr->type == PJSIP_TRANSPORT_SCTP) + { + send_addr->flag |= PJSIP_TRANSPORT_RELIABLE; + } +#endif + } else { + PJ_LOG(2, (tsx->obj_name, "Unable to lookup destination address for " + "non SIP-URL")); + return -1; + } + + /* If target URI is different than request URI, replace + * request URI add put the original URI in the last Route header. + */ + if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) { + pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool); + route->name_addr.uri = tdata->msg->line.req.uri; + if (last_route_hdr) + pj_list_insert_after(last_route_hdr, route); + else + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route); + tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri; + } + + /* Success. */ + return 0; +} + + +/* + * Callback from the transport job. + * This callback is called when asychronous transport connect() operation + * has completed, with or without error. + */ +static void tsx_transport_callback(pjsip_transport_t *tr, + void *token, + pj_status_t status) +{ + char addr[PJ_MAX_HOSTNAME]; + pjsip_transaction *tsx = token; + struct tsx_lock_data lck; + + pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen); + addr[tsx->dest_name.host.slen] = '\0'; + + + if (status == PJ_SUCCESS) { + PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d", + pjsip_transport_get_type_name(tr), + addr, tsx->dest_name.port)); + } else { + PJ_LOG(3, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d", + pjsip_transport_get_type_name(tr), + addr, tsx->dest_name.port, status)); + } + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + if (status != PJ_SUCCESS) { + pjsip_event event; + + event.type = PJSIP_EVENT_TRANSPORT_ERROR; + event.src_type = PJSIP_EVENT_TX_MSG; + event.src.tdata = tsx->last_tx; + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); + + /* Unlock transaction. */ + unlock_tsx(tsx, &lck); + return; + } + + /* See if transaction has already been terminated. If so, schedule to destroy + * the transaction. + */ + if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { + pj_time_val timeout = {0, 0}; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, + &timeout); + + /* Unlock transaction. */ + unlock_tsx(tsx, &lck); + return; + } + + /* Add reference counter to the transport. */ + pjsip_transport_add_ref(tr); + + /* Mark transport as ready. */ + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->transport = tr; + + /* If there's a pending message to send, send it now. */ + if (tsx->has_unsent_msg) { + tsx_send_msg( tsx, tsx->last_tx ); + } + + /* Unlock transaction. */ + unlock_tsx(tsx, &lck); +} + +/* + * Callback from the resolver job. + */ +static void tsx_resolver_callback(pj_status_t status, + void *token, + const struct pjsip_server_addresses *addr) +{ + pjsip_transaction *tsx = token; + struct tsx_lock_data lck; + + PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status)); + + if (status != PJ_SUCCESS || addr->count == 0) { + pjsip_event event; + + event.type = PJSIP_EVENT_TRANSPORT_ERROR; + event.src_type = PJSIP_EVENT_TX_MSG; + event.src.tdata = tsx->last_tx; + + lock_tsx(tsx, &lck); + tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR; + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); + unlock_tsx(tsx, &lck); + return; + } + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + /* Copy server addresses. */ + pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr)); + + /* Create/find the transport for the remote address. */ + PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d", + pj_sockaddr_get_str_addr(&addr->entry[0].addr), + pj_sockaddr_get_port(&addr->entry[0].addr))); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING; + pjsip_endpt_get_transport(tsx->endpt, tsx->pool, + addr->entry[0].type, &addr->entry[0].addr, + tsx, + &tsx_transport_callback); + + /* Unlock transaction */ + unlock_tsx(tsx, &lck); + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the callback for the + * transport is called. + */ +} + +/* + * Initialize the transaction as UAC transaction. + */ +PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + pjsip_msg *msg; + pjsip_cseq_hdr *cseq; + pjsip_via_hdr *via; + struct tsx_lock_data lck; + const pjsip_hdr *endpt_hdr; + + PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata)); + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + /* Keep shortcut */ + msg = tdata->msg; + + /* Role is UAC. */ + tsx->role = PJSIP_ROLE_UAC; + + /* Save method. */ + pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); + + /* Generate branch parameter if it doesn't exist. */ + via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); + if (via == NULL) { + via = pjsip_via_hdr_create(tdata->pool); + pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via); + } + + if (via->branch_param.slen == 0) { + pj_str_t tmp; + via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN); + via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; + pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, + PJSIP_RFC3261_BRANCH_LEN); + + tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN; + pj_generate_unique_string( &tmp ); + } + + /* Copy branch parameter. */ + tsx->branch = via->branch_param; + + /* Add additional request headers from endpoint. */ + endpt_hdr = pjsip_endpt_get_request_headers(tsx->endpt)->next; + while (endpt_hdr != pjsip_endpt_get_request_headers(tsx->endpt)) { + pjsip_hdr *hdr = pjsip_hdr_shallow_clone(tdata->pool, endpt_hdr); + pjsip_msg_add_hdr( tdata->msg, hdr ); + endpt_hdr = endpt_hdr->next; + } + + /* Generate transaction key. */ + create_tsx_key_3261( tsx->pool, &tsx->transaction_key, + PJSIP_ROLE_UAC, &tsx->method, + &via->branch_param); + + PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, + tsx->transaction_key.ptr)); + + /* Save CSeq. */ + cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL); + if (!cseq) { + PJ_LOG(4,(tsx->obj_name, "CSeq header not present in outgoing message!")); + return -1; + } + tsx->cseq = cseq->cseq; + + + /* Begin with State_Null. + * Manually set-up the state becase we don't want to call the callback. + */ + tsx->state = PJSIP_TSX_STATE_NULL; + tsx->state_handler = pjsip_tsx_on_state_null; + + /* Get destination name from the message. */ + if (tsx_process_route(tsx, tdata, &tsx->dest_name) != 0) { + pjsip_event event; + PJ_LOG(3,(tsx->obj_name, "Error: unable to get destination address for request")); + + event.type = PJSIP_EVENT_TRANSPORT_ERROR; + event.src_type = PJSIP_EVENT_TX_MSG; + event.src.tdata = tsx->last_tx; + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); + unlock_tsx(tsx, &lck); + return -1; + } + + /* Resolve destination. + * This will start asynchronous resolver job, and when it finishes, + * the callback will be called. + */ + PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", + tsx->dest_name.host.slen, + tsx->dest_name.host.ptr, + tsx->dest_name.port)); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, + tsx, &tsx_resolver_callback); + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the resolver callback is + * called. + */ + + /* Unlock transaction and return. + * If transaction has been destroyed WITHIN the current thread, the + * unlock_tsx() function will return -1. + */ + return unlock_tsx(tsx, &lck); +} + + +/* + * Initialize the transaction as UAS transaction. + */ +PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + pjsip_msg *msg = rdata->msg; + pj_str_t *branch; + pjsip_cseq_hdr *cseq; + struct tsx_lock_data lck; + + PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata)); + + /* Lock transaction. */ + lock_tsx(tsx, &lck); + + /* Keep shortcut to message */ + msg = rdata->msg; + + /* Role is UAS */ + tsx->role = PJSIP_ROLE_UAS; + + /* Save method. */ + pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); + + /* Get transaction key either from branch for RFC3261 message, or + * create transaction key. + */ + pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, PJSIP_ROLE_UAS, + &tsx->method, rdata); + + /* Duplicate branch parameter for transaction. */ + branch = &rdata->via->branch_param; + pj_strdup(tsx->pool, &tsx->branch, branch); + + PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, + tsx->transaction_key.ptr)); + + /* Save CSeq */ + cseq = rdata->cseq; + tsx->cseq = cseq->cseq; + + /* Begin with state NULL + * Manually set-up the state becase we don't want to call the callback. + */ + tsx->state = PJSIP_TSX_STATE_NULL; + tsx->state_handler = &pjsip_tsx_on_state_null; + + /* Get the transport to send the response. + * According to section 18.2.2 of RFC3261, if the transport is reliable + * then the response must be sent using that transport. + */ + /* In addition, RFC 3581 says, if Via has "rport" parameter specified, + * then return the response using the same transport. + */ + if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->transport) || + rdata->via->rport_param >= 0) + { + tsx->transport = rdata->transport; + pjsip_transport_add_ref(tsx->transport); + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + + tsx->current_addr = 0; + tsx->remote_addr.count = 1; + tsx->remote_addr.entry[0].type = pjsip_transport_get_type(tsx->transport); + pj_memcpy(&tsx->remote_addr.entry[0].addr, + &rdata->addr, rdata->addr_len); + + } else { + pj_status_t status; + + status = pjsip_get_response_addr(tsx->pool, rdata->transport, + rdata->via, &tsx->dest_name); + if (status != PJ_SUCCESS) { + pjsip_event event; + PJ_LOG(2,(tsx->obj_name, "Unable to get destination address " + "for response")); + + event.type = PJSIP_EVENT_TRANSPORT_ERROR; + event.src_type = PJSIP_EVENT_TX_MSG; + event.src.tdata = tsx->last_tx; + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, &event); + unlock_tsx(tsx, &lck); + return status; + } + + /* Resolve destination. + * This will start asynchronous resolver job, and when it finishes, + * the callback will be called. + */ + PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", + tsx->dest_name.host.slen, tsx->dest_name.host.ptr, + tsx->dest_name.port)); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, + tsx, &tsx_resolver_callback); + } + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the resolver callback is + * called. + */ + + /* Unlock transaction and return. + * If transaction has been destroyed WITHIN the current thread, the + * unlock_tsx() function will return -1. + */ + return unlock_tsx(tsx, &lck); +} + +/* + * Callback when timer expires. + */ +static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) +{ + pjsip_event event; + pjsip_transaction *tsx = entry->user_data; + struct tsx_lock_data lck; + + PJ_UNUSED_ARG(theap); + + PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)", + (entry->id == TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout"))); + + event.type = event.src_type = PJSIP_EVENT_TIMER; + event.src.timer = (entry->id == TSX_TIMER_RETRANSMISSION ? + &tsx->retransmit_timer : &tsx->timeout_timer); + + /* Dispatch event to transaction. */ + lock_tsx(tsx, &lck); + (*tsx->state_handler)(tsx, &event); + unlock_tsx(tsx, &lck); +} + +/* + * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for + * non-2xx/INVITE is automatically sent by the transaction. + * This operation is only valid if the transaction is configured to handle ACK + * (tsx->handle_ack is non-zero). If this attribute is not set, then the + * transaction will comply with RFC-3261, i.e. it will set itself to + * TERMINATED state when it receives 2xx/INVITE. + */ +PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata) +{ + pjsip_msg *msg; + pjsip_host_port dest_addr; + pjsip_event event; + pjsip_via_hdr *via; + struct tsx_lock_data lck; + + lock_tsx(tsx, &lck); + + pj_assert(tsx->handle_ack != 0); + + msg = tdata->msg; + + /* Generate branch parameter if it doesn't exist. */ + via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); + if (via == NULL) { + via = pjsip_via_hdr_create(tdata->pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) via); + } + + if (via->branch_param.slen == 0) { + via->branch_param = tsx->branch; + } else { + pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 ); + } + + /* Get destination name from the message. */ + if (tsx_process_route(tsx, tdata, &dest_addr) != 0){ + PJ_LOG(2,(tsx->obj_name, "Unable to get destination address for request")); + goto on_error; + } + + /* Compare message's destination name with transaction's destination name. + * If NOT equal, then we'll have to resolve the destination. + */ + if (dest_addr.type == tsx->dest_name.type && + dest_addr.flag == tsx->dest_name.flag && + dest_addr.port == tsx->dest_name.port && + pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0) + { + /* Equal destination. We can use current transport. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + unlock_tsx(tsx, &lck); + return; + + } + + /* New destination; we'll have to resolve host and create new transport. */ + pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr)); + pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host); + + PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", + tsx->dest_name.host.slen, + tsx->dest_name.host.ptr, + tsx->dest_name.port)); + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_transport_dec_ref(tsx->transport); + tsx->transport = NULL; + + /* Put the message in queue. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + /* This is a bug! + * We shouldn't change transaction's state before actually sending the + * message. Otherwise transaction will terminate before message is sent, + * and timeout timer will be scheduled. + */ + PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK) + + /* + * This will start asynchronous resolver job, and when it finishes, + * the callback will be called. + */ + + tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; + pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, + tsx, &tsx_resolver_callback); + + unlock_tsx(tsx, &lck); + + /* There should be nothing to do after this point. + * Execution for the transaction will resume when the resolver callback is + * called. + */ + return; + +on_error: + /* Failure condition. + * Send TERMINATED event. + */ + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + event.type = PJSIP_EVENT_TRANSPORT_ERROR; + event.src_type = PJSIP_EVENT_TX_MSG; + event.src.tdata = tdata; + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event); + + unlock_tsx(tsx, &lck); +} + + +/* + * This function is called by TU to send a message. + */ +PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx, + pjsip_tx_data *tdata ) +{ + pjsip_event event; + struct tsx_lock_data lck; + + PJ_LOG(5,(tsx->obj_name, "on transmit msg (tdata=%p)", tdata)); + + event.type = event.src_type = PJSIP_EVENT_TX_MSG; + event.src.tdata = tdata; + + PJ_LOG(5,(tsx->obj_name, "on state %s (ev=%s, src=%s, data=%p)", + state_str[tsx->state], pjsip_event_str(event.type), + pjsip_event_str(event.src_type), event.src.data)); + + /* Dispatch to transaction. */ + lock_tsx(tsx, &lck); + (*tsx->state_handler)(tsx, &event); + unlock_tsx(tsx, &lck); +} + +/* + * This function is called by endpoint when incoming message for the + * transaction is received. + */ +PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + pjsip_event event; + struct tsx_lock_data lck; + + event.type = event.src_type = PJSIP_EVENT_RX_MSG; + event.src.rdata = rdata; + + PJ_LOG(5,(tsx->obj_name, "on state %s (ev=%s, src=%s, data=%p)", + state_str[tsx->state], pjsip_event_str(event.type), + pjsip_event_str(event.src_type), event.src.data)); + + /* Dispatch to transaction. */ + lock_tsx(tsx, &lck); + (*tsx->state_handler)(tsx, &event); + unlock_tsx(tsx, &lck); +} + +/* + * Forcely terminate transaction. + */ +PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code ) +{ + pjsip_event event; + struct tsx_lock_data lck; + + lock_tsx(tsx, &lck); + + tsx->status_code = code; + event.type = PJSIP_EVENT_USER; + event.src_type = PJSIP_EVENT_USER; + event.src.data = 0; + event.obj.tsx = tsx; + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event); + + unlock_tsx(tsx, &lck); +} + +/* + * Send message to the transport. + * If transport is not yet available, then do nothing. The message will be + * transmitted when transport connection completion callback is called. + */ +static int tsx_send_msg( pjsip_transaction *tsx, pjsip_tx_data *tdata) +{ + pjsip_event event; + + PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata)); + + if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { + int sent; + pjsip_event before_tx_event; + + pj_assert(tsx->transport != NULL); + + /* Make sure Via transport info is filled up properly for + * requests. + */ + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + const pj_sockaddr_in *addr_name; + pjsip_via_hdr *via = (pjsip_via_hdr*) + pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); + + /* For request message, set "rport" parameter by default. */ + if (tdata->msg->type == PJSIP_REQUEST_MSG) + via->rport_param = 0; + + /* Don't update Via sent-by on retransmission. */ + if (via->sent_by.host.slen == 0) { + addr_name = pjsip_transport_get_addr_name(tsx->transport); + pj_strdup2(tdata->pool, &via->transport, + pjsip_transport_get_type_name(tsx->transport)); + pj_strdup2(tdata->pool, &via->sent_by.host, + pj_sockaddr_get_str_addr(addr_name)); + via->sent_by.port = pj_sockaddr_get_port(addr_name); + } + } + + /* Notify everybody we're about to send message. */ + before_tx_event.type = PJSIP_EVENT_BEFORE_TX; + before_tx_event.src_type = PJSIP_EVENT_TX_MSG; + before_tx_event.obj.tsx = tsx; + before_tx_event.src.tdata = tdata; + before_tx_event.data.long_data = tsx->retransmit_count; + pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event ); + + tsx->has_unsent_msg = 0; + sent = pjsip_transport_send_msg( + tsx->transport, tdata, + &tsx->remote_addr.entry[tsx->current_addr].addr + ); + if (sent < 1) { + goto on_error; + } + } else { + tsx->has_unsent_msg = 1; + } + + return 0; + +on_error: + tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; + event.type = PJSIP_EVENT_TRANSPORT_ERROR; + event.src_type = PJSIP_EVENT_TX_MSG; + event.src.tdata = tdata; + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, &event); + return -1; +} + +/* + * Retransmit last message sent. + */ +static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx, + int should_restart_timer) +{ + PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)", + tsx->last_tx, tsx->retransmit_count, should_restart_timer)); + + pj_assert(tsx->last_tx != NULL); + + ++tsx->retransmit_count; + + if (tsx_send_msg( tsx, tsx->last_tx) != 0) { + return -1; + } + + /* Restart timer T1. */ + if (should_restart_timer) { + pj_time_val timeout; + int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT; + + if (tsx->method.id != PJSIP_INVITE_METHOD && msec_time > PJSIP_T2_TIMEOUT) + msec_time = PJSIP_T2_TIMEOUT; + + timeout.sec = msec_time / 1000; + timeout.msec = msec_time % 1000; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, &timeout); + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in state Null. + */ +static int pjsip_tsx_on_state_null( pjsip_transaction *tsx, pjsip_event *event ) +{ + pj_assert( tsx->state == PJSIP_TSX_STATE_NULL); + pj_assert( tsx->last_tx == NULL ); + pj_assert( tsx->has_unsent_msg == 0); + + if (tsx->role == PJSIP_ROLE_UAS) { + + /* Set state to Trying. */ + pj_assert(event->type == PJSIP_EVENT_RX_MSG); + tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, event); + + } else { + pjsip_tx_data *tdata = event->src.tdata; + + /* Save the message for retransmission. */ + tsx->last_tx = tdata; + pjsip_tx_data_add_ref(tdata); + + /* Send the message. */ + if (tsx_send_msg( tsx, tdata) != 0) { + return -1; + } + + /* Start Timer B (or called timer F for non-INVITE) for transaction + * timeout. + */ + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout_timer_val); + + /* Start Timer A (or timer E) for retransmission only if unreliable + * transport is being used. + */ + if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL && + PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) + { + pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, &t1_timer_val); + tsx->retransmit_count = 0; + } + + /* Move state. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, event); + } + + return PJ_SUCCESS; +} + +/* + * State Calling is for UAC after it sends request but before any responses + * is received. + */ +static int pjsip_tsx_on_state_calling( pjsip_transaction *tsx, + pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING); + pj_assert(tsx->role == PJSIP_ROLE_UAC); + + if (event->type == PJSIP_EVENT_TIMER && + event->src.timer == &tsx->retransmit_timer) + { + + /* Retransmit the request. */ + if (pjsip_tsx_retransmit( tsx, 1 ) != 0) { + return -1; + } + + } else if (event->type == PJSIP_EVENT_TIMER && + event->src.timer == &tsx->timeout_timer) + { + + /* Cancel retransmission timer. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); + } + + /* Set status code */ + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + + /* Inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event ); + + /* Transaction is destroyed */ + return -1; + + } else if (event->type == PJSIP_EVENT_RX_MSG) { + int code; + + /* Cancel retransmission timer A. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); + + /* Cancel timer B (transaction timeout) */ + pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); + + /* Discard retransmission message if it is not INVITE. + * The INVITE tdata is needed in case we have to generate ACK for + * the final response. + */ + /* Keep last_tx for authorization. */ + code = event->src.rdata->msg->line.status.code; + if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) { + pjsip_tx_data_dec_ref(tsx->last_tx); + tsx->last_tx = NULL; + } + + /* Processing is similar to state Proceeding. */ + pjsip_tsx_on_state_proceeding_uac( tsx, event); + + } else { + pj_assert(0); + } + + return PJ_SUCCESS; +} + +/* + * State Trying is for UAS after it received request but before any responses + * is sent. + * Note: this is different than RFC3261, which can use Trying state for + * non-INVITE client transaction (bug in RFC?). + */ +static int pjsip_tsx_on_state_trying( pjsip_transaction *tsx, pjsip_event *event) +{ + int result; + + pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING); + + /* This state is only for UAS */ + pj_assert(tsx->role == PJSIP_ROLE_UAS); + + /* Better be transmission of response message. + * If we've got request retransmission, this means that the TU hasn't + * transmitted any responses within 500 ms, which is not allowed. If + * this happens, just ignore the event (we couldn't retransmit last + * response because we haven't sent any!). + */ + //pj_assert(event->type == PJSIP_EVENT_TX_MSG); + if (event->type != PJSIP_EVENT_TX_MSG) { + return PJ_SUCCESS; + } + + /* The rest of the processing of the event is exactly the same as in + * "Proceeding" state. + */ + result = pjsip_tsx_on_state_proceeding_uas( tsx, event); + + /* Inform the TU of the state transision if state is still State_Trying */ + if (result==0 && tsx->state == PJSIP_TSX_STATE_TRYING) { + tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event); + } + + return result; +} + +/* + * Handler for events in Proceeding for UAS + * This state happens after the TU sends provisional response. + */ +static int pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || + tsx->state == PJSIP_TSX_STATE_TRYING); + + /* This state is only for UAS. */ + pj_assert(tsx->role == PJSIP_ROLE_UAS); + + /* Receive request retransmission. */ + if (event->type == PJSIP_EVENT_RX_MSG) { + + /* Send last response. */ + if (pjsip_tsx_retransmit( tsx, 0 ) != 0) { + return -1; + } + + } else if (event->type == PJSIP_EVENT_TX_MSG ) { + pjsip_tx_data *tdata = event->src.tdata; + + /* The TU sends response message to the request. Save this message so + * that we can retransmit the last response in case we receive request + * retransmission. + */ + pjsip_msg *msg = tdata->msg; + + /* This can only be a response message. */ + pj_assert(msg->type == PJSIP_RESPONSE_MSG); + + /* Status code must be higher than last sent. */ + pj_assert(msg->line.status.code >= tsx->status_code); + + /* Update last status */ + tsx->status_code = msg->line.status.code; + + /* Discard the saved last response (it will be updated later as + * necessary). + */ + if (tsx->last_tx && tsx->last_tx != tdata) { + pjsip_tx_data_dec_ref( tsx->last_tx ); + tsx->last_tx = NULL; + } + + /* Send the message. */ + if (tsx_send_msg(tsx, tdata) != 0) { + return -1; + } + + // Update To tag header for RFC2543 transaction. + // TODO: + + /* Update transaction state */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { + + if (tsx->last_tx != tdata) { + tsx->last_tx = tdata; + pjsip_tx_data_add_ref( tdata ); + } + tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event); + + } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + + if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) { + + /* 2xx class message is not saved, because retransmission is handled + * by the TU. + */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); + + /* Transaction is destroyed. */ + return -1; + + } else { + pj_time_val timeout; + + if (tsx->method.id == PJSIP_INVITE_METHOD) { + tsx->retransmit_count = 0; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, + &t1_timer_val); + } + + /* Save last response sent for retransmission when request + * retransmission is received. + */ + if (tsx->last_tx != tdata) { + tsx->last_tx = tdata; + pjsip_tx_data_add_ref(tdata); + } + + /* Start timer J at 64*T1 for unreliable transport or zero for + * reliable transport. + */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { + timeout = timeout_timer_val; + } else { + timeout.sec = timeout.msec = 0; + } + + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); + + /* Set state to "Completed" */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); + } + + } else if (tsx->status_code >= 300) { + + /* 3xx-6xx class message causes transaction to move to "Completed" state. */ + if (tsx->last_tx != tdata) { + tsx->last_tx = tdata; + pjsip_tx_data_add_ref( tdata ); + } + + /* Start timer H for transaction termination */ + pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,&timeout_timer_val); + + /* For INVITE, if unreliable transport is used, retransmission + * timer G will be scheduled (retransmission). + */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { + pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL); + if (cseq->method.id == PJSIP_INVITE_METHOD) { + tsx->retransmit_count = 0; + pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, + &t1_timer_val); + } + } + + /* Inform TU */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); + + } else { + pj_assert(0); + } + + + } else if (event->type == PJSIP_EVENT_TIMER && + event->src.timer == &tsx->retransmit_timer) { + /* Retransmission timer elapsed. */ + + /* Must have last response to retransmit. */ + pj_assert(tsx->last_tx != NULL); + + /* Retransmit the last response. */ + if (pjsip_tsx_retransmit( tsx, 1 ) != 0) { + return -1; + } + + } else if (event->type == PJSIP_EVENT_TIMER && + event->src.timer == &tsx->timeout_timer) { + + /* Timeout timer. should not happen? */ + pj_assert(0); + + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); + + return -1; + + } else { + pj_assert(0); + } + + return 0; +} + +/* + * Handler for events in Proceeding for UAC + * This state happens after provisional response(s) has been received from + * UAS. + */ +static int pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, pjsip_event *event) +{ + + pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || + tsx->state == PJSIP_TSX_STATE_CALLING); + + if (event->type != PJSIP_EVENT_TIMER) { + /* Must be incoming response, because we should not retransmit + * request once response has been received. + */ + pj_assert(event->type == PJSIP_EVENT_RX_MSG); + if (event->type != PJSIP_EVENT_RX_MSG) { + return 0; + } + + tsx->status_code = event->src.rdata->msg->line.status.code; + } else { + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + } + + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { + + /* Inform the message to TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, event); + + } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { + + /* Stop timeout timer B/F. */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); + + /* For INVITE, the state moves to Terminated state (because ACK is + * handled in TU). For non-INVITE, state moves to Completed. + */ + if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) { + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); + return -1; + + } else { + pj_time_val timeout; + + /* For unreliable transport, start timer D (for INVITE) or + * timer K for non-INVITE. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + timeout = td_timer_val; + } else { + timeout = t4_timer_val; + } + } else { + timeout.sec = timeout.msec = 0; + } + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); + + /* Move state to Completed, inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); + } + + } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { + pj_time_val timeout; + + /* Stop timer B. */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); + + /* Generate and send ACK for INVITE. */ + if (tsx->method.id == PJSIP_INVITE_METHOD) { + pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, event->src.rdata ); + if (tsx_send_msg( tsx, tsx->last_tx) != 0) { + return -1; + } + } + + /* Start Timer D with TD/T4 timer if unreliable transport is used. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + timeout = td_timer_val; + } else { + timeout = t4_timer_val; + } + } else { + timeout.sec = timeout.msec = 0; + } + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); + + /* Inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event); + + } else { + // Shouldn't happen because there's no timer for this state. + pj_assert(0); + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in Completed state for UAS + */ +static int pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); + + if (event->type == PJSIP_EVENT_RX_MSG) { + pjsip_msg *msg = event->src.rdata->msg; + pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL ); + + /* On receive request retransmission, retransmit last response. */ + if (cseq->method.id != PJSIP_ACK_METHOD) { + if (pjsip_tsx_retransmit( tsx, 0 ) != 0) { + return -1; + } + + } else { + /* Process incoming ACK request. */ + + /* Cease retransmission. */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer ); + + /* Start timer I in T4 interval (transaction termination). */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &t4_timer_val); + + /* Move state to "Confirmed" */ + tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, event); + } + + } else if (event->type == PJSIP_EVENT_TIMER) { + + if (event->src.timer == &tsx->retransmit_timer) { + /* Retransmit message. */ + if (pjsip_tsx_retransmit( tsx, 1 ) != 0) { + return -1; + } + + } else { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + + /* For INVITE, this means that ACK was never received. + * Set state to Terminated, and inform TU. + */ + + tsx->status_code = PJSIP_SC_TSX_TIMEOUT; + + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); + + return -1; + + } else { + /* Transaction terminated, it can now be deleted. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); + return -1; + } + } + + } else { + /* Ignore request to transmit. */ + pj_assert(event->src.tdata == tsx->last_tx); + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in Completed state for UAC transaction. + */ +static int pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); + + if (event->type == PJSIP_EVENT_TIMER) { + /* Must be the timeout timer. */ + pj_assert(event->src.timer == &tsx->timeout_timer); + + /* Move to Terminated state. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); + + /* Transaction has been destroyed. */ + return -1; + + } else if (event->type == PJSIP_EVENT_RX_MSG) { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + /* On received of final response retransmission, retransmit the ACK. + * TU doesn't need to be informed. + */ + pjsip_msg *msg = event->src.rdata->msg; + pj_assert(msg->type == PJSIP_RESPONSE_MSG); + if (msg->type==PJSIP_RESPONSE_MSG && + msg->line.status.code >= 200) + { + if (pjsip_tsx_retransmit( tsx, 0 ) != 0) { + return -1; + } + } else { + /* Very late retransmission of privisional response. */ + pj_assert( msg->type == PJSIP_RESPONSE_MSG ); + } + } else { + /* Just drop the response. */ + } + } else if (tsx->method.id == PJSIP_INVITE_METHOD && + event->type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) { + + /* Set last transmitted message. */ + if (tsx->last_tx != event->src.tdata) { + pjsip_tx_data_dec_ref( tsx->last_tx ); + tsx->last_tx = event->src.tdata; + pjsip_tx_data_add_ref( tsx->last_tx ); + } + + /* No state changed, but notify app. + * Must notify now, so app has chance to put SDP in outgoing ACK msg. + */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, event ); + + /* Send msg */ + tsx_send_msg(tsx, event->src.tdata); + + } else { + pj_assert(0); + } + + return 0; +} + +/* + * Handler for events in state Confirmed. + */ +static int pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED); + + /* This state is only for UAS for INVITE. */ + pj_assert(tsx->role == PJSIP_ROLE_UAS); + pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); + + /* Absorb any ACK received. */ + if (event->type == PJSIP_EVENT_RX_MSG) { + /* Must be a request message. */ + pj_assert(event->src.rdata->msg->type == PJSIP_REQUEST_MSG); + + /* Must be an ACK request or a late INVITE retransmission. */ + pj_assert(event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD || + event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD); + + } else if (event->type == PJSIP_EVENT_TIMER) { + /* Must be from timeout_timer_. */ + pj_assert(event->src.timer == &tsx->timeout_timer); + + /* Move to Terminated state. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, event); + + /* Transaction has been destroyed. */ + return -1; + + } else { + pj_assert(0); + } + + return PJ_SUCCESS; +} + +/* + * Handler for events in state Terminated. + */ +static int pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, pjsip_event *event) +{ + pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED); + + PJ_UNUSED_ARG(event) + + /* Destroy this transaction */ + tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, event); + + return PJ_SUCCESS; +} + + +static int pjsip_tsx_on_state_destroyed( pjsip_transaction *tsx, pjsip_event *event) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(event) + return PJ_SUCCESS; +} + diff --git a/pjsip/src/pjsip/sip_transaction.h b/pjsip/src/pjsip/sip_transaction.h index c0bcae14d..10379e6a2 100644 --- a/pjsip/src/pjsip/sip_transaction.h +++ b/pjsip/src/pjsip/sip_transaction.h @@ -1,189 +1,189 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transaction.h 7 10/14/05 12:23a Bennylp $ */ -#ifndef __PJSIP_SIP_TRANSACTION_H__ -#define __PJSIP_SIP_TRANSACTION_H__ - -/** - * @file sip_transaction.h - * @brief SIP Transaction - */ - -#include -#include -//#include -//#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_TRANSACT SIP Transaction - * @ingroup PJSIP - * @{ - */ - -struct pjsip_transaction; - - -/** - * Transaction state. - */ -typedef enum pjsip_tsx_state_e -{ - PJSIP_TSX_STATE_NULL, - PJSIP_TSX_STATE_CALLING, - PJSIP_TSX_STATE_TRYING, - PJSIP_TSX_STATE_PROCEEDING, - PJSIP_TSX_STATE_COMPLETED, - PJSIP_TSX_STATE_CONFIRMED, - PJSIP_TSX_STATE_TERMINATED, - PJSIP_TSX_STATE_DESTROYED, - PJSIP_TSX_STATE_MAX, -} pjsip_tsx_state_e; - - -/** - * State of the transport in the transaction. - * The transport is progressing independently of the transaction. - */ -typedef enum pjsip_tsx_transport_state_e -{ - PJSIP_TSX_TRANSPORT_STATE_NULL, - PJSIP_TSX_TRANSPORT_STATE_RESOLVING, - PJSIP_TSX_TRANSPORT_STATE_CONNECTING, - PJSIP_TSX_TRANSPORT_STATE_FINAL, -} pjsip_tsx_transport_state_e; - - -/** - * Transaction state. - */ -struct pjsip_transaction -{ - pj_pool_t *pool; - pjsip_endpoint *endpt; - char obj_name[PJ_MAX_OBJ_NAME]; - pjsip_role_e role; - int status_code; - pjsip_tsx_state_e state; - int (*state_handler)(struct pjsip_transaction *, pjsip_event *); - - pj_mutex_t *mutex; - pjsip_method method; - int cseq; - pj_str_t transaction_key; - pj_str_t branch; - - pjsip_tsx_transport_state_e transport_state; - pjsip_host_port dest_name; - int current_addr; - pjsip_server_addresses remote_addr; - pjsip_transport_t *transport; - - pjsip_tx_data *last_tx; - int has_unsent_msg; - int handle_ack; - int retransmit_count; - - pj_timer_entry retransmit_timer; - pj_timer_entry timeout_timer; - void *module_data[PJSIP_MAX_MODULE]; -}; - - -/** - * Init transaction as UAC. - * @param tsx the transaction. - * @param tdata the transmit data. - * @return PJ_SUCCESS if successfull. - */ -PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, - pjsip_tx_data *tdata); - -/** - * Init transaction as UAS. - * @param tsx the transaction to be initialized. - * @param rdata the received incoming request. - * @return PJ_SUCCESS if successfull. - */ -PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, - pjsip_rx_data *rdata); - -/** - * Process incoming message for this transaction. - * @param tsx the transaction. - * @param rdata the incoming message. - */ -PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx, - pjsip_rx_data *rdata); - -/** - * Transmit message with this transaction. - * @param tsx the transaction. - * @param tdata the outgoing message. - */ -PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx, - pjsip_tx_data *tdata); - - -/** - * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for - * non-2xx/INVITE is automatically sent by the transaction. - * This operation is only valid if the transaction is configured to handle ACK - * (tsx->handle_ack is non-zero). If this attribute is not set, then the - * transaction will comply with RFC-3261, i.e. it will set itself to - * TERMINATED state when it receives 2xx/INVITE. - * @param tsx The transaction. - * @param tdata The ACK request. - */ -PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, - pjsip_tx_data *tdata); - -/** - * Forcely terminate transaction. - * @param tsx the transaction. - * @param code the status code to report. - */ -PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx, - int code ); - -/** - * Create transaction key, which is used to match incoming requests - * or response (retransmissions) against transactions. - * @param pool The pool - * @param key Output key. - * @param role The role of the transaction. - * @param method The method to be put as a key. - * @param rdata The received data to calculate. - */ -PJ_DECL(void) pjsip_tsx_create_key( pj_pool_t *pool, - pj_str_t *key, - pjsip_role_e role, - const pjsip_method *method, - const pjsip_rx_data *rdata ); - -/** - * @} - */ - -/* - * Internal. - */ - -/* - * Get the string name for the state. - */ -PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state); - -/* - * Get the role name. - */ -PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role); - - -/* Thread Local Storage ID for transaction lock (initialized by endpoint) */ -extern int pjsip_tsx_lock_tls_id; - -PJ_END_DECL - -#endif /* __PJSIP_TRANSACT_H__ */ - +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transaction.h 7 10/14/05 12:23a Bennylp $ */ +#ifndef __PJSIP_SIP_TRANSACTION_H__ +#define __PJSIP_SIP_TRANSACTION_H__ + +/** + * @file sip_transaction.h + * @brief SIP Transaction + */ + +#include +#include +//#include +//#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_TRANSACT SIP Transaction + * @ingroup PJSIP + * @{ + */ + +struct pjsip_transaction; + + +/** + * Transaction state. + */ +typedef enum pjsip_tsx_state_e +{ + PJSIP_TSX_STATE_NULL, + PJSIP_TSX_STATE_CALLING, + PJSIP_TSX_STATE_TRYING, + PJSIP_TSX_STATE_PROCEEDING, + PJSIP_TSX_STATE_COMPLETED, + PJSIP_TSX_STATE_CONFIRMED, + PJSIP_TSX_STATE_TERMINATED, + PJSIP_TSX_STATE_DESTROYED, + PJSIP_TSX_STATE_MAX, +} pjsip_tsx_state_e; + + +/** + * State of the transport in the transaction. + * The transport is progressing independently of the transaction. + */ +typedef enum pjsip_tsx_transport_state_e +{ + PJSIP_TSX_TRANSPORT_STATE_NULL, + PJSIP_TSX_TRANSPORT_STATE_RESOLVING, + PJSIP_TSX_TRANSPORT_STATE_CONNECTING, + PJSIP_TSX_TRANSPORT_STATE_FINAL, +} pjsip_tsx_transport_state_e; + + +/** + * Transaction state. + */ +struct pjsip_transaction +{ + pj_pool_t *pool; + pjsip_endpoint *endpt; + char obj_name[PJ_MAX_OBJ_NAME]; + pjsip_role_e role; + int status_code; + pjsip_tsx_state_e state; + int (*state_handler)(struct pjsip_transaction *, pjsip_event *); + + pj_mutex_t *mutex; + pjsip_method method; + int cseq; + pj_str_t transaction_key; + pj_str_t branch; + + pjsip_tsx_transport_state_e transport_state; + pjsip_host_port dest_name; + int current_addr; + pjsip_server_addresses remote_addr; + pjsip_transport_t *transport; + + pjsip_tx_data *last_tx; + int has_unsent_msg; + int handle_ack; + int retransmit_count; + + pj_timer_entry retransmit_timer; + pj_timer_entry timeout_timer; + void *module_data[PJSIP_MAX_MODULE]; +}; + + +/** + * Init transaction as UAC. + * @param tsx the transaction. + * @param tdata the transmit data. + * @return PJ_SUCCESS if successfull. + */ +PJ_DECL(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, + pjsip_tx_data *tdata); + +/** + * Init transaction as UAS. + * @param tsx the transaction to be initialized. + * @param rdata the received incoming request. + * @return PJ_SUCCESS if successfull. + */ +PJ_DECL(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, + pjsip_rx_data *rdata); + +/** + * Process incoming message for this transaction. + * @param tsx the transaction. + * @param rdata the incoming message. + */ +PJ_DECL(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx, + pjsip_rx_data *rdata); + +/** + * Transmit message with this transaction. + * @param tsx the transaction. + * @param tdata the outgoing message. + */ +PJ_DECL(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx, + pjsip_tx_data *tdata); + + +/** + * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for + * non-2xx/INVITE is automatically sent by the transaction. + * This operation is only valid if the transaction is configured to handle ACK + * (tsx->handle_ack is non-zero). If this attribute is not set, then the + * transaction will comply with RFC-3261, i.e. it will set itself to + * TERMINATED state when it receives 2xx/INVITE. + * @param tsx The transaction. + * @param tdata The ACK request. + */ +PJ_DECL(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, + pjsip_tx_data *tdata); + +/** + * Forcely terminate transaction. + * @param tsx the transaction. + * @param code the status code to report. + */ +PJ_DECL(void) pjsip_tsx_terminate( pjsip_transaction *tsx, + int code ); + +/** + * Create transaction key, which is used to match incoming requests + * or response (retransmissions) against transactions. + * @param pool The pool + * @param key Output key. + * @param role The role of the transaction. + * @param method The method to be put as a key. + * @param rdata The received data to calculate. + */ +PJ_DECL(void) pjsip_tsx_create_key( pj_pool_t *pool, + pj_str_t *key, + pjsip_role_e role, + const pjsip_method *method, + const pjsip_rx_data *rdata ); + +/** + * @} + */ + +/* + * Internal. + */ + +/* + * Get the string name for the state. + */ +PJ_DECL(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state); + +/* + * Get the role name. + */ +PJ_DECL(const char *) pjsip_role_name(pjsip_role_e role); + + +/* Thread Local Storage ID for transaction lock (initialized by endpoint) */ +extern int pjsip_tsx_lock_tls_id; + +PJ_END_DECL + +#endif /* __PJSIP_TRANSACT_H__ */ + diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 0e29660b8..7b18aba05 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1,1608 +1,1608 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transport.c 18 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MGR_IDLE_CHECK_INTERVAL 30 -#define MGR_HASH_TABLE_SIZE PJSIP_MAX_DIALOG_COUNT -#define BACKLOG 5 -#define DEFAULT_SO_SNDBUF (8 * 1024 * 1024) -#define DEFAULT_SO_RCVBUF (8 * 1024 * 1024) - -#define LOG_TRANSPORT_MGR "trmgr" -#define THIS_FILE "sip_transport" - -static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr ); - - -/** - * New TCP socket for accept. - */ -typedef struct incoming_socket_rec -{ - pj_sock_t sock; - pj_sockaddr_in remote; - pj_sockaddr_in local; - int addrlen; -} incoming_socket_rec; - -/** - * SIP Transport. - */ -struct pjsip_transport_t -{ - /** Standard list members, for chaining the transport in the - * listener list. - */ - PJ_DECL_LIST_MEMBER(struct pjsip_transport_t) - - /** Transport's pool. */ - pj_pool_t *pool; - - /** Mutex */ - pj_mutex_t *tr_mutex; - - /** Transport name for logging purpose */ - char obj_name[PJ_MAX_OBJ_NAME]; - - /** Socket handle */ - pj_sock_t sock; - - /** Transport type. */ - pjsip_transport_type_e type; - - /** Flags to keep various states (see pjsip_transport_flags_e). */ - pj_uint32_t flag; - - /** I/O Queue key */ - pj_ioqueue_key_t *key; - - /** Receive data buffer */ - pjsip_rx_data *rdata; - - /** Pointer to transport manager */ - pjsip_transport_mgr *mgr; - - /** Reference counter, to prevent this transport from being closed while - * it's being used. - */ - pj_atomic_t *ref_cnt; - - /** Local address. */ - pj_sockaddr_in local_addr; - - /** Address name (what to put in Via address field). */ - pj_sockaddr_in addr_name; - - /** Remote address (can be zero for UDP and for listeners). UDP listener - * bound to local loopback interface (127.0.0.1) has remote address set - * to 127.0.0.1 to prevent client from using it to send to remote hosts, - * because remote host then will receive 127.0.0.1 as the packet's - * source address. - */ - pj_sockaddr_in remote_addr; - - /** Struct to save incoming socket information. */ - incoming_socket_rec accept_data; - - /** When this transport should be closed. */ - pj_time_val close_time; - - /** List of callbacks to be called when client attempt to use this - * transport while it's not connected (i.e. still connecting). - */ - pj_list cb_list; -}; - - -/* - * Transport manager. - */ -struct pjsip_transport_mgr -{ - pj_hash_table_t *transport_table; - pj_mutex_t *mutex; - pjsip_endpoint *endpt; - pj_ioqueue_t *ioqueue; - pj_time_val next_idle_check; - void (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata); -}; - -/* - * Transport role. - */ -typedef enum transport_role_e -{ - TRANSPORT_ROLE_LISTENER, - TRANSPORT_ROLE_TRANSPORT, -} transport_role_e; - -/* - * Transport key for indexing in the hash table. - * WATCH OUT FOR ALIGNMENT PROBLEM HERE! - */ -typedef struct transport_key -{ - pj_uint8_t type; - pj_uint8_t zero; - pj_uint16_t port; - pj_uint32_t addr; -} transport_key; - -/* - * Transport callback. - */ -struct transport_callback -{ - PJ_DECL_LIST_MEMBER(struct transport_callback) - - /** User defined token to be passed to the callback. */ - void *token; - - /** The callback function. */ - void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status); - -}; - -/* - * Transport names. - */ -const struct -{ - pjsip_transport_type_e type; - pj_uint16_t port; - pj_str_t name; -} transport_names[] = -{ - { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}}, - { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}}, -#if PJ_HAS_TCP - { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}}, - { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}}, - { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}} -#endif -}; - -static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read); -static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent); -static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status); -static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status); - -static pj_ioqueue_callback ioqueue_transport_callback = -{ - &on_ioqueue_read, - &on_ioqueue_write, - &on_ioqueue_accept, - &on_ioqueue_connect -}; - -static void init_key_from_transport(transport_key *key, - const pjsip_transport_t *tr) -{ - /* This is to detect alignment problems. */ - pj_assert(sizeof(transport_key) == 8); - - key->type = (pj_uint8_t)tr->type; - key->zero = 0; - key->addr = pj_sockaddr_get_addr(&tr->remote_addr); - key->port = pj_sockaddr_get_port(&tr->remote_addr); - /* - if (key->port == 0) { - key->port = pj_sockaddr_get_port(&tr->local_addr); - } - */ -} - -#if PJ_HAS_TCP -static void init_tcp_key(transport_key *key, pjsip_transport_type_e type, - const pj_sockaddr_in *addr) -{ - /* This is to detect alignment problems. */ - pj_assert(sizeof(transport_key) == 8); - - key->type = (pj_uint8_t)type; - key->zero = 0; - key->addr = pj_sockaddr_get_addr(addr); - key->port = pj_sockaddr_get_port(addr); -} -#endif - -static void init_udp_key(transport_key *key, pjsip_transport_type_e type, - const pj_sockaddr_in *addr) -{ - PJ_UNUSED_ARG(addr) - - /* This is to detect alignment problems. */ - pj_assert(sizeof(transport_key) == 8); - - pj_memset(key, 0, sizeof(*key)); - key->type = (pj_uint8_t)type; - -#if 0 /* Not sure why we need to make 127.0.0.1 a special case */ - if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) { - /* This looks more complicated than it is because key->addr uses - * the host version of the address (i.e. converted with ntohl()). - */ - pj_str_t localaddr = pj_str("127.0.0.1"); - pj_sockaddr_in addr; - pj_sockaddr_set_str_addr(&addr, &localaddr); - key->addr = pj_sockaddr_get_addr(&addr); - } -#endif -} - -/* - * Get type format name (for pool name). - */ -static const char *transport_get_name_format( int type ) -{ - switch (type) { - case PJSIP_TRANSPORT_UDP: - return " udp%p"; -#if PJ_HAS_TCP - case PJSIP_TRANSPORT_TCP: - return " tcp%p"; - case PJSIP_TRANSPORT_TLS: - return " tls%p"; - case PJSIP_TRANSPORT_SCTP: - return "sctp%p"; -#endif - } - pj_assert(0); - return 0; -} - -/* - * Get the default SIP port number for the specified type. - */ -PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type) -{ - return transport_names[type].port; -} - -/* - * Get transport name. - */ -static const char *get_type_name(int type) -{ - return transport_names[type].name.ptr; -} - -/* - * Get transport type from name. - */ -PJ_DEF(pjsip_transport_type_e) -pjsip_transport_get_type_from_name(const pj_str_t *name) -{ - unsigned i; - - for (i=0; iendpt, "ptdt%p", - PJSIP_POOL_LEN_TDATA, - PJSIP_POOL_INC_TDATA ); - if (!pool) { - return NULL; - } - tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data)); - tdata->pool = pool; - tdata->mgr = mgr; - sprintf(tdata->obj_name,"txd%p", tdata); - - tdata->ref_cnt = pj_atomic_create(tdata->pool, 0); - if (!tdata->ref_cnt) { - pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool ); - return NULL; - } - - return tdata; -} - -/* - * Add reference to tx buffer. - */ -PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ) -{ - pj_atomic_inc(tdata->ref_cnt); -} - -/* - * Decrease transport data reference, destroy it when the reference count - * reaches zero. - */ -PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) -{ - pj_assert( pj_atomic_get(tdata->ref_cnt) > 0); - if (pj_atomic_dec(tdata->ref_cnt) <= 0) { - PJ_LOG(6,(tdata->obj_name, "destroying txdata")); - pj_atomic_destroy( tdata->ref_cnt ); - pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool ); - } -} - -/* - * Invalidate the content of the print buffer to force the message to be - * re-printed when sent. - */ -PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ) -{ - tdata->buf.cur = tdata->buf.start; -} - -/* - * Get the transport type. - */ -PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr) -{ - return tr->type; -} - -/* - * Get transport type from transport flag. - */ -PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag) -{ -#if PJ_HAS_TCP - if (flag & PJSIP_TRANSPORT_SECURE) { - return PJSIP_TRANSPORT_TLS; - } else if (flag & PJSIP_TRANSPORT_RELIABLE) { - return PJSIP_TRANSPORT_TCP; - } else -#else - PJ_UNUSED_ARG(flag) -#endif - { - return PJSIP_TRANSPORT_UDP; - } -} - -/* - * Get the transport type name. - */ -PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr) -{ - return get_type_name(tr->type); -} - -/* - * Get the transport's object name. - */ -PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr ) -{ - return tr->obj_name; -} - -/* - * Get the transport's reference counter. - */ -PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr ) -{ - return pj_atomic_get(tr->ref_cnt); -} - -/* - * Get transport local address. - */ -PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr ) -{ - return &tr->local_addr; -} - -/* - * Get address name. - */ -PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr) -{ - return &tr->addr_name; -} - -/* - * Get transport remote address. - */ -PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr ) -{ - return &tr->remote_addr; -} - -/* - * Get transport flag. - */ -PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr ) -{ - return tr->flag; -} - -/* - * Add reference to the specified transport. - */ -PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr ) -{ - pj_atomic_inc(tr->ref_cnt); -} - -/* - * Decrease the reference time of the transport. - */ -PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr ) -{ - pj_assert(tr->ref_cnt > 0); - if (pj_atomic_dec(tr->ref_cnt) == 0) { - pj_gettimeofday(&tr->close_time); - tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT; - } -} - -/* - * Open the underlying transport. - */ -static pj_sock_t create_socket( pjsip_transport_type_e type, - pj_sockaddr_in *local ) -{ - int sock_family; - int sock_type; - int sock_proto; - int len; - pj_sock_t sock; - - /* Set socket parameters */ - if (type == PJSIP_TRANSPORT_UDP) { - sock_family = PJ_AF_INET; - sock_type = PJ_SOCK_DGRAM; - sock_proto = 0; - -#if PJ_HAS_TCP - } else if (type == PJSIP_TRANSPORT_TCP) { - sock_family = PJ_AF_INET; - sock_type = PJ_SOCK_STREAM; - sock_proto = 0; -#endif - } else { - PJ_LOG(2,("", "create_socket: unsupported transport type %s", - get_type_name(type))); - return PJ_INVALID_SOCKET; - } - - /* Create socket. */ - sock = pj_sock_socket( sock_family, sock_type, sock_proto, PJ_SOCK_ASYNC); - if (sock == PJ_INVALID_SOCKET) { - PJ_PERROR((THIS_FILE, "%s socket()", get_type_name(type))); - return PJ_INVALID_SOCKET; - } - - /* Bind the socket to the requested address, or if no address is - * specified, let the operating system chooses the address. - */ - if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) { - /* Bind to the requested address. */ - if (pj_sock_bind(sock, local, sizeof(*local)) != 0) { - PJ_PERROR((THIS_FILE, "bind() to %s %s:%d", - get_type_name(type), - pj_sockaddr_get_str_addr(local), - pj_sockaddr_get_port(local))); - pj_sock_close(sock); - return PJ_INVALID_SOCKET; - } - } else if (type == PJSIP_TRANSPORT_UDP) { - /* Only for UDP sockets: bind to any address so that the operating - * system allocates the port for us. For TCP, let the OS implicitly - * bind the socket with connect() syscall (if we bind now, then we'll - * get 0.0.0.0 as local address). - */ - pj_memset(local, 0, sizeof(*local)); - local->sin_family = PJ_AF_INET; - if (pj_sock_bind(sock, local, sizeof(*local)) != 0) { - PJ_PERROR((THIS_FILE, "bind() to %s 0.0.0.0:0", get_type_name(type))); - pj_sock_close(sock); - return PJ_INVALID_SOCKET; - } - - /* Get the local address. */ - len = sizeof(pj_sockaddr_in); - if (pj_sock_getsockname(sock, local, &len)) { - PJ_PERROR((THIS_FILE, "getsockname()")); - pj_sock_close(sock); - return -1; - } - } - - return sock; -} - -/* - * Close the transport. - */ -static void destroy_socket( pjsip_transport_t * tr) -{ - pj_assert( pj_atomic_get(tr->ref_cnt) == 0); - pj_sock_close(tr->sock); - tr->sock = -1; -} - -/* - * Create a new transport object. - */ -static pjsip_transport_t* create_transport( pjsip_transport_mgr *mgr, - pjsip_transport_type_e type, - pj_sock_t sock_hnd, - const pj_sockaddr_in *local_addr, - const pj_sockaddr_in *addr_name) -{ - pj_pool_t *tr_pool=NULL, *rdata_pool=NULL; - pjsip_transport_t *tr = NULL; - - /* Allocate pool for transport from endpoint. */ - tr_pool = pjsip_endpt_create_pool( mgr->endpt, - transport_get_name_format(type), - PJSIP_POOL_LEN_TRANSPORT, - PJSIP_POOL_INC_TRANSPORT ); - if (!tr_pool) { - goto on_error; - } - - /* Allocate pool for rdata from endpoint. */ - rdata_pool = pjsip_endpt_create_pool( mgr->endpt, - "prdt%p", - PJSIP_POOL_LEN_RDATA, - PJSIP_POOL_INC_RDATA ); - if (!rdata_pool) { - goto on_error; - } - - /* Allocate and initialize the transport. */ - tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr)); - tr->pool = tr_pool; - tr->type = type; - tr->mgr = mgr; - tr->sock = sock_hnd; - pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in)); - pj_list_init(&tr->cb_list); - sprintf(tr->obj_name, transport_get_name_format(type), tr); - - if (type != PJSIP_TRANSPORT_UDP) { - tr->flag |= PJSIP_TRANSPORT_RELIABLE; - } - - /* Address name. */ - if (addr_name == NULL) { - addr_name = &tr->local_addr; - } - pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name)); - - /* Create atomic */ - tr->ref_cnt = pj_atomic_create(tr_pool, 0); - if (!tr->ref_cnt) { - goto on_error; - } - - /* Init rdata in the transport. */ - tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata)); - tr->rdata->pool = rdata_pool; - tr->rdata->len = 0; - tr->rdata->transport = tr; - - /* Init transport mutex. */ - tr->tr_mutex = pj_mutex_create(tr_pool, "mtr%p", 0); - if (!tr->tr_mutex) { - PJ_PERROR((tr->obj_name, "pj_mutex_create()")); - goto on_error; - } - - /* Register to I/O Queue */ - tr->key = pj_ioqueue_register(tr_pool, mgr->ioqueue, - (pj_oshandle_t)tr->sock, tr, - &ioqueue_transport_callback); - if (tr->key == NULL) { - PJ_PERROR((tr->obj_name, "pj_ioqueue_register()")); - goto on_error; - } - - return tr; - -on_error: - if (tr && tr->tr_mutex) { - pj_mutex_destroy(tr->tr_mutex); - } - if (tr_pool) { - pjsip_endpt_destroy_pool(mgr->endpt, tr_pool); - } - if (rdata_pool) { - pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool); - } - return NULL; -} - -/* - * Destroy transport. - */ -static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr ) -{ - transport_key hash_key; - - /* Remove from I/O queue. */ - pj_ioqueue_unregister( mgr->ioqueue, tr->key ); - - /* Remove from hash table */ - init_key_from_transport(&hash_key, tr); - pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL); - - /* Close transport. */ - destroy_socket(tr); - - /* Destroy the transport mutex. */ - pj_mutex_destroy(tr->tr_mutex); - - /* Destroy atomic */ - pj_atomic_destroy( tr->ref_cnt ); - - /* Release the pool associated with the rdata. */ - pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool ); - - /* Release the pool associated with the transport. */ - pjsip_endpt_destroy_pool(mgr->endpt, tr->pool ); -} - - -static int transport_send_msg( pjsip_transport_t *tr, pjsip_tx_data *tdata, - const pj_sockaddr_in *addr) -{ - const char *buf = tdata->buf.start; - int sent; - int len; - - /* Allocate buffer if necessary. */ - if (tdata->buf.start == NULL) { - tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN); - tdata->buf.cur = tdata->buf.start; - tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN; - } - - /* Print the message if it's not printed */ - if (tdata->buf.cur <= tdata->buf.start) { - len = pjsip_msg_print(tdata->msg, tdata->buf.start, - tdata->buf.end - tdata->buf.start); - if (len < 1) { - return len; - } - tdata->buf.cur += len; - tdata->buf.cur[len] = '\0'; - } - - /* BUG BUG BUG */ - /* MUST CHECK THAT THE SOCKET IS READY TO SEND (IOQueue)! */ - PJ_TODO(BUG_BUG_BUG___SENDING_DATAGRAM_WHILE_SOCKET_IS_PENDING__) - - /* Send the message. */ - buf = tdata->buf.start; - len = tdata->buf.cur - tdata->buf.start; - - if (tr->type == PJSIP_TRANSPORT_UDP) { - PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n" - "----------- begin msg ------------\n" - "%s" - "------------ end msg -------------", - pj_sockaddr_get_str_addr(addr), - pj_sockaddr_get_port(addr), - len, buf)); - - sent = pj_ioqueue_sendto( tr->mgr->ioqueue, tr->key, - buf, len, addr, sizeof(*addr)); - } -#if PJ_HAS_TCP - else { - PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n" - "----------- begin msg ------------\n" - "%s" - "------------ end msg -------------", - len, buf)); - - sent = pj_ioqueue_write (tr->mgr->ioqueue, tr->key, buf, len); - } -#else - else { - pj_assert(0); - sent = -1; - } -#endif - - if (sent == len || sent == PJ_IOQUEUE_PENDING) { - return len; - } - - /* On error, clear the flag. */ - PJ_PERROR((tr->obj_name, tr->type == PJSIP_TRANSPORT_UDP ? "pj_ioqueue_sendto()" : "pj_ioqueue_write()")); - return -1; -} - -/* - * Send a SIP message using the specified transport, to the address specified - * in the outgoing data. - */ -PJ_DEF(int) pjsip_transport_send_msg( pjsip_transport_t *tr, - pjsip_tx_data *tdata, - const pj_sockaddr_in *addr) -{ - int sent; - - PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name)); - - sent = transport_send_msg(tr, tdata, addr ); - return sent; -} - -/////////////////////////////////////////////////////////////////////////////// - -/* - * Create a new transport manager. - */ -PJ_DEF(pjsip_transport_mgr*) -pjsip_transport_mgr_create( pj_pool_t *pool, - pjsip_endpoint * endpt, - void (*cb)(pjsip_endpoint*,pjsip_rx_data *) ) -{ - pjsip_transport_mgr *mgr; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()")); - - mgr = pj_pool_alloc(pool, sizeof(*mgr)); - mgr->endpt = endpt; - mgr->message_callback = cb; - - mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE); - if (!mgr->transport_table) { - PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating transport manager hash table")); - return NULL; - } - mgr->ioqueue = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS); - if (!mgr->ioqueue) { - PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating IO queue")); - return NULL; - } - mgr->mutex = pj_mutex_create(pool, "tmgr%p", 0); - if (!mgr->mutex) { - PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating mutex")); - pj_ioqueue_destroy(mgr->ioqueue); - return NULL; - } - pj_gettimeofday(&mgr->next_idle_check); - mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL; - return mgr; -} - -/* - * Destroy transport manager. - */ -PJ_DEF(void) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr ) -{ - pj_hash_iterator_t itr_val; - pj_hash_iterator_t *itr; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()")); - - pj_mutex_lock(mgr->mutex); - - itr = pjsip_transport_first(mgr, &itr_val); - while (itr != NULL) { - pj_hash_iterator_t *next; - pjsip_transport_t *transport; - - transport = pjsip_transport_this(mgr, itr); - - next = pjsip_transport_next(mgr, itr); - - pj_atomic_set(transport->ref_cnt, 0); - destroy_transport( mgr, transport); - - itr = next; - } - pj_ioqueue_destroy(mgr->ioqueue); - - pj_mutex_unlock(mgr->mutex); -} - -/* - * Create listener - */ -static pj_status_t create_listener( pjsip_transport_mgr *mgr, - pjsip_transport_type_e type, - pj_sock_t sock_hnd, - pj_sockaddr_in *local_addr, - const pj_sockaddr_in *addr_name) -{ - pjsip_transport_t *tr; - struct transport_key *hash_key; - int opt_val; - - opt_val = DEFAULT_SO_SNDBUF; - if (pj_sock_setsockopt( sock_hnd, SOL_SOCKET, SO_SNDBUF, &opt_val, sizeof(opt_val)) != PJ_SUCCESS) { - PJ_LOG(3, (LOG_TRANSPORT_MGR, "create listener: error setting SNDBUF to %d", DEFAULT_SO_SNDBUF)); - // Just ignore the error. - } - - opt_val = DEFAULT_SO_RCVBUF; - if (pj_sock_setsockopt( sock_hnd, SOL_SOCKET, SO_RCVBUF, &opt_val, sizeof(opt_val)) != PJ_SUCCESS) { - PJ_LOG(3, (LOG_TRANSPORT_MGR, "create listener: error setting RCVBUF to %d", DEFAULT_SO_SNDBUF)); - // Just ignore the error - } - - tr = create_transport(mgr, type, sock_hnd, local_addr, addr_name); - if (!tr) { - pj_sock_close(sock_hnd); - return -1; - } -#if PJ_HAS_TCP - if (type == PJSIP_TRANSPORT_TCP) { - pj_status_t status; - - if (pj_sock_listen(tr->sock, BACKLOG) != 0) { - PJ_PERROR((tr->obj_name, "listen()")); - destroy_transport(mgr, tr); - return -1; - } - tr->accept_data.addrlen = sizeof(tr->accept_data.local); - status = pj_ioqueue_accept(mgr->ioqueue, tr->key, - &tr->accept_data.sock, - &tr->accept_data.local, - &tr->accept_data.remote, - &tr->accept_data.addrlen); - if (status != PJ_IOQUEUE_PENDING) { - PJ_PERROR((tr->obj_name, "pj_ioqueue_accept()")); - destroy_transport(mgr, tr); - return -1; - } - - } else -#endif - if (type == PJSIP_TRANSPORT_UDP) { - pj_status_t status; - - tr->rdata->addr_len = sizeof(tr->rdata->addr); - status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, - tr->rdata->packet, PJSIP_MAX_PKT_LEN, - &tr->rdata->addr, - &tr->rdata->addr_len); - if (status != PJ_IOQUEUE_PENDING) { - PJ_PERROR((tr->obj_name, "pj_ioqueue_recvfrom()")); - destroy_transport(mgr, tr); - return -1; - } - } - - pj_atomic_set(tr->ref_cnt, 1); - - /* Listeners normally have no remote address */ - pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr)); - - /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1. - * See further comments on struct pjsip_transport_t definition. - */ - if (type == PJSIP_TRANSPORT_UDP && local_addr->sin_addr.s_addr == inet_addr("127.0.0.1")) { - pj_str_t localaddr = pj_str("127.0.0.1"); - pj_sockaddr_set_str_addr( &tr->remote_addr, &localaddr); - } - hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); - init_key_from_transport(hash_key, tr); - - pj_mutex_lock(mgr->mutex); - pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr); - pj_mutex_unlock(mgr->mutex); - - PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d", - get_type_name(tr->type), - pj_sockaddr_get_str_addr(&tr->local_addr), - pj_sockaddr_get_port(&tr->local_addr))); - PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d", - get_type_name(tr->type), - pj_sockaddr_get_str_addr(&tr->addr_name), - pj_sockaddr_get_port(&tr->addr_name))); - return PJ_SUCCESS; -} - -/* - * Create listener. - */ -PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr, - pjsip_transport_type_e type, - pj_sockaddr_in *local_addr, - const pj_sockaddr_in *addr_name) -{ - pj_sock_t sock_hnd; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type)); - - sock_hnd = create_socket(type, local_addr); - if (sock_hnd == PJ_INVALID_SOCKET) { - return -1; - } - - return create_listener(mgr, type, sock_hnd, local_addr, addr_name); -} - -/* - * Create UDP listener. - */ -PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr, - pj_sock_t sock, - const pj_sockaddr_in *addr_name) -{ - pj_sockaddr_in local_addr; - int addrlen = sizeof(local_addr); - - if (pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen) != 0) - return -1; - - return create_listener(mgr, PJSIP_TRANSPORT_UDP, sock, &local_addr, addr_name); -} - -/* - * Find transport to be used to send message to remote destination. If no - * suitable transport is found, a new one will be created. - */ -PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr, - pj_pool_t *pool, - pjsip_transport_type_e type, - const pj_sockaddr_in *remote, - void *token, - pjsip_transport_completion_callback *cb) -{ - transport_key search_key, *hash_key; - pjsip_transport_t *tr; - pj_sockaddr_in local; - int sock_hnd; - pj_status_t status; - struct transport_callback *cb_rec; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()")); - - /* Create the callback record. - */ - cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec)); - cb_rec->token = token; - cb_rec->cb = cb; - - /* Create key for hash table look-up. - * The key creation is different for TCP and UDP. - */ -#if PJ_HAS_TCP - if (type==PJSIP_TRANSPORT_TCP) { - init_tcp_key(&search_key, type, remote); - } else -#endif - if (type==PJSIP_TRANSPORT_UDP) { - init_udp_key(&search_key, type, remote); - } - - /* Start lock the manager. */ - pj_mutex_lock(mgr->mutex); - - /* Lookup the transport in the hash table. */ - tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key)); - - if (tr) { - /* Transport found. If the transport is still busy (i.e. connecting - * is in progress), then just register the callback. Otherwise - * report via the callback if callback is specified. - */ - pj_mutex_unlock(mgr->mutex); - pj_mutex_lock(tr->tr_mutex); - - if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) { - /* Transport is busy. Just register the callback. */ - pj_list_insert_before(&tr->cb_list, cb_rec); - - } else { - /* Transport is ready. Call callback now. - */ - (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS); - } - pj_mutex_unlock(tr->tr_mutex); - - return; - } - - - /* Transport not found. Create new one. */ - pj_memset(&local, 0, sizeof(local)); - local.sin_family = PJ_AF_INET; - sock_hnd = create_socket(type, &local); - if (sock_hnd == PJ_INVALID_SOCKET) { - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, -1); - return; - } - tr = create_transport(mgr, type, sock_hnd, &local, NULL); - if (!tr) { - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, -1); - return; - } - -#if PJ_HAS_TCP - if (type == PJSIP_TRANSPORT_TCP) { - pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in)); - status = pj_ioqueue_connect(mgr->ioqueue, tr->key, - &tr->remote_addr, sizeof(pj_sockaddr_in)); - pj_assert(status != 0); - if (status != PJ_IOQUEUE_PENDING) { - PJ_PERROR((tr->obj_name, "pj_ioqueue_connect()")); - destroy_transport(mgr, tr); - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, -1); - return; - } - } else -#endif - if (type == PJSIP_TRANSPORT_UDP) { - int len; - - do { - tr->rdata->addr_len = sizeof(tr->rdata->addr); - len = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, - tr->rdata->packet, PJSIP_MAX_PKT_LEN, - &tr->rdata->addr, - &tr->rdata->addr_len); - pj_assert(len < 0); - if (len != PJ_IOQUEUE_PENDING) { - PJ_PERROR((tr->obj_name, "pj_ioqueue_recvfrom()")); - destroy_transport(mgr, tr); - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, -1); - return; - } - - /* Bug here. - * If data is immediately available, although not likely, it will - * be dropped because we don't expect to have data right after - * the socket is created, do we ?! - */ - PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA); - - } while (len != PJ_IOQUEUE_PENDING); - - //Bug: cb will never be called! - // Must force status to PJ_SUCCESS; - //status = PJ_IOQUEUE_PENDING; - - status = PJ_SUCCESS; - - } else { - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, -1); - return; - } - - pj_assert(status==PJ_IOQUEUE_PENDING || status==PJ_SUCCESS); - pj_mutex_lock(tr->tr_mutex); - hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); - pj_memcpy(hash_key, &search_key, sizeof(transport_key)); - pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr); - if (status == PJ_SUCCESS) { - pj_mutex_unlock(tr->tr_mutex); - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS); - } else { - pj_list_insert_before(&tr->cb_list, cb_rec); - pj_mutex_unlock(tr->tr_mutex); - pj_mutex_unlock(mgr->mutex); - } - -} - -#if PJ_HAS_TCP -/* - * Handle completion of asynchronous accept() operation. - * This function is called by handle_events() function. - */ -static void handle_new_connection( pjsip_transport_mgr *mgr, - pjsip_transport_t *listener, - pj_status_t status ) -{ - pjsip_transport_t *tr; - transport_key *hash_key; - - pj_assert (listener->type == PJSIP_TRANSPORT_TCP); - - if (status != PJ_SUCCESS) { - PJ_PERROR((listener->obj_name, "accept() returned error")); - return; - } - - PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d", - pj_sockaddr_get_str_addr(&listener->accept_data.remote), - pj_sockaddr_get_port(&listener->accept_data.remote))); - - tr = create_transport(mgr, listener->type, - listener->accept_data.sock, - &listener->accept_data.local, - NULL); - if (!tr) { - goto on_return; - } - - /* - tr->rdata->addr_len = sizeof(tr->rdata->addr); - status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, - tr->rdata->packet, PJSIP_MAX_PKT_LEN, - &tr->rdata->addr, - &tr->rdata->addr_len); - */ - tr->rdata->addr = listener->accept_data.remote; - tr->rdata->addr_len = listener->accept_data.addrlen; - - status = pj_ioqueue_read (mgr->ioqueue, tr->key, tr->rdata->packet, PJSIP_MAX_PKT_LEN); - if (status != PJ_IOQUEUE_PENDING) { - PJ_PERROR((tr->obj_name, "pj_ioqueue_read()")); - destroy_transport(mgr, tr); - goto on_return; - } - - pj_memcpy(&tr->remote_addr, &listener->accept_data.remote, listener->accept_data.addrlen); - hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); - init_key_from_transport(hash_key, tr); - - pj_mutex_lock(mgr->mutex); - pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr); - pj_mutex_unlock(mgr->mutex); - -on_return: - /* Re-initiate asynchronous accept() */ - listener->accept_data.addrlen = sizeof(listener->accept_data.local); - status = pj_ioqueue_accept(mgr->ioqueue, listener->key, - &listener->accept_data.sock, - &listener->accept_data.local, - &listener->accept_data.remote, - &listener->accept_data.addrlen); - if (status != PJ_IOQUEUE_PENDING) { - PJ_PERROR((listener->obj_name, "pj_ioqueue_accept()")); - return; - } -} - -/* - * Handle completion of asynchronous connect() function. - * This function is called by the handle_events() function. - */ -static void handle_connect_completion( pjsip_transport_mgr *mgr, - pjsip_transport_t *tr, - pj_status_t status ) -{ - struct transport_callback new_list; - struct transport_callback *cb_rec; - - PJ_UNUSED_ARG(mgr) - - /* On connect completion, we must call all registered callbacks in - * the transport. - */ - - /* Initialize new list. */ - pj_list_init(&new_list); - - /* Hold transport's mutex. We don't want other thread to register a - * callback while we're dealing with it. - */ - pj_mutex_lock(tr->tr_mutex); - - /* Copy callback list to new list so that we can call the callbacks - * without holding the mutex. - */ - pj_list_merge_last(&new_list, &tr->cb_list); - - /* Clear transport's busy flag. */ - tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY; - - /* If success, update local address. - * Local address is only available after connect() has returned. - */ - if (status == PJ_SUCCESS) { - int addrlen = sizeof(tr->local_addr); - int rc; - if ((rc=pj_sock_getsockname(tr->sock, (pj_sockaddr_t*)&tr->local_addr, &addrlen)) == 0) { - pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name)); - } else { - PJ_LOG(4,(tr->obj_name, "Unable to get local address (getsockname=%d)", rc)); - } - } - - /* Unlock mutex. */ - pj_mutex_unlock(tr->tr_mutex); - - /* Call all registered callbacks. */ - cb_rec = new_list.next; - while (cb_rec != &new_list) { - struct transport_callback *next; - next = cb_rec->next; - (*cb_rec->cb)(tr, cb_rec->token, status); - cb_rec = next; - } - - /* Success? */ - if (status != PJ_SUCCESS) { - destroy_transport(mgr, tr); - return; - } - - /* Initiate read operation to socket. */ - status = pj_ioqueue_read (mgr->ioqueue, tr->key, tr->rdata->packet, PJSIP_MAX_PKT_LEN); - if (status != PJ_IOQUEUE_PENDING) { - PJ_PERROR((tr->obj_name, "pj_ioqueue_read()")); - destroy_transport(mgr, tr); - return; - } -} -#endif /* PJ_HAS_TCP */ - -/* - * Handle incoming data. - * This function is called when the transport manager receives 'notification' - * from the I/O Queue that the receive operation has completed. - * This function will then attempt to parse the message, and hands over the - * message to the endpoint. - */ -static void handle_received_data( pjsip_transport_mgr *mgr, - pjsip_transport_t *tr, - pj_ssize_t size ) -{ - pjsip_msg *msg; - pjsip_cid_hdr *call_id; - pjsip_rx_data *rdata = tr->rdata; - pj_pool_t *rdata_pool; - pjsip_hdr *hdr; - pj_str_t s; - char *src_addr; - int src_port; - pj_size_t msg_fragment_size = 0; - - /* Check size. */ - if (size < 1) { - if (tr->type != PJSIP_TRANSPORT_UDP) { - /* zero bytes indicates transport has been closed for TCP. - * But alas, we can't destroy it now since transactions may still - * have reference to it. In that case, just do nothing, the - * transaction will receive error when it tries to send anything. - * But alas!! UAC transactions wont send anything!!. - * So this is a bug! - */ - if (pj_atomic_get(tr->ref_cnt)==0) { - PJ_LOG(4,(tr->obj_name, "connection closed")); - destroy_transport(mgr, tr); - } else { - PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED); - //PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED); - } - return; - } else { - /* On Windows machines, UDP recv() will return zero upon receiving - * ICMP port unreachable message. - */ - PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)")); - goto on_return; - } - } - - /* Save received time. */ - pj_gettimeofday(&rdata->timestamp); - - /* Update length. */ - rdata->len += size; - - /* Null terminate packet, this is the requirement of the parser. */ - rdata->packet[rdata->len] = '\0'; - - /* Get source address and port for logging purpose. */ - src_addr = pj_sockaddr_get_str_addr(&rdata->addr); - src_port = pj_sockaddr_get_port(&rdata->addr); - - /* Print the whole data to the log. */ - PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n" - "----------- begin msg ------------\n" - "%s" - "------------ end msg -------------", - rdata->len, src_addr, src_port, rdata->packet)); - - - /* Process all message fragments. */ - while (rdata->len > 0) { - - msg_fragment_size = rdata->len; -#if PJ_HAS_TCP - /* For TCP transport, check if the whole message has been received. */ - if (tr->type != PJSIP_TRANSPORT_UDP) { - pj_bool_t is_complete; - is_complete = pjsip_find_msg(rdata->packet, rdata->len, PJ_FALSE, &msg_fragment_size); - if (!is_complete) { - if (rdata->len == PJSIP_MAX_PKT_LEN) { - PJ_LOG(1,(tr->obj_name, - "Transport buffer full (%d bytes) for TCP socket %s:%d " - "(probably too many invalid fragments received). " - "Buffer will be discarded.", - PJSIP_MAX_PKT_LEN, src_addr, src_port)); - goto on_return; - } else { - goto tcp_read_packet; - } - } - } -#endif - - /* Clear parser error report */ - pj_list_init(&rdata->parse_err); - - /* Parse the message. */ - PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size, - src_addr, src_port)); - - msg = pjsip_parse_msg( rdata->pool, rdata->packet, msg_fragment_size, - &rdata->parse_err); - if (msg == NULL) { - PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size, - src_addr, src_port)); - goto finish_process_fragment; - } - - /* Attach newly created message to rdata. */ - rdata->msg = msg; - - /* Extract Call-ID, From and To header and tags, topmost Via, and CSeq - * header from the message. - */ - call_id = pjsip_msg_find_hdr( msg, PJSIP_H_CALL_ID, NULL); - rdata->from = pjsip_msg_find_hdr( msg, PJSIP_H_FROM, NULL); - rdata->to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL); - rdata->via = pjsip_msg_find_hdr( msg, PJSIP_H_VIA, NULL); - rdata->cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL); - - if (call_id == NULL || rdata->from == NULL || rdata->to == NULL || - rdata->via == NULL || rdata->cseq == NULL) - { - PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header", - src_addr, src_port)); - goto finish_process_fragment; - } - rdata->call_id = call_id->id; - rdata->from_tag = rdata->from->tag; - rdata->to_tag = rdata->to->tag; - - /* If message is received from address that's different from the sent-by, - * MUST add received parameter to the via. - * In our case, we add Via receive param for EVERY received message, - * because it saves us from resolving the host HERE in case sent-by is in - * FQDN format. And it doesn't hurt either. - */ - s = pj_str(src_addr); - pj_strdup(rdata->pool, &rdata->via->recvd_param, &s); - - /* RFC 3581: - * If message contains "rport" param, put the received port there. - */ - if (rdata->via->rport_param == 0) { - rdata->via->rport_param = pj_sockaddr_get_port(&rdata->addr); - } - - /* Drop response message if it has more than one Via. - */ - if (msg->type == PJSIP_RESPONSE_MSG) { - hdr = (pjsip_hdr*)rdata->via->next; - if (hdr) { - hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr); - if (hdr) { - PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: " - "multiple Via in response message", - src_addr, src_port)); - goto finish_process_fragment; - } - } - } - - /* Call the transport manager's upstream message callback. - */ - (*mgr->message_callback)(mgr->endpt, rdata); - -finish_process_fragment: - rdata->len -= msg_fragment_size; - if (rdata->len > 0) { - pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len); - PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len)); - } - - } /* while (rdata->len > 0) */ - -on_return: - /* Reset the pool and rdata */ - rdata_pool = rdata->pool; - pj_pool_reset(rdata_pool); - rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) ); - rdata->len = 0; - rdata->transport = tr; - rdata->pool = rdata_pool; - tr->rdata = rdata; - - /* Read the next packet. */ - rdata->addr_len = sizeof(rdata->addr); - if (tr->type == PJSIP_TRANSPORT_UDP) { - pj_ioqueue_recvfrom( tr->mgr->ioqueue, tr->key, - tr->rdata->packet, PJSIP_MAX_PKT_LEN, - &rdata->addr, &rdata->addr_len); - } - -#if PJ_HAS_TCP - /* The next 'if' should have been 'else if', but we need to put the - label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning. - */ -tcp_read_packet: - if (tr->type == PJSIP_TRANSPORT_TCP) { - pj_ioqueue_read( tr->mgr->ioqueue, tr->key, - tr->rdata->packet + tr->rdata->len, - PJSIP_MAX_PKT_LEN - tr->rdata->len); - } -#endif -} - -static void transport_mgr_on_idle( pjsip_transport_mgr *mgr ) -{ - pj_time_val now; - pj_hash_iterator_t itr_val; - pj_hash_iterator_t *itr; - - - /* Get time for comparing transport's close time. */ - pj_gettimeofday(&now); - if (now.sec < mgr->next_idle_check.sec) { - return; - } - - /* Acquire transport manager's lock. */ - pj_mutex_lock(mgr->mutex); - - /* Update next idle check. */ - mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL; - - /* Iterate all transports, and close transports that are not used for - some periods. - */ - itr = pjsip_transport_first(mgr, &itr_val); - while (itr != NULL) { - pj_hash_iterator_t *next; - pjsip_transport_t *transport; - - transport = pjsip_transport_this(mgr, itr); - - next = pjsip_transport_next(mgr, itr); - - if (pj_atomic_get(transport->ref_cnt)==0 && - PJ_TIME_VAL_LTE(transport->close_time, now)) - { - destroy_transport(mgr, transport); - } - - itr = next; - } - - /* Release transport manager's lock. */ - pj_mutex_unlock(mgr->mutex); -} - -static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) -{ - pjsip_transport_t *t; - t = pj_ioqueue_get_user_data(key); - - handle_received_data( t->mgr, t, bytes_read ); -} - -static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent) -{ - PJ_UNUSED_ARG(key) - PJ_UNUSED_ARG(bytes_sent) - - /* Completion of write operation. - * Do nothing. - */ -} - -static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status) -{ -#if PJ_HAS_TCP - pjsip_transport_t *t; - t = pj_ioqueue_get_user_data(key); - - handle_new_connection( t->mgr, t, status ); -#else - PJ_UNUSED_ARG(key) - PJ_UNUSED_ARG(status) -#endif -} - -static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status) -{ -#if PJ_HAS_TCP - pjsip_transport_t *t; - t = pj_ioqueue_get_user_data(key); - - handle_connect_completion( t->mgr, t, status); -#else - PJ_UNUSED_ARG(key) - PJ_UNUSED_ARG(status) -#endif -} - - -/* - * Poll for events. - */ -PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr, - const pj_time_val *req_timeout ) -{ - int event_count; - int break_loop; - int result; - pj_time_val timeout; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()")); - - event_count = 0; - break_loop = 0; - timeout = *req_timeout; - do { - result = pj_ioqueue_poll( mgr->ioqueue, &timeout); - if (result == 1) { - ++event_count; - - /* Break the loop. */ - //if (timeout.msec==0 && timeout.sec==0) { - break_loop = 1; - //} - - } else { - /* On idle, cleanup transport. */ - transport_mgr_on_idle(mgr); - - break_loop = 1; - } - timeout.sec = timeout.msec = 0; - } while (!break_loop); - - return event_count; -} - - -PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *it ) -{ - return pj_hash_first(mgr->transport_table, it); -} - -PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *itr ) -{ - return pj_hash_next(mgr->transport_table, itr); -} - -PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *itr ) -{ - return pj_hash_this(mgr->transport_table, itr); -} +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transport.c 18 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MGR_IDLE_CHECK_INTERVAL 30 +#define MGR_HASH_TABLE_SIZE PJSIP_MAX_DIALOG_COUNT +#define BACKLOG 5 +#define DEFAULT_SO_SNDBUF (8 * 1024 * 1024) +#define DEFAULT_SO_RCVBUF (8 * 1024 * 1024) + +#define LOG_TRANSPORT_MGR "trmgr" +#define THIS_FILE "sip_transport" + +static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr ); + + +/** + * New TCP socket for accept. + */ +typedef struct incoming_socket_rec +{ + pj_sock_t sock; + pj_sockaddr_in remote; + pj_sockaddr_in local; + int addrlen; +} incoming_socket_rec; + +/** + * SIP Transport. + */ +struct pjsip_transport_t +{ + /** Standard list members, for chaining the transport in the + * listener list. + */ + PJ_DECL_LIST_MEMBER(struct pjsip_transport_t) + + /** Transport's pool. */ + pj_pool_t *pool; + + /** Mutex */ + pj_mutex_t *tr_mutex; + + /** Transport name for logging purpose */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** Socket handle */ + pj_sock_t sock; + + /** Transport type. */ + pjsip_transport_type_e type; + + /** Flags to keep various states (see pjsip_transport_flags_e). */ + pj_uint32_t flag; + + /** I/O Queue key */ + pj_ioqueue_key_t *key; + + /** Receive data buffer */ + pjsip_rx_data *rdata; + + /** Pointer to transport manager */ + pjsip_transport_mgr *mgr; + + /** Reference counter, to prevent this transport from being closed while + * it's being used. + */ + pj_atomic_t *ref_cnt; + + /** Local address. */ + pj_sockaddr_in local_addr; + + /** Address name (what to put in Via address field). */ + pj_sockaddr_in addr_name; + + /** Remote address (can be zero for UDP and for listeners). UDP listener + * bound to local loopback interface (127.0.0.1) has remote address set + * to 127.0.0.1 to prevent client from using it to send to remote hosts, + * because remote host then will receive 127.0.0.1 as the packet's + * source address. + */ + pj_sockaddr_in remote_addr; + + /** Struct to save incoming socket information. */ + incoming_socket_rec accept_data; + + /** When this transport should be closed. */ + pj_time_val close_time; + + /** List of callbacks to be called when client attempt to use this + * transport while it's not connected (i.e. still connecting). + */ + pj_list cb_list; +}; + + +/* + * Transport manager. + */ +struct pjsip_transport_mgr +{ + pj_hash_table_t *transport_table; + pj_mutex_t *mutex; + pjsip_endpoint *endpt; + pj_ioqueue_t *ioqueue; + pj_time_val next_idle_check; + void (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata); +}; + +/* + * Transport role. + */ +typedef enum transport_role_e +{ + TRANSPORT_ROLE_LISTENER, + TRANSPORT_ROLE_TRANSPORT, +} transport_role_e; + +/* + * Transport key for indexing in the hash table. + * WATCH OUT FOR ALIGNMENT PROBLEM HERE! + */ +typedef struct transport_key +{ + pj_uint8_t type; + pj_uint8_t zero; + pj_uint16_t port; + pj_uint32_t addr; +} transport_key; + +/* + * Transport callback. + */ +struct transport_callback +{ + PJ_DECL_LIST_MEMBER(struct transport_callback) + + /** User defined token to be passed to the callback. */ + void *token; + + /** The callback function. */ + void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status); + +}; + +/* + * Transport names. + */ +const struct +{ + pjsip_transport_type_e type; + pj_uint16_t port; + pj_str_t name; +} transport_names[] = +{ + { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}}, + { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}}, +#if PJ_HAS_TCP + { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}}, + { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}}, + { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}} +#endif +}; + +static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read); +static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent); +static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status); +static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status); + +static pj_ioqueue_callback ioqueue_transport_callback = +{ + &on_ioqueue_read, + &on_ioqueue_write, + &on_ioqueue_accept, + &on_ioqueue_connect +}; + +static void init_key_from_transport(transport_key *key, + const pjsip_transport_t *tr) +{ + /* This is to detect alignment problems. */ + pj_assert(sizeof(transport_key) == 8); + + key->type = (pj_uint8_t)tr->type; + key->zero = 0; + key->addr = pj_sockaddr_get_addr(&tr->remote_addr); + key->port = pj_sockaddr_get_port(&tr->remote_addr); + /* + if (key->port == 0) { + key->port = pj_sockaddr_get_port(&tr->local_addr); + } + */ +} + +#if PJ_HAS_TCP +static void init_tcp_key(transport_key *key, pjsip_transport_type_e type, + const pj_sockaddr_in *addr) +{ + /* This is to detect alignment problems. */ + pj_assert(sizeof(transport_key) == 8); + + key->type = (pj_uint8_t)type; + key->zero = 0; + key->addr = pj_sockaddr_get_addr(addr); + key->port = pj_sockaddr_get_port(addr); +} +#endif + +static void init_udp_key(transport_key *key, pjsip_transport_type_e type, + const pj_sockaddr_in *addr) +{ + PJ_UNUSED_ARG(addr) + + /* This is to detect alignment problems. */ + pj_assert(sizeof(transport_key) == 8); + + pj_memset(key, 0, sizeof(*key)); + key->type = (pj_uint8_t)type; + +#if 0 /* Not sure why we need to make 127.0.0.1 a special case */ + if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) { + /* This looks more complicated than it is because key->addr uses + * the host version of the address (i.e. converted with ntohl()). + */ + pj_str_t localaddr = pj_str("127.0.0.1"); + pj_sockaddr_in addr; + pj_sockaddr_set_str_addr(&addr, &localaddr); + key->addr = pj_sockaddr_get_addr(&addr); + } +#endif +} + +/* + * Get type format name (for pool name). + */ +static const char *transport_get_name_format( int type ) +{ + switch (type) { + case PJSIP_TRANSPORT_UDP: + return " udp%p"; +#if PJ_HAS_TCP + case PJSIP_TRANSPORT_TCP: + return " tcp%p"; + case PJSIP_TRANSPORT_TLS: + return " tls%p"; + case PJSIP_TRANSPORT_SCTP: + return "sctp%p"; +#endif + } + pj_assert(0); + return 0; +} + +/* + * Get the default SIP port number for the specified type. + */ +PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type) +{ + return transport_names[type].port; +} + +/* + * Get transport name. + */ +static const char *get_type_name(int type) +{ + return transport_names[type].name.ptr; +} + +/* + * Get transport type from name. + */ +PJ_DEF(pjsip_transport_type_e) +pjsip_transport_get_type_from_name(const pj_str_t *name) +{ + unsigned i; + + for (i=0; iendpt, "ptdt%p", + PJSIP_POOL_LEN_TDATA, + PJSIP_POOL_INC_TDATA ); + if (!pool) { + return NULL; + } + tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data)); + tdata->pool = pool; + tdata->mgr = mgr; + sprintf(tdata->obj_name,"txd%p", tdata); + + tdata->ref_cnt = pj_atomic_create(tdata->pool, 0); + if (!tdata->ref_cnt) { + pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool ); + return NULL; + } + + return tdata; +} + +/* + * Add reference to tx buffer. + */ +PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ) +{ + pj_atomic_inc(tdata->ref_cnt); +} + +/* + * Decrease transport data reference, destroy it when the reference count + * reaches zero. + */ +PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) +{ + pj_assert( pj_atomic_get(tdata->ref_cnt) > 0); + if (pj_atomic_dec(tdata->ref_cnt) <= 0) { + PJ_LOG(6,(tdata->obj_name, "destroying txdata")); + pj_atomic_destroy( tdata->ref_cnt ); + pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool ); + } +} + +/* + * Invalidate the content of the print buffer to force the message to be + * re-printed when sent. + */ +PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ) +{ + tdata->buf.cur = tdata->buf.start; +} + +/* + * Get the transport type. + */ +PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr) +{ + return tr->type; +} + +/* + * Get transport type from transport flag. + */ +PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag) +{ +#if PJ_HAS_TCP + if (flag & PJSIP_TRANSPORT_SECURE) { + return PJSIP_TRANSPORT_TLS; + } else if (flag & PJSIP_TRANSPORT_RELIABLE) { + return PJSIP_TRANSPORT_TCP; + } else +#else + PJ_UNUSED_ARG(flag) +#endif + { + return PJSIP_TRANSPORT_UDP; + } +} + +/* + * Get the transport type name. + */ +PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr) +{ + return get_type_name(tr->type); +} + +/* + * Get the transport's object name. + */ +PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr ) +{ + return tr->obj_name; +} + +/* + * Get the transport's reference counter. + */ +PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr ) +{ + return pj_atomic_get(tr->ref_cnt); +} + +/* + * Get transport local address. + */ +PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr ) +{ + return &tr->local_addr; +} + +/* + * Get address name. + */ +PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr) +{ + return &tr->addr_name; +} + +/* + * Get transport remote address. + */ +PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr ) +{ + return &tr->remote_addr; +} + +/* + * Get transport flag. + */ +PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr ) +{ + return tr->flag; +} + +/* + * Add reference to the specified transport. + */ +PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr ) +{ + pj_atomic_inc(tr->ref_cnt); +} + +/* + * Decrease the reference time of the transport. + */ +PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr ) +{ + pj_assert(tr->ref_cnt > 0); + if (pj_atomic_dec(tr->ref_cnt) == 0) { + pj_gettimeofday(&tr->close_time); + tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT; + } +} + +/* + * Open the underlying transport. + */ +static pj_sock_t create_socket( pjsip_transport_type_e type, + pj_sockaddr_in *local ) +{ + int sock_family; + int sock_type; + int sock_proto; + int len; + pj_sock_t sock; + + /* Set socket parameters */ + if (type == PJSIP_TRANSPORT_UDP) { + sock_family = PJ_AF_INET; + sock_type = PJ_SOCK_DGRAM; + sock_proto = 0; + +#if PJ_HAS_TCP + } else if (type == PJSIP_TRANSPORT_TCP) { + sock_family = PJ_AF_INET; + sock_type = PJ_SOCK_STREAM; + sock_proto = 0; +#endif + } else { + PJ_LOG(2,("", "create_socket: unsupported transport type %s", + get_type_name(type))); + return PJ_INVALID_SOCKET; + } + + /* Create socket. */ + sock = pj_sock_socket( sock_family, sock_type, sock_proto, PJ_SOCK_ASYNC); + if (sock == PJ_INVALID_SOCKET) { + PJ_PERROR((THIS_FILE, "%s socket()", get_type_name(type))); + return PJ_INVALID_SOCKET; + } + + /* Bind the socket to the requested address, or if no address is + * specified, let the operating system chooses the address. + */ + if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) { + /* Bind to the requested address. */ + if (pj_sock_bind(sock, local, sizeof(*local)) != 0) { + PJ_PERROR((THIS_FILE, "bind() to %s %s:%d", + get_type_name(type), + pj_sockaddr_get_str_addr(local), + pj_sockaddr_get_port(local))); + pj_sock_close(sock); + return PJ_INVALID_SOCKET; + } + } else if (type == PJSIP_TRANSPORT_UDP) { + /* Only for UDP sockets: bind to any address so that the operating + * system allocates the port for us. For TCP, let the OS implicitly + * bind the socket with connect() syscall (if we bind now, then we'll + * get 0.0.0.0 as local address). + */ + pj_memset(local, 0, sizeof(*local)); + local->sin_family = PJ_AF_INET; + if (pj_sock_bind(sock, local, sizeof(*local)) != 0) { + PJ_PERROR((THIS_FILE, "bind() to %s 0.0.0.0:0", get_type_name(type))); + pj_sock_close(sock); + return PJ_INVALID_SOCKET; + } + + /* Get the local address. */ + len = sizeof(pj_sockaddr_in); + if (pj_sock_getsockname(sock, local, &len)) { + PJ_PERROR((THIS_FILE, "getsockname()")); + pj_sock_close(sock); + return -1; + } + } + + return sock; +} + +/* + * Close the transport. + */ +static void destroy_socket( pjsip_transport_t * tr) +{ + pj_assert( pj_atomic_get(tr->ref_cnt) == 0); + pj_sock_close(tr->sock); + tr->sock = -1; +} + +/* + * Create a new transport object. + */ +static pjsip_transport_t* create_transport( pjsip_transport_mgr *mgr, + pjsip_transport_type_e type, + pj_sock_t sock_hnd, + const pj_sockaddr_in *local_addr, + const pj_sockaddr_in *addr_name) +{ + pj_pool_t *tr_pool=NULL, *rdata_pool=NULL; + pjsip_transport_t *tr = NULL; + + /* Allocate pool for transport from endpoint. */ + tr_pool = pjsip_endpt_create_pool( mgr->endpt, + transport_get_name_format(type), + PJSIP_POOL_LEN_TRANSPORT, + PJSIP_POOL_INC_TRANSPORT ); + if (!tr_pool) { + goto on_error; + } + + /* Allocate pool for rdata from endpoint. */ + rdata_pool = pjsip_endpt_create_pool( mgr->endpt, + "prdt%p", + PJSIP_POOL_LEN_RDATA, + PJSIP_POOL_INC_RDATA ); + if (!rdata_pool) { + goto on_error; + } + + /* Allocate and initialize the transport. */ + tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr)); + tr->pool = tr_pool; + tr->type = type; + tr->mgr = mgr; + tr->sock = sock_hnd; + pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in)); + pj_list_init(&tr->cb_list); + sprintf(tr->obj_name, transport_get_name_format(type), tr); + + if (type != PJSIP_TRANSPORT_UDP) { + tr->flag |= PJSIP_TRANSPORT_RELIABLE; + } + + /* Address name. */ + if (addr_name == NULL) { + addr_name = &tr->local_addr; + } + pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name)); + + /* Create atomic */ + tr->ref_cnt = pj_atomic_create(tr_pool, 0); + if (!tr->ref_cnt) { + goto on_error; + } + + /* Init rdata in the transport. */ + tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata)); + tr->rdata->pool = rdata_pool; + tr->rdata->len = 0; + tr->rdata->transport = tr; + + /* Init transport mutex. */ + tr->tr_mutex = pj_mutex_create(tr_pool, "mtr%p", 0); + if (!tr->tr_mutex) { + PJ_PERROR((tr->obj_name, "pj_mutex_create()")); + goto on_error; + } + + /* Register to I/O Queue */ + tr->key = pj_ioqueue_register(tr_pool, mgr->ioqueue, + (pj_oshandle_t)tr->sock, tr, + &ioqueue_transport_callback); + if (tr->key == NULL) { + PJ_PERROR((tr->obj_name, "pj_ioqueue_register()")); + goto on_error; + } + + return tr; + +on_error: + if (tr && tr->tr_mutex) { + pj_mutex_destroy(tr->tr_mutex); + } + if (tr_pool) { + pjsip_endpt_destroy_pool(mgr->endpt, tr_pool); + } + if (rdata_pool) { + pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool); + } + return NULL; +} + +/* + * Destroy transport. + */ +static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr ) +{ + transport_key hash_key; + + /* Remove from I/O queue. */ + pj_ioqueue_unregister( mgr->ioqueue, tr->key ); + + /* Remove from hash table */ + init_key_from_transport(&hash_key, tr); + pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL); + + /* Close transport. */ + destroy_socket(tr); + + /* Destroy the transport mutex. */ + pj_mutex_destroy(tr->tr_mutex); + + /* Destroy atomic */ + pj_atomic_destroy( tr->ref_cnt ); + + /* Release the pool associated with the rdata. */ + pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool ); + + /* Release the pool associated with the transport. */ + pjsip_endpt_destroy_pool(mgr->endpt, tr->pool ); +} + + +static int transport_send_msg( pjsip_transport_t *tr, pjsip_tx_data *tdata, + const pj_sockaddr_in *addr) +{ + const char *buf = tdata->buf.start; + int sent; + int len; + + /* Allocate buffer if necessary. */ + if (tdata->buf.start == NULL) { + tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN); + tdata->buf.cur = tdata->buf.start; + tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN; + } + + /* Print the message if it's not printed */ + if (tdata->buf.cur <= tdata->buf.start) { + len = pjsip_msg_print(tdata->msg, tdata->buf.start, + tdata->buf.end - tdata->buf.start); + if (len < 1) { + return len; + } + tdata->buf.cur += len; + tdata->buf.cur[len] = '\0'; + } + + /* BUG BUG BUG */ + /* MUST CHECK THAT THE SOCKET IS READY TO SEND (IOQueue)! */ + PJ_TODO(BUG_BUG_BUG___SENDING_DATAGRAM_WHILE_SOCKET_IS_PENDING__) + + /* Send the message. */ + buf = tdata->buf.start; + len = tdata->buf.cur - tdata->buf.start; + + if (tr->type == PJSIP_TRANSPORT_UDP) { + PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n" + "----------- begin msg ------------\n" + "%s" + "------------ end msg -------------", + pj_sockaddr_get_str_addr(addr), + pj_sockaddr_get_port(addr), + len, buf)); + + sent = pj_ioqueue_sendto( tr->mgr->ioqueue, tr->key, + buf, len, addr, sizeof(*addr)); + } +#if PJ_HAS_TCP + else { + PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n" + "----------- begin msg ------------\n" + "%s" + "------------ end msg -------------", + len, buf)); + + sent = pj_ioqueue_write (tr->mgr->ioqueue, tr->key, buf, len); + } +#else + else { + pj_assert(0); + sent = -1; + } +#endif + + if (sent == len || sent == PJ_IOQUEUE_PENDING) { + return len; + } + + /* On error, clear the flag. */ + PJ_PERROR((tr->obj_name, tr->type == PJSIP_TRANSPORT_UDP ? "pj_ioqueue_sendto()" : "pj_ioqueue_write()")); + return -1; +} + +/* + * Send a SIP message using the specified transport, to the address specified + * in the outgoing data. + */ +PJ_DEF(int) pjsip_transport_send_msg( pjsip_transport_t *tr, + pjsip_tx_data *tdata, + const pj_sockaddr_in *addr) +{ + int sent; + + PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name)); + + sent = transport_send_msg(tr, tdata, addr ); + return sent; +} + +/////////////////////////////////////////////////////////////////////////////// + +/* + * Create a new transport manager. + */ +PJ_DEF(pjsip_transport_mgr*) +pjsip_transport_mgr_create( pj_pool_t *pool, + pjsip_endpoint * endpt, + void (*cb)(pjsip_endpoint*,pjsip_rx_data *) ) +{ + pjsip_transport_mgr *mgr; + + PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()")); + + mgr = pj_pool_alloc(pool, sizeof(*mgr)); + mgr->endpt = endpt; + mgr->message_callback = cb; + + mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE); + if (!mgr->transport_table) { + PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating transport manager hash table")); + return NULL; + } + mgr->ioqueue = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS); + if (!mgr->ioqueue) { + PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating IO queue")); + return NULL; + } + mgr->mutex = pj_mutex_create(pool, "tmgr%p", 0); + if (!mgr->mutex) { + PJ_LOG(3, (LOG_TRANSPORT_MGR, "error creating mutex")); + pj_ioqueue_destroy(mgr->ioqueue); + return NULL; + } + pj_gettimeofday(&mgr->next_idle_check); + mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL; + return mgr; +} + +/* + * Destroy transport manager. + */ +PJ_DEF(void) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr ) +{ + pj_hash_iterator_t itr_val; + pj_hash_iterator_t *itr; + + PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()")); + + pj_mutex_lock(mgr->mutex); + + itr = pjsip_transport_first(mgr, &itr_val); + while (itr != NULL) { + pj_hash_iterator_t *next; + pjsip_transport_t *transport; + + transport = pjsip_transport_this(mgr, itr); + + next = pjsip_transport_next(mgr, itr); + + pj_atomic_set(transport->ref_cnt, 0); + destroy_transport( mgr, transport); + + itr = next; + } + pj_ioqueue_destroy(mgr->ioqueue); + + pj_mutex_unlock(mgr->mutex); +} + +/* + * Create listener + */ +static pj_status_t create_listener( pjsip_transport_mgr *mgr, + pjsip_transport_type_e type, + pj_sock_t sock_hnd, + pj_sockaddr_in *local_addr, + const pj_sockaddr_in *addr_name) +{ + pjsip_transport_t *tr; + struct transport_key *hash_key; + int opt_val; + + opt_val = DEFAULT_SO_SNDBUF; + if (pj_sock_setsockopt( sock_hnd, SOL_SOCKET, SO_SNDBUF, &opt_val, sizeof(opt_val)) != PJ_SUCCESS) { + PJ_LOG(3, (LOG_TRANSPORT_MGR, "create listener: error setting SNDBUF to %d", DEFAULT_SO_SNDBUF)); + // Just ignore the error. + } + + opt_val = DEFAULT_SO_RCVBUF; + if (pj_sock_setsockopt( sock_hnd, SOL_SOCKET, SO_RCVBUF, &opt_val, sizeof(opt_val)) != PJ_SUCCESS) { + PJ_LOG(3, (LOG_TRANSPORT_MGR, "create listener: error setting RCVBUF to %d", DEFAULT_SO_SNDBUF)); + // Just ignore the error + } + + tr = create_transport(mgr, type, sock_hnd, local_addr, addr_name); + if (!tr) { + pj_sock_close(sock_hnd); + return -1; + } +#if PJ_HAS_TCP + if (type == PJSIP_TRANSPORT_TCP) { + pj_status_t status; + + if (pj_sock_listen(tr->sock, BACKLOG) != 0) { + PJ_PERROR((tr->obj_name, "listen()")); + destroy_transport(mgr, tr); + return -1; + } + tr->accept_data.addrlen = sizeof(tr->accept_data.local); + status = pj_ioqueue_accept(mgr->ioqueue, tr->key, + &tr->accept_data.sock, + &tr->accept_data.local, + &tr->accept_data.remote, + &tr->accept_data.addrlen); + if (status != PJ_IOQUEUE_PENDING) { + PJ_PERROR((tr->obj_name, "pj_ioqueue_accept()")); + destroy_transport(mgr, tr); + return -1; + } + + } else +#endif + if (type == PJSIP_TRANSPORT_UDP) { + pj_status_t status; + + tr->rdata->addr_len = sizeof(tr->rdata->addr); + status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, + tr->rdata->packet, PJSIP_MAX_PKT_LEN, + &tr->rdata->addr, + &tr->rdata->addr_len); + if (status != PJ_IOQUEUE_PENDING) { + PJ_PERROR((tr->obj_name, "pj_ioqueue_recvfrom()")); + destroy_transport(mgr, tr); + return -1; + } + } + + pj_atomic_set(tr->ref_cnt, 1); + + /* Listeners normally have no remote address */ + pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr)); + + /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1. + * See further comments on struct pjsip_transport_t definition. + */ + if (type == PJSIP_TRANSPORT_UDP && local_addr->sin_addr.s_addr == inet_addr("127.0.0.1")) { + pj_str_t localaddr = pj_str("127.0.0.1"); + pj_sockaddr_set_str_addr( &tr->remote_addr, &localaddr); + } + hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); + init_key_from_transport(hash_key, tr); + + pj_mutex_lock(mgr->mutex); + pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr); + pj_mutex_unlock(mgr->mutex); + + PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d", + get_type_name(tr->type), + pj_sockaddr_get_str_addr(&tr->local_addr), + pj_sockaddr_get_port(&tr->local_addr))); + PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d", + get_type_name(tr->type), + pj_sockaddr_get_str_addr(&tr->addr_name), + pj_sockaddr_get_port(&tr->addr_name))); + return PJ_SUCCESS; +} + +/* + * Create listener. + */ +PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr, + pjsip_transport_type_e type, + pj_sockaddr_in *local_addr, + const pj_sockaddr_in *addr_name) +{ + pj_sock_t sock_hnd; + + PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type)); + + sock_hnd = create_socket(type, local_addr); + if (sock_hnd == PJ_INVALID_SOCKET) { + return -1; + } + + return create_listener(mgr, type, sock_hnd, local_addr, addr_name); +} + +/* + * Create UDP listener. + */ +PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr, + pj_sock_t sock, + const pj_sockaddr_in *addr_name) +{ + pj_sockaddr_in local_addr; + int addrlen = sizeof(local_addr); + + if (pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen) != 0) + return -1; + + return create_listener(mgr, PJSIP_TRANSPORT_UDP, sock, &local_addr, addr_name); +} + +/* + * Find transport to be used to send message to remote destination. If no + * suitable transport is found, a new one will be created. + */ +PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr, + pj_pool_t *pool, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + void *token, + pjsip_transport_completion_callback *cb) +{ + transport_key search_key, *hash_key; + pjsip_transport_t *tr; + pj_sockaddr_in local; + int sock_hnd; + pj_status_t status; + struct transport_callback *cb_rec; + + PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()")); + + /* Create the callback record. + */ + cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec)); + cb_rec->token = token; + cb_rec->cb = cb; + + /* Create key for hash table look-up. + * The key creation is different for TCP and UDP. + */ +#if PJ_HAS_TCP + if (type==PJSIP_TRANSPORT_TCP) { + init_tcp_key(&search_key, type, remote); + } else +#endif + if (type==PJSIP_TRANSPORT_UDP) { + init_udp_key(&search_key, type, remote); + } + + /* Start lock the manager. */ + pj_mutex_lock(mgr->mutex); + + /* Lookup the transport in the hash table. */ + tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key)); + + if (tr) { + /* Transport found. If the transport is still busy (i.e. connecting + * is in progress), then just register the callback. Otherwise + * report via the callback if callback is specified. + */ + pj_mutex_unlock(mgr->mutex); + pj_mutex_lock(tr->tr_mutex); + + if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) { + /* Transport is busy. Just register the callback. */ + pj_list_insert_before(&tr->cb_list, cb_rec); + + } else { + /* Transport is ready. Call callback now. + */ + (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS); + } + pj_mutex_unlock(tr->tr_mutex); + + return; + } + + + /* Transport not found. Create new one. */ + pj_memset(&local, 0, sizeof(local)); + local.sin_family = PJ_AF_INET; + sock_hnd = create_socket(type, &local); + if (sock_hnd == PJ_INVALID_SOCKET) { + pj_mutex_unlock(mgr->mutex); + (*cb_rec->cb)(NULL, cb_rec->token, -1); + return; + } + tr = create_transport(mgr, type, sock_hnd, &local, NULL); + if (!tr) { + pj_mutex_unlock(mgr->mutex); + (*cb_rec->cb)(NULL, cb_rec->token, -1); + return; + } + +#if PJ_HAS_TCP + if (type == PJSIP_TRANSPORT_TCP) { + pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in)); + status = pj_ioqueue_connect(mgr->ioqueue, tr->key, + &tr->remote_addr, sizeof(pj_sockaddr_in)); + pj_assert(status != 0); + if (status != PJ_IOQUEUE_PENDING) { + PJ_PERROR((tr->obj_name, "pj_ioqueue_connect()")); + destroy_transport(mgr, tr); + pj_mutex_unlock(mgr->mutex); + (*cb_rec->cb)(NULL, cb_rec->token, -1); + return; + } + } else +#endif + if (type == PJSIP_TRANSPORT_UDP) { + int len; + + do { + tr->rdata->addr_len = sizeof(tr->rdata->addr); + len = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, + tr->rdata->packet, PJSIP_MAX_PKT_LEN, + &tr->rdata->addr, + &tr->rdata->addr_len); + pj_assert(len < 0); + if (len != PJ_IOQUEUE_PENDING) { + PJ_PERROR((tr->obj_name, "pj_ioqueue_recvfrom()")); + destroy_transport(mgr, tr); + pj_mutex_unlock(mgr->mutex); + (*cb_rec->cb)(NULL, cb_rec->token, -1); + return; + } + + /* Bug here. + * If data is immediately available, although not likely, it will + * be dropped because we don't expect to have data right after + * the socket is created, do we ?! + */ + PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA); + + } while (len != PJ_IOQUEUE_PENDING); + + //Bug: cb will never be called! + // Must force status to PJ_SUCCESS; + //status = PJ_IOQUEUE_PENDING; + + status = PJ_SUCCESS; + + } else { + pj_mutex_unlock(mgr->mutex); + (*cb_rec->cb)(NULL, cb_rec->token, -1); + return; + } + + pj_assert(status==PJ_IOQUEUE_PENDING || status==PJ_SUCCESS); + pj_mutex_lock(tr->tr_mutex); + hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); + pj_memcpy(hash_key, &search_key, sizeof(transport_key)); + pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr); + if (status == PJ_SUCCESS) { + pj_mutex_unlock(tr->tr_mutex); + pj_mutex_unlock(mgr->mutex); + (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS); + } else { + pj_list_insert_before(&tr->cb_list, cb_rec); + pj_mutex_unlock(tr->tr_mutex); + pj_mutex_unlock(mgr->mutex); + } + +} + +#if PJ_HAS_TCP +/* + * Handle completion of asynchronous accept() operation. + * This function is called by handle_events() function. + */ +static void handle_new_connection( pjsip_transport_mgr *mgr, + pjsip_transport_t *listener, + pj_status_t status ) +{ + pjsip_transport_t *tr; + transport_key *hash_key; + + pj_assert (listener->type == PJSIP_TRANSPORT_TCP); + + if (status != PJ_SUCCESS) { + PJ_PERROR((listener->obj_name, "accept() returned error")); + return; + } + + PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d", + pj_sockaddr_get_str_addr(&listener->accept_data.remote), + pj_sockaddr_get_port(&listener->accept_data.remote))); + + tr = create_transport(mgr, listener->type, + listener->accept_data.sock, + &listener->accept_data.local, + NULL); + if (!tr) { + goto on_return; + } + + /* + tr->rdata->addr_len = sizeof(tr->rdata->addr); + status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, + tr->rdata->packet, PJSIP_MAX_PKT_LEN, + &tr->rdata->addr, + &tr->rdata->addr_len); + */ + tr->rdata->addr = listener->accept_data.remote; + tr->rdata->addr_len = listener->accept_data.addrlen; + + status = pj_ioqueue_read (mgr->ioqueue, tr->key, tr->rdata->packet, PJSIP_MAX_PKT_LEN); + if (status != PJ_IOQUEUE_PENDING) { + PJ_PERROR((tr->obj_name, "pj_ioqueue_read()")); + destroy_transport(mgr, tr); + goto on_return; + } + + pj_memcpy(&tr->remote_addr, &listener->accept_data.remote, listener->accept_data.addrlen); + hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); + init_key_from_transport(hash_key, tr); + + pj_mutex_lock(mgr->mutex); + pj_hash_set(tr->pool, mgr->transport_table, hash_key, sizeof(transport_key), tr); + pj_mutex_unlock(mgr->mutex); + +on_return: + /* Re-initiate asynchronous accept() */ + listener->accept_data.addrlen = sizeof(listener->accept_data.local); + status = pj_ioqueue_accept(mgr->ioqueue, listener->key, + &listener->accept_data.sock, + &listener->accept_data.local, + &listener->accept_data.remote, + &listener->accept_data.addrlen); + if (status != PJ_IOQUEUE_PENDING) { + PJ_PERROR((listener->obj_name, "pj_ioqueue_accept()")); + return; + } +} + +/* + * Handle completion of asynchronous connect() function. + * This function is called by the handle_events() function. + */ +static void handle_connect_completion( pjsip_transport_mgr *mgr, + pjsip_transport_t *tr, + pj_status_t status ) +{ + struct transport_callback new_list; + struct transport_callback *cb_rec; + + PJ_UNUSED_ARG(mgr) + + /* On connect completion, we must call all registered callbacks in + * the transport. + */ + + /* Initialize new list. */ + pj_list_init(&new_list); + + /* Hold transport's mutex. We don't want other thread to register a + * callback while we're dealing with it. + */ + pj_mutex_lock(tr->tr_mutex); + + /* Copy callback list to new list so that we can call the callbacks + * without holding the mutex. + */ + pj_list_merge_last(&new_list, &tr->cb_list); + + /* Clear transport's busy flag. */ + tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY; + + /* If success, update local address. + * Local address is only available after connect() has returned. + */ + if (status == PJ_SUCCESS) { + int addrlen = sizeof(tr->local_addr); + int rc; + if ((rc=pj_sock_getsockname(tr->sock, (pj_sockaddr_t*)&tr->local_addr, &addrlen)) == 0) { + pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name)); + } else { + PJ_LOG(4,(tr->obj_name, "Unable to get local address (getsockname=%d)", rc)); + } + } + + /* Unlock mutex. */ + pj_mutex_unlock(tr->tr_mutex); + + /* Call all registered callbacks. */ + cb_rec = new_list.next; + while (cb_rec != &new_list) { + struct transport_callback *next; + next = cb_rec->next; + (*cb_rec->cb)(tr, cb_rec->token, status); + cb_rec = next; + } + + /* Success? */ + if (status != PJ_SUCCESS) { + destroy_transport(mgr, tr); + return; + } + + /* Initiate read operation to socket. */ + status = pj_ioqueue_read (mgr->ioqueue, tr->key, tr->rdata->packet, PJSIP_MAX_PKT_LEN); + if (status != PJ_IOQUEUE_PENDING) { + PJ_PERROR((tr->obj_name, "pj_ioqueue_read()")); + destroy_transport(mgr, tr); + return; + } +} +#endif /* PJ_HAS_TCP */ + +/* + * Handle incoming data. + * This function is called when the transport manager receives 'notification' + * from the I/O Queue that the receive operation has completed. + * This function will then attempt to parse the message, and hands over the + * message to the endpoint. + */ +static void handle_received_data( pjsip_transport_mgr *mgr, + pjsip_transport_t *tr, + pj_ssize_t size ) +{ + pjsip_msg *msg; + pjsip_cid_hdr *call_id; + pjsip_rx_data *rdata = tr->rdata; + pj_pool_t *rdata_pool; + pjsip_hdr *hdr; + pj_str_t s; + char *src_addr; + int src_port; + pj_size_t msg_fragment_size = 0; + + /* Check size. */ + if (size < 1) { + if (tr->type != PJSIP_TRANSPORT_UDP) { + /* zero bytes indicates transport has been closed for TCP. + * But alas, we can't destroy it now since transactions may still + * have reference to it. In that case, just do nothing, the + * transaction will receive error when it tries to send anything. + * But alas!! UAC transactions wont send anything!!. + * So this is a bug! + */ + if (pj_atomic_get(tr->ref_cnt)==0) { + PJ_LOG(4,(tr->obj_name, "connection closed")); + destroy_transport(mgr, tr); + } else { + PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED); + //PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED); + } + return; + } else { + /* On Windows machines, UDP recv() will return zero upon receiving + * ICMP port unreachable message. + */ + PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)")); + goto on_return; + } + } + + /* Save received time. */ + pj_gettimeofday(&rdata->timestamp); + + /* Update length. */ + rdata->len += size; + + /* Null terminate packet, this is the requirement of the parser. */ + rdata->packet[rdata->len] = '\0'; + + /* Get source address and port for logging purpose. */ + src_addr = pj_sockaddr_get_str_addr(&rdata->addr); + src_port = pj_sockaddr_get_port(&rdata->addr); + + /* Print the whole data to the log. */ + PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n" + "----------- begin msg ------------\n" + "%s" + "------------ end msg -------------", + rdata->len, src_addr, src_port, rdata->packet)); + + + /* Process all message fragments. */ + while (rdata->len > 0) { + + msg_fragment_size = rdata->len; +#if PJ_HAS_TCP + /* For TCP transport, check if the whole message has been received. */ + if (tr->type != PJSIP_TRANSPORT_UDP) { + pj_bool_t is_complete; + is_complete = pjsip_find_msg(rdata->packet, rdata->len, PJ_FALSE, &msg_fragment_size); + if (!is_complete) { + if (rdata->len == PJSIP_MAX_PKT_LEN) { + PJ_LOG(1,(tr->obj_name, + "Transport buffer full (%d bytes) for TCP socket %s:%d " + "(probably too many invalid fragments received). " + "Buffer will be discarded.", + PJSIP_MAX_PKT_LEN, src_addr, src_port)); + goto on_return; + } else { + goto tcp_read_packet; + } + } + } +#endif + + /* Clear parser error report */ + pj_list_init(&rdata->parse_err); + + /* Parse the message. */ + PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size, + src_addr, src_port)); + + msg = pjsip_parse_msg( rdata->pool, rdata->packet, msg_fragment_size, + &rdata->parse_err); + if (msg == NULL) { + PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size, + src_addr, src_port)); + goto finish_process_fragment; + } + + /* Attach newly created message to rdata. */ + rdata->msg = msg; + + /* Extract Call-ID, From and To header and tags, topmost Via, and CSeq + * header from the message. + */ + call_id = pjsip_msg_find_hdr( msg, PJSIP_H_CALL_ID, NULL); + rdata->from = pjsip_msg_find_hdr( msg, PJSIP_H_FROM, NULL); + rdata->to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL); + rdata->via = pjsip_msg_find_hdr( msg, PJSIP_H_VIA, NULL); + rdata->cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL); + + if (call_id == NULL || rdata->from == NULL || rdata->to == NULL || + rdata->via == NULL || rdata->cseq == NULL) + { + PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header", + src_addr, src_port)); + goto finish_process_fragment; + } + rdata->call_id = call_id->id; + rdata->from_tag = rdata->from->tag; + rdata->to_tag = rdata->to->tag; + + /* If message is received from address that's different from the sent-by, + * MUST add received parameter to the via. + * In our case, we add Via receive param for EVERY received message, + * because it saves us from resolving the host HERE in case sent-by is in + * FQDN format. And it doesn't hurt either. + */ + s = pj_str(src_addr); + pj_strdup(rdata->pool, &rdata->via->recvd_param, &s); + + /* RFC 3581: + * If message contains "rport" param, put the received port there. + */ + if (rdata->via->rport_param == 0) { + rdata->via->rport_param = pj_sockaddr_get_port(&rdata->addr); + } + + /* Drop response message if it has more than one Via. + */ + if (msg->type == PJSIP_RESPONSE_MSG) { + hdr = (pjsip_hdr*)rdata->via->next; + if (hdr) { + hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr); + if (hdr) { + PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: " + "multiple Via in response message", + src_addr, src_port)); + goto finish_process_fragment; + } + } + } + + /* Call the transport manager's upstream message callback. + */ + (*mgr->message_callback)(mgr->endpt, rdata); + +finish_process_fragment: + rdata->len -= msg_fragment_size; + if (rdata->len > 0) { + pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len); + PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len)); + } + + } /* while (rdata->len > 0) */ + +on_return: + /* Reset the pool and rdata */ + rdata_pool = rdata->pool; + pj_pool_reset(rdata_pool); + rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) ); + rdata->len = 0; + rdata->transport = tr; + rdata->pool = rdata_pool; + tr->rdata = rdata; + + /* Read the next packet. */ + rdata->addr_len = sizeof(rdata->addr); + if (tr->type == PJSIP_TRANSPORT_UDP) { + pj_ioqueue_recvfrom( tr->mgr->ioqueue, tr->key, + tr->rdata->packet, PJSIP_MAX_PKT_LEN, + &rdata->addr, &rdata->addr_len); + } + +#if PJ_HAS_TCP + /* The next 'if' should have been 'else if', but we need to put the + label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning. + */ +tcp_read_packet: + if (tr->type == PJSIP_TRANSPORT_TCP) { + pj_ioqueue_read( tr->mgr->ioqueue, tr->key, + tr->rdata->packet + tr->rdata->len, + PJSIP_MAX_PKT_LEN - tr->rdata->len); + } +#endif +} + +static void transport_mgr_on_idle( pjsip_transport_mgr *mgr ) +{ + pj_time_val now; + pj_hash_iterator_t itr_val; + pj_hash_iterator_t *itr; + + + /* Get time for comparing transport's close time. */ + pj_gettimeofday(&now); + if (now.sec < mgr->next_idle_check.sec) { + return; + } + + /* Acquire transport manager's lock. */ + pj_mutex_lock(mgr->mutex); + + /* Update next idle check. */ + mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL; + + /* Iterate all transports, and close transports that are not used for + some periods. + */ + itr = pjsip_transport_first(mgr, &itr_val); + while (itr != NULL) { + pj_hash_iterator_t *next; + pjsip_transport_t *transport; + + transport = pjsip_transport_this(mgr, itr); + + next = pjsip_transport_next(mgr, itr); + + if (pj_atomic_get(transport->ref_cnt)==0 && + PJ_TIME_VAL_LTE(transport->close_time, now)) + { + destroy_transport(mgr, transport); + } + + itr = next; + } + + /* Release transport manager's lock. */ + pj_mutex_unlock(mgr->mutex); +} + +static void on_ioqueue_read(pj_ioqueue_key_t *key, pj_ssize_t bytes_read) +{ + pjsip_transport_t *t; + t = pj_ioqueue_get_user_data(key); + + handle_received_data( t->mgr, t, bytes_read ); +} + +static void on_ioqueue_write(pj_ioqueue_key_t *key, pj_ssize_t bytes_sent) +{ + PJ_UNUSED_ARG(key) + PJ_UNUSED_ARG(bytes_sent) + + /* Completion of write operation. + * Do nothing. + */ +} + +static void on_ioqueue_accept(pj_ioqueue_key_t *key, int status) +{ +#if PJ_HAS_TCP + pjsip_transport_t *t; + t = pj_ioqueue_get_user_data(key); + + handle_new_connection( t->mgr, t, status ); +#else + PJ_UNUSED_ARG(key) + PJ_UNUSED_ARG(status) +#endif +} + +static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status) +{ +#if PJ_HAS_TCP + pjsip_transport_t *t; + t = pj_ioqueue_get_user_data(key); + + handle_connect_completion( t->mgr, t, status); +#else + PJ_UNUSED_ARG(key) + PJ_UNUSED_ARG(status) +#endif +} + + +/* + * Poll for events. + */ +PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr, + const pj_time_val *req_timeout ) +{ + int event_count; + int break_loop; + int result; + pj_time_val timeout; + + PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()")); + + event_count = 0; + break_loop = 0; + timeout = *req_timeout; + do { + result = pj_ioqueue_poll( mgr->ioqueue, &timeout); + if (result == 1) { + ++event_count; + + /* Break the loop. */ + //if (timeout.msec==0 && timeout.sec==0) { + break_loop = 1; + //} + + } else { + /* On idle, cleanup transport. */ + transport_mgr_on_idle(mgr); + + break_loop = 1; + } + timeout.sec = timeout.msec = 0; + } while (!break_loop); + + return event_count; +} + + +PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr, + pj_hash_iterator_t *it ) +{ + return pj_hash_first(mgr->transport_table, it); +} + +PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr, + pj_hash_iterator_t *itr ) +{ + return pj_hash_next(mgr->transport_table, itr); +} + +PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr, + pj_hash_iterator_t *itr ) +{ + return pj_hash_this(mgr->transport_table, itr); +} diff --git a/pjsip/src/pjsip/sip_transport.h b/pjsip/src/pjsip/sip_transport.h index 9b1afb957..71fae8ae6 100644 --- a/pjsip/src/pjsip/sip_transport.h +++ b/pjsip/src/pjsip/sip_transport.h @@ -1,457 +1,457 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transport.h 11 10/14/05 12:23a Bennylp $ */ -#ifndef __PJSIP_SIP_TRANSPORT_H__ -#define __PJSIP_SIP_TRANSPORT_H__ - -/** - * @file sip_transport.h - * @brief SIP Transport - */ - -#include -#include -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_TRANSPORT SIP Transport - * @ingroup PJSIP - * - * This is the low-level transport layer. Application normally won't need to - * use this function, but instead can use transaction or higher layer API to - * send and receive messages. - * - * @{ - */ - -/** - * Incoming message buffer. - * This structure keep all the information regarding the received message. This - * buffer lifetime is only very short, normally after the transaction has been - * called, this buffer will be deleted/recycled. So care must be taken when - * allocating storage from the pool of this buffer. - */ -struct pjsip_rx_data -{ - PJ_DECL_LIST_MEMBER(struct pjsip_rx_data) - - /** Memory pool for this buffer. */ - pj_pool_t *pool; - - /** Time when the message was received. */ - pj_time_val timestamp; - - /** The packet buffer. */ - char packet[PJSIP_MAX_PKT_LEN]; - - /** The length of the packet received. */ - int len; - - /** The source address from which the packet was received. */ - pj_sockaddr_in addr; - - /** The length of the source address. */ - int addr_len; - - /** The transport object which received this packet. */ - pjsip_transport_t *transport; - - /** The parsed message, if any. */ - pjsip_msg *msg; - - /** This the transaction key generated from the message. This key is only - * available after the rdata has reached the endpoint. - */ - pj_str_t key; - - /** The Call-ID header as found in the message. */ - pj_str_t call_id; - - /** The From header as found in the message. */ - pjsip_from_hdr *from; - - /** The tag in the From header as found in the message. */ - pj_str_t from_tag; - - /** The To header as found in the message. */ - pjsip_to_hdr *to; - - /** The To tag header as found in the message. */ - pj_str_t to_tag; - - /** The topmost Via header as found in the message. */ - pjsip_via_hdr *via; - - /** The CSeq header as found in the message. */ - pjsip_cseq_hdr *cseq; - - /** The list of error generated by the parser when parsing this message. */ - pjsip_parser_err_report parse_err; -}; - - -/** - * Data structure for sending outgoing message. Application normally creates - * this buffer by calling #pjsip_endpt_create_tdata. - * - * The lifetime of this buffer is controlled by the reference counter in this - * structure, which is manipulated by calling #pjsip_tx_data_add_ref and - * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then - * this buffer will be destroyed. - * - * A transaction object normally will add reference counter to this buffer - * when application calls #pjsip_tsx_on_tx_msg, because it needs to keep the - * message for retransmission. The transaction will release the reference - * counter once its state has reached final state. - */ -struct pjsip_tx_data -{ - PJ_DECL_LIST_MEMBER(struct pjsip_tx_data) - - /** Memory pool for this buffer. */ - pj_pool_t *pool; - - /** A name to identify this buffer. */ - char obj_name[PJ_MAX_OBJ_NAME]; - - /** For response message, this contains the reference to timestamp when - * the original request message was received. The value of this field - * is set when application creates response message to a request by - * calling #pjsip_endpt_create_response. - */ - pj_time_val rx_timestamp; - - /** The transport manager for this buffer. */ - pjsip_transport_mgr *mgr; - - /** The message in this buffer. */ - pjsip_msg *msg; - - /** Buffer to the printed text representation of the message. When the - * content of this buffer is set, then the transport will send the content - * of this buffer instead of re-printing the message structure. If the - * message structure has changed, then application must invalidate this - * buffer by calling #pjsip_tx_data_invalidate_msg. - */ - pjsip_buffer buf; - - /** Reference counter. */ - pj_atomic_t *ref_cnt; -}; - - -/** - * Add reference counter to the transmit buffer. The reference counter controls - * the life time of the buffer, ie. when the counter reaches zero, then it - * will be destroyed. - * - * @param tdata The transmit buffer. - */ -PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ); - -/** - * Decrement reference counter of the transmit buffer. - * When the transmit buffer is no longer used, it will be destroyed. - * - * @param tdata The transmit buffer data. - */ -PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ); - -/** - * Invalidate the print buffer to force message to be re-printed. Call - * when the message has changed after it has been printed to buffer. The - * message is printed to buffer normally by transport when it is about to be - * sent to the wire. Subsequent sending of the message will not cause - * the message to be re-printed, unless application invalidates the buffer - * by calling this function. - * - * @param tdata The transmit buffer. - */ -PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ); - - -/** - * Flags for SIP transports. - */ -enum pjsip_transport_flags_e -{ - PJSIP_TRANSPORT_RELIABLE = 1, /**< Transport is reliable. */ - PJSIP_TRANSPORT_SECURE = 2, /**< Transport is secure. */ - PJSIP_TRANSPORT_IOQUEUE_BUSY = 4, /**< WTH?? */ -}; - -/** - * Get the transport type from the transport name. - * - * @param name Transport name, such as "TCP", or "UDP". - * - * @return The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if - * the name is not recognized as the name of supported - * transport. - */ -PJ_DECL(pjsip_transport_type_e) -pjsip_transport_get_type_from_name(const pj_str_t *name); - -/** - * Get the transport type for the specified flags. - * - * @param flag The transport flag. - * - * @return Transport type. - */ -PJ_DECL(pjsip_transport_type_e) -pjsip_transport_get_type_from_flag(unsigned flag); - -/** - * Get the default SIP port number for the specified type. - * - * @param type Transport type. - * - * @return The port number, which is the default SIP port number for - * the specified type. - */ -PJ_DECL(int) -pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type); - - -/** - * Add reference to transport. - * Transactions or dialogs that uses a particular transport must call this - * function to indicate that the transport is being used, thus preventing the - * transport from being closed. - * - * @param transport The transport. - */ -PJ_DECL(void) -pjsip_transport_add_ref( pjsip_transport_t *transport ); - -/** - * Decrease reference to transport. - * When the transport reference counter becomes zero, a timer will be started - * and when this timer expires and the reference counter is still zero, the - * transport will be released. - * - * @param transport The transport - */ -PJ_DECL(void) -pjsip_transport_dec_ref( pjsip_transport_t *transport ); - - -/** - * Macro to check whether the transport is reliable. - * - * @param transport The transport - * - * @return non-zero (not necessarily 1) if transport is reliable. - */ -#define PJSIP_TRANSPORT_IS_RELIABLE(transport) \ - (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_RELIABLE) - - -/** - * Macro to check whether the transport is secure. - * - * @param transport The transport - * - * @return non-zero (not necessarily one) if transport is secure. - */ -#define PJSIP_TRANSPORT_IS_SECURE(transport) \ - (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_SECURE) - -/** - * Get the transport type. - * - * @param tr The transport. - * - * @return Transport type. - */ -PJ_DECL(pjsip_transport_type_e) -pjsip_transport_get_type( const pjsip_transport_t * tr); - -/** - * Get the transport type name (ie "UDP", or "TCP"). - * - * @param tr The transport. - * @return The string type. - */ -PJ_DECL(const char *) -pjsip_transport_get_type_name( const pjsip_transport_t * tr); - -/** - * Get the transport's object name. - * - * @param tr The transport. - * @return The object name. - */ -PJ_DECL(const char*) -pjsip_transport_get_obj_name( const pjsip_transport_t *tr ); - -/** - * Get the transport's reference counter. - * - * @param tr The transport. - * @return The reference count value. - */ -PJ_DECL(int) -pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr ); - -/** - * Get transport flag. - * - * @param tr The transport. - * @return Transport flag. - */ -PJ_DECL(unsigned) -pjsip_transport_get_flag( const pjsip_transport_t * tr ); - -/** - * Get the local address of the transport, ie. the address which the socket - * is bound. - * - * @param tr The transport. - * @return The address. - */ -PJ_DECL(const pj_sockaddr_in *) -pjsip_transport_get_local_addr( pjsip_transport_t * tr ); - -/** - * Get the address name of the transport. Address name can be an arbitrary - * address assigned to a transport. This is usefull for example when STUN - * is used, then the address name of an UDP transport can specify the public - * address of the transport. When the address name is not set, then value - * will be equal to the local/bound address. Application should normally - * prefer to use the address name instead of the local address. - * - * @param tr The transport. - * @return The address name. - */ -PJ_DECL(const pj_sockaddr_in*) -pjsip_transport_get_addr_name (pjsip_transport_t *tr); - -/** - * Get the remote address of the transport. Not all transports will have - * a valid remote address. UDP transports, for example, will likely to have - * zero has their remote address, because UDP transport can be used to send - * and receive messages from multiple destinations. - * - * @param tr The transport. - * @return The address. - */ -PJ_DECL(const pj_sockaddr_in *) -pjsip_transport_get_remote_addr( const pjsip_transport_t * tr ); - -/** - * Send a SIP message using the specified transport, to the address specified - * in the outgoing data. This function is only usefull for application when it - * wants to handle the message statelessly, because otherwise it should create - * a transaction and let the transaction handles the transmission of the - * message. - * - * This function will send the message immediately, so application must be - * sure that the transport is ready to do so before calling this function. - * - * @param tr The transport to send the message. - * @param tdata The outgoing message buffer. - * @param addr The remote address. - * - * @return The number of bytes sent, or zero if the connection - * has closed, or -1 on error. - */ -PJ_DECL(int) pjsip_transport_send_msg( pjsip_transport_t *tr, - pjsip_tx_data *tdata, - const pj_sockaddr_in *addr); - - -/** - * @} - */ - -/* - * PRIVATE FUNCTIONS!!! - * - * These functions are normally to be used by endpoint. Application should - * use the variant provided by the endpoint instance. - * - * Application normally wouldn't be able to call these functions because it - * has no reference of the transport manager (the instance of the transport - * manager is hidden by endpoint!). - */ - -/* - * Create a new transmit buffer. - * - * @param mgr The transport manager. - * @return The transmit buffer data, or NULL on error. - */ -pjsip_tx_data* pjsip_tx_data_create( pjsip_transport_mgr *mgr ); - - -/** - * Create listener. - * - * @param mgr The transport manager. - * @param type Transport type. - * @param local_addr The address to bind. - * @param addr_name If not null, sets the address name. If NULL, - * then the local address will be used. - * - * @return PJ_SUCCESS if listener was successfully created. - */ -PJ_DECL(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr, - pjsip_transport_type_e type, - pj_sockaddr_in *local_addr, - const pj_sockaddr_in *addr_name); - - -/** - * Create UDP listener. - * - * @param mgr The transport manager. - * @param sock The UDP socket. - * @param addr_name If not null, sets the address name. If NULL, - * then the local address will be used. - * - * @return PJ_SUCCESS if listener was successfully created. - */ -PJ_DECL(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr, - pj_sock_t sock, - const pj_sockaddr_in *addr_name); - -/** - * Type of function to receive asynchronous transport completion for - * pjsip_transport_get() operation. - * - * @param tr The transport. - * @param token Token registered previously. - * @param status Status of operation. - */ -typedef void pjsip_transport_completion_callback(pjsip_transport_t *tr, - void *token, - pj_status_t status); - -/** - * Find transport to be used to send message to remote destination. If no - * suitable transport is found, a new one will be created. If transport - * can not be available immediately (for example, an outgoing TCP connec()), - * then the caller will be notified later via the callback. - * - * @param mgr The transport manager. - * @param pool Pool to allocate asychronous job (if required). - * @param type The transport type. - * @param remote The remote address. - * @param token The token that will be passed to the callback. - * @param cb The callback to be called to report the completion of - * the operation. - */ -PJ_DECL(void) pjsip_transport_get( pjsip_transport_mgr *mgr, - pj_pool_t *pool, - pjsip_transport_type_e type, - const pj_sockaddr_in *remote, - void *token, - pjsip_transport_completion_callback *cb); - -PJ_END_DECL - -#endif /* __PJSIP_SIP_TRANSPORT_H__ */ - +/* $Header: /pjproject-0.3/pjsip/src/pjsip/sip_transport.h 11 10/14/05 12:23a Bennylp $ */ +#ifndef __PJSIP_SIP_TRANSPORT_H__ +#define __PJSIP_SIP_TRANSPORT_H__ + +/** + * @file sip_transport.h + * @brief SIP Transport + */ + +#include +#include +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_TRANSPORT SIP Transport + * @ingroup PJSIP + * + * This is the low-level transport layer. Application normally won't need to + * use this function, but instead can use transaction or higher layer API to + * send and receive messages. + * + * @{ + */ + +/** + * Incoming message buffer. + * This structure keep all the information regarding the received message. This + * buffer lifetime is only very short, normally after the transaction has been + * called, this buffer will be deleted/recycled. So care must be taken when + * allocating storage from the pool of this buffer. + */ +struct pjsip_rx_data +{ + PJ_DECL_LIST_MEMBER(struct pjsip_rx_data) + + /** Memory pool for this buffer. */ + pj_pool_t *pool; + + /** Time when the message was received. */ + pj_time_val timestamp; + + /** The packet buffer. */ + char packet[PJSIP_MAX_PKT_LEN]; + + /** The length of the packet received. */ + int len; + + /** The source address from which the packet was received. */ + pj_sockaddr_in addr; + + /** The length of the source address. */ + int addr_len; + + /** The transport object which received this packet. */ + pjsip_transport_t *transport; + + /** The parsed message, if any. */ + pjsip_msg *msg; + + /** This the transaction key generated from the message. This key is only + * available after the rdata has reached the endpoint. + */ + pj_str_t key; + + /** The Call-ID header as found in the message. */ + pj_str_t call_id; + + /** The From header as found in the message. */ + pjsip_from_hdr *from; + + /** The tag in the From header as found in the message. */ + pj_str_t from_tag; + + /** The To header as found in the message. */ + pjsip_to_hdr *to; + + /** The To tag header as found in the message. */ + pj_str_t to_tag; + + /** The topmost Via header as found in the message. */ + pjsip_via_hdr *via; + + /** The CSeq header as found in the message. */ + pjsip_cseq_hdr *cseq; + + /** The list of error generated by the parser when parsing this message. */ + pjsip_parser_err_report parse_err; +}; + + +/** + * Data structure for sending outgoing message. Application normally creates + * this buffer by calling #pjsip_endpt_create_tdata. + * + * The lifetime of this buffer is controlled by the reference counter in this + * structure, which is manipulated by calling #pjsip_tx_data_add_ref and + * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then + * this buffer will be destroyed. + * + * A transaction object normally will add reference counter to this buffer + * when application calls #pjsip_tsx_on_tx_msg, because it needs to keep the + * message for retransmission. The transaction will release the reference + * counter once its state has reached final state. + */ +struct pjsip_tx_data +{ + PJ_DECL_LIST_MEMBER(struct pjsip_tx_data) + + /** Memory pool for this buffer. */ + pj_pool_t *pool; + + /** A name to identify this buffer. */ + char obj_name[PJ_MAX_OBJ_NAME]; + + /** For response message, this contains the reference to timestamp when + * the original request message was received. The value of this field + * is set when application creates response message to a request by + * calling #pjsip_endpt_create_response. + */ + pj_time_val rx_timestamp; + + /** The transport manager for this buffer. */ + pjsip_transport_mgr *mgr; + + /** The message in this buffer. */ + pjsip_msg *msg; + + /** Buffer to the printed text representation of the message. When the + * content of this buffer is set, then the transport will send the content + * of this buffer instead of re-printing the message structure. If the + * message structure has changed, then application must invalidate this + * buffer by calling #pjsip_tx_data_invalidate_msg. + */ + pjsip_buffer buf; + + /** Reference counter. */ + pj_atomic_t *ref_cnt; +}; + + +/** + * Add reference counter to the transmit buffer. The reference counter controls + * the life time of the buffer, ie. when the counter reaches zero, then it + * will be destroyed. + * + * @param tdata The transmit buffer. + */ +PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ); + +/** + * Decrement reference counter of the transmit buffer. + * When the transmit buffer is no longer used, it will be destroyed. + * + * @param tdata The transmit buffer data. + */ +PJ_DECL(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ); + +/** + * Invalidate the print buffer to force message to be re-printed. Call + * when the message has changed after it has been printed to buffer. The + * message is printed to buffer normally by transport when it is about to be + * sent to the wire. Subsequent sending of the message will not cause + * the message to be re-printed, unless application invalidates the buffer + * by calling this function. + * + * @param tdata The transmit buffer. + */ +PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ); + + +/** + * Flags for SIP transports. + */ +enum pjsip_transport_flags_e +{ + PJSIP_TRANSPORT_RELIABLE = 1, /**< Transport is reliable. */ + PJSIP_TRANSPORT_SECURE = 2, /**< Transport is secure. */ + PJSIP_TRANSPORT_IOQUEUE_BUSY = 4, /**< WTH?? */ +}; + +/** + * Get the transport type from the transport name. + * + * @param name Transport name, such as "TCP", or "UDP". + * + * @return The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if + * the name is not recognized as the name of supported + * transport. + */ +PJ_DECL(pjsip_transport_type_e) +pjsip_transport_get_type_from_name(const pj_str_t *name); + +/** + * Get the transport type for the specified flags. + * + * @param flag The transport flag. + * + * @return Transport type. + */ +PJ_DECL(pjsip_transport_type_e) +pjsip_transport_get_type_from_flag(unsigned flag); + +/** + * Get the default SIP port number for the specified type. + * + * @param type Transport type. + * + * @return The port number, which is the default SIP port number for + * the specified type. + */ +PJ_DECL(int) +pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type); + + +/** + * Add reference to transport. + * Transactions or dialogs that uses a particular transport must call this + * function to indicate that the transport is being used, thus preventing the + * transport from being closed. + * + * @param transport The transport. + */ +PJ_DECL(void) +pjsip_transport_add_ref( pjsip_transport_t *transport ); + +/** + * Decrease reference to transport. + * When the transport reference counter becomes zero, a timer will be started + * and when this timer expires and the reference counter is still zero, the + * transport will be released. + * + * @param transport The transport + */ +PJ_DECL(void) +pjsip_transport_dec_ref( pjsip_transport_t *transport ); + + +/** + * Macro to check whether the transport is reliable. + * + * @param transport The transport + * + * @return non-zero (not necessarily 1) if transport is reliable. + */ +#define PJSIP_TRANSPORT_IS_RELIABLE(transport) \ + (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_RELIABLE) + + +/** + * Macro to check whether the transport is secure. + * + * @param transport The transport + * + * @return non-zero (not necessarily one) if transport is secure. + */ +#define PJSIP_TRANSPORT_IS_SECURE(transport) \ + (pjsip_transport_get_flag(transport) & PJSIP_TRANSPORT_SECURE) + +/** + * Get the transport type. + * + * @param tr The transport. + * + * @return Transport type. + */ +PJ_DECL(pjsip_transport_type_e) +pjsip_transport_get_type( const pjsip_transport_t * tr); + +/** + * Get the transport type name (ie "UDP", or "TCP"). + * + * @param tr The transport. + * @return The string type. + */ +PJ_DECL(const char *) +pjsip_transport_get_type_name( const pjsip_transport_t * tr); + +/** + * Get the transport's object name. + * + * @param tr The transport. + * @return The object name. + */ +PJ_DECL(const char*) +pjsip_transport_get_obj_name( const pjsip_transport_t *tr ); + +/** + * Get the transport's reference counter. + * + * @param tr The transport. + * @return The reference count value. + */ +PJ_DECL(int) +pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr ); + +/** + * Get transport flag. + * + * @param tr The transport. + * @return Transport flag. + */ +PJ_DECL(unsigned) +pjsip_transport_get_flag( const pjsip_transport_t * tr ); + +/** + * Get the local address of the transport, ie. the address which the socket + * is bound. + * + * @param tr The transport. + * @return The address. + */ +PJ_DECL(const pj_sockaddr_in *) +pjsip_transport_get_local_addr( pjsip_transport_t * tr ); + +/** + * Get the address name of the transport. Address name can be an arbitrary + * address assigned to a transport. This is usefull for example when STUN + * is used, then the address name of an UDP transport can specify the public + * address of the transport. When the address name is not set, then value + * will be equal to the local/bound address. Application should normally + * prefer to use the address name instead of the local address. + * + * @param tr The transport. + * @return The address name. + */ +PJ_DECL(const pj_sockaddr_in*) +pjsip_transport_get_addr_name (pjsip_transport_t *tr); + +/** + * Get the remote address of the transport. Not all transports will have + * a valid remote address. UDP transports, for example, will likely to have + * zero has their remote address, because UDP transport can be used to send + * and receive messages from multiple destinations. + * + * @param tr The transport. + * @return The address. + */ +PJ_DECL(const pj_sockaddr_in *) +pjsip_transport_get_remote_addr( const pjsip_transport_t * tr ); + +/** + * Send a SIP message using the specified transport, to the address specified + * in the outgoing data. This function is only usefull for application when it + * wants to handle the message statelessly, because otherwise it should create + * a transaction and let the transaction handles the transmission of the + * message. + * + * This function will send the message immediately, so application must be + * sure that the transport is ready to do so before calling this function. + * + * @param tr The transport to send the message. + * @param tdata The outgoing message buffer. + * @param addr The remote address. + * + * @return The number of bytes sent, or zero if the connection + * has closed, or -1 on error. + */ +PJ_DECL(int) pjsip_transport_send_msg( pjsip_transport_t *tr, + pjsip_tx_data *tdata, + const pj_sockaddr_in *addr); + + +/** + * @} + */ + +/* + * PRIVATE FUNCTIONS!!! + * + * These functions are normally to be used by endpoint. Application should + * use the variant provided by the endpoint instance. + * + * Application normally wouldn't be able to call these functions because it + * has no reference of the transport manager (the instance of the transport + * manager is hidden by endpoint!). + */ + +/* + * Create a new transmit buffer. + * + * @param mgr The transport manager. + * @return The transmit buffer data, or NULL on error. + */ +pjsip_tx_data* pjsip_tx_data_create( pjsip_transport_mgr *mgr ); + + +/** + * Create listener. + * + * @param mgr The transport manager. + * @param type Transport type. + * @param local_addr The address to bind. + * @param addr_name If not null, sets the address name. If NULL, + * then the local address will be used. + * + * @return PJ_SUCCESS if listener was successfully created. + */ +PJ_DECL(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr, + pjsip_transport_type_e type, + pj_sockaddr_in *local_addr, + const pj_sockaddr_in *addr_name); + + +/** + * Create UDP listener. + * + * @param mgr The transport manager. + * @param sock The UDP socket. + * @param addr_name If not null, sets the address name. If NULL, + * then the local address will be used. + * + * @return PJ_SUCCESS if listener was successfully created. + */ +PJ_DECL(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr, + pj_sock_t sock, + const pj_sockaddr_in *addr_name); + +/** + * Type of function to receive asynchronous transport completion for + * pjsip_transport_get() operation. + * + * @param tr The transport. + * @param token Token registered previously. + * @param status Status of operation. + */ +typedef void pjsip_transport_completion_callback(pjsip_transport_t *tr, + void *token, + pj_status_t status); + +/** + * Find transport to be used to send message to remote destination. If no + * suitable transport is found, a new one will be created. If transport + * can not be available immediately (for example, an outgoing TCP connec()), + * then the caller will be notified later via the callback. + * + * @param mgr The transport manager. + * @param pool Pool to allocate asychronous job (if required). + * @param type The transport type. + * @param remote The remote address. + * @param token The token that will be passed to the callback. + * @param cb The callback to be called to report the completion of + * the operation. + */ +PJ_DECL(void) pjsip_transport_get( pjsip_transport_mgr *mgr, + pj_pool_t *pool, + pjsip_transport_type_e type, + const pj_sockaddr_in *remote, + void *token, + pjsip_transport_completion_callback *cb); + +PJ_END_DECL + +#endif /* __PJSIP_SIP_TRANSPORT_H__ */ + diff --git a/pjsip/src/pjsip/sip_types.h b/pjsip/src/pjsip/sip_types.h index f103019b7..9ed7d484b 100644 --- a/pjsip/src/pjsip/sip_types.h +++ b/pjsip/src/pjsip/sip_types.h @@ -1,136 +1,136 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_types.h 5 6/19/05 6:12p Bennylp $ */ -#ifndef __PJSIP_SIP_TYPES_H__ -#define __PJSIP_SIP_TYPES_H__ - -#include -#include - -/** - * Opaque data structure for transports (sip_transport.h). - */ -typedef struct pjsip_transport_t pjsip_transport_t; - -/** - * Opaque data type for transport manager (sip_transport.h). - */ -typedef struct pjsip_transport_mgr pjsip_transport_mgr; - -/** - * Transport types. - */ -typedef enum pjsip_transport_type_e -{ - /** Unspecified. */ - PJSIP_TRANSPORT_UNSPECIFIED, - - /** UDP. */ - PJSIP_TRANSPORT_UDP, - -#if PJ_HAS_TCP - /** TCP. */ - PJSIP_TRANSPORT_TCP, - - /** TLS. */ - PJSIP_TRANSPORT_TLS, - - /** SCTP. */ - PJSIP_TRANSPORT_SCTP, -#endif - -} pjsip_transport_type_e; - - -/** - * Forward declaration for endpoint (sip_endpoint.h). - */ -typedef struct pjsip_endpoint pjsip_endpoint; - -/** - * Forward declaration for transactions (sip_transaction.h). - */ -typedef struct pjsip_transaction pjsip_transaction; - -/** - * Forward declaration for events (sip_event.h). - */ -typedef struct pjsip_event pjsip_event; - -/** - * Forward declaration for transmit data/buffer (sip_transport.h). - */ -typedef struct pjsip_tx_data pjsip_tx_data; - -/** - * Forward declaration for receive data/buffer (sip_transport.h). - */ -typedef struct pjsip_rx_data pjsip_rx_data; - -/** - * Forward declaration for message (sip_msg.h). - */ -typedef struct pjsip_msg pjsip_msg; - -/** - * Forward declaration for URI (sip_uri.h). - */ -typedef struct pjsip_uri pjsip_uri; - -/** - * Opaque data type for the resolver engine (sip_resolve.h). - */ -typedef struct pjsip_resolver_t pjsip_resolver_t; - -/** - * Forward declaration for credential. - */ -typedef struct pjsip_cred_info pjsip_cred_info; - - -/** - * Forward declaration for module (sip_module.h). - */ -typedef struct pjsip_module pjsip_module; - -/** - * Transaction role. - */ -typedef enum pjsip_role_e -{ - PJSIP_ROLE_UAC, /**< Transaction role is UAC. */ - PJSIP_ROLE_UAS, /**< Transaction role is UAS. */ -} pjsip_role_e; - - -/** - * General purpose buffer. - */ -typedef struct pjsip_buffer -{ - /** The start of the buffer. */ - char *start; - - /** Pointer to current end of the buffer, which also indicates the position - of subsequent buffer write. - */ - char *cur; - - /** The absolute end of the buffer. */ - char *end; - -} pjsip_buffer; - - -/** - * General host:port pair, used for example as Via sent-by. - */ -typedef struct pjsip_host_port -{ - unsigned flag; /**< Flags of pjsip_transport_flags_e (not used in Via). */ - unsigned type; /**< Transport type (pjsip_transport_type_e), or zero. */ - pj_str_t host; /**< Host part. */ - int port; /**< Port number. */ -} pjsip_host_port; - - -#endif /* __PJSIP_SIP_TYPES_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_types.h 5 6/19/05 6:12p Bennylp $ */ +#ifndef __PJSIP_SIP_TYPES_H__ +#define __PJSIP_SIP_TYPES_H__ + +#include +#include + +/** + * Opaque data structure for transports (sip_transport.h). + */ +typedef struct pjsip_transport_t pjsip_transport_t; + +/** + * Opaque data type for transport manager (sip_transport.h). + */ +typedef struct pjsip_transport_mgr pjsip_transport_mgr; + +/** + * Transport types. + */ +typedef enum pjsip_transport_type_e +{ + /** Unspecified. */ + PJSIP_TRANSPORT_UNSPECIFIED, + + /** UDP. */ + PJSIP_TRANSPORT_UDP, + +#if PJ_HAS_TCP + /** TCP. */ + PJSIP_TRANSPORT_TCP, + + /** TLS. */ + PJSIP_TRANSPORT_TLS, + + /** SCTP. */ + PJSIP_TRANSPORT_SCTP, +#endif + +} pjsip_transport_type_e; + + +/** + * Forward declaration for endpoint (sip_endpoint.h). + */ +typedef struct pjsip_endpoint pjsip_endpoint; + +/** + * Forward declaration for transactions (sip_transaction.h). + */ +typedef struct pjsip_transaction pjsip_transaction; + +/** + * Forward declaration for events (sip_event.h). + */ +typedef struct pjsip_event pjsip_event; + +/** + * Forward declaration for transmit data/buffer (sip_transport.h). + */ +typedef struct pjsip_tx_data pjsip_tx_data; + +/** + * Forward declaration for receive data/buffer (sip_transport.h). + */ +typedef struct pjsip_rx_data pjsip_rx_data; + +/** + * Forward declaration for message (sip_msg.h). + */ +typedef struct pjsip_msg pjsip_msg; + +/** + * Forward declaration for URI (sip_uri.h). + */ +typedef struct pjsip_uri pjsip_uri; + +/** + * Opaque data type for the resolver engine (sip_resolve.h). + */ +typedef struct pjsip_resolver_t pjsip_resolver_t; + +/** + * Forward declaration for credential. + */ +typedef struct pjsip_cred_info pjsip_cred_info; + + +/** + * Forward declaration for module (sip_module.h). + */ +typedef struct pjsip_module pjsip_module; + +/** + * Transaction role. + */ +typedef enum pjsip_role_e +{ + PJSIP_ROLE_UAC, /**< Transaction role is UAC. */ + PJSIP_ROLE_UAS, /**< Transaction role is UAS. */ +} pjsip_role_e; + + +/** + * General purpose buffer. + */ +typedef struct pjsip_buffer +{ + /** The start of the buffer. */ + char *start; + + /** Pointer to current end of the buffer, which also indicates the position + of subsequent buffer write. + */ + char *cur; + + /** The absolute end of the buffer. */ + char *end; + +} pjsip_buffer; + + +/** + * General host:port pair, used for example as Via sent-by. + */ +typedef struct pjsip_host_port +{ + unsigned flag; /**< Flags of pjsip_transport_flags_e (not used in Via). */ + unsigned type; /**< Transport type (pjsip_transport_type_e), or zero. */ + pj_str_t host; /**< Host part. */ + int port; /**< Port number. */ +} pjsip_host_port; + + +#endif /* __PJSIP_SIP_TYPES_H__ */ + diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c index aa1b2ee9b..a18abe166 100644 --- a/pjsip/src/pjsip/sip_uri.c +++ b/pjsip/src/pjsip/sip_uri.c @@ -1,396 +1,396 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_uri.c 8 8/31/05 9:05p Bennylp $ */ -#include -#include -#include -#include -#include - -#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr) - -static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* ); -static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* ); -static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * ); -static void *pjsip_get_uri( pjsip_uri *uri ); -static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ); - -static pj_str_t sip_str = { "sip", 3 }; -static pj_str_t sips_str = { "sips", 4 }; - -#ifdef __GNUC__ -# define HAPPY_FLAG (void*) -#else -# define HAPPY_FLAG -#endif - -static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, - const pjsip_name_addr *rhs); -static int pjsip_name_addr_print( pjsip_uri_context_e context, - const pjsip_name_addr *name, - char *buf, pj_size_t size); -static int pjsip_name_addr_compare( pjsip_uri_context_e context, - const pjsip_name_addr *naddr1, - const pjsip_name_addr *naddr2); -static int pjsip_url_print( pjsip_uri_context_e context, - const pjsip_url *url, - char *buf, pj_size_t size); -static int pjsip_url_compare( pjsip_uri_context_e context, - const pjsip_url *url1, const pjsip_url *url2); -static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs); - -static pjsip_uri_vptr sip_url_vptr = -{ - HAPPY_FLAG &pjsip_url_get_scheme, - HAPPY_FLAG &pjsip_get_uri, - HAPPY_FLAG &pjsip_url_print, - HAPPY_FLAG &pjsip_url_compare, - HAPPY_FLAG &pjsip_url_clone -}; - -static pjsip_uri_vptr sips_url_vptr = -{ - HAPPY_FLAG &pjsips_url_get_scheme, - HAPPY_FLAG &pjsip_get_uri, - HAPPY_FLAG &pjsip_url_print, - HAPPY_FLAG &pjsip_url_compare, - HAPPY_FLAG &pjsip_url_clone -}; - -static pjsip_uri_vptr name_addr_vptr = -{ - HAPPY_FLAG &pjsip_name_addr_get_scheme, - HAPPY_FLAG &pjsip_name_addr_get_uri, - HAPPY_FLAG &pjsip_name_addr_print, - HAPPY_FLAG &pjsip_name_addr_compare, - HAPPY_FLAG &pjsip_name_addr_clone -}; - -static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url) -{ - PJ_UNUSED_ARG(url) - return &sip_str; -} - -static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url) -{ - PJ_UNUSED_ARG(url) - return &sips_str; -} - -static void *pjsip_get_uri( pjsip_uri *uri ) -{ - return uri; -} - -static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ) -{ - return name->uri; -} - -PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure) -{ - pj_memset(url, 0, sizeof(*url)); - url->ttl_param = -1; - url->vptr = secure ? &sips_url_vptr : &sip_url_vptr; -} - -PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure ) -{ - pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); - pjsip_url_init(url, secure); - return url; -} - -static int pjsip_url_print( pjsip_uri_context_e context, - const pjsip_url *url, - char *buf, pj_size_t size) -{ - int printed; - pj_size_t size_required; - char *startbuf = buf; - const pj_str_t *scheme; - *buf = '\0'; - - /* Check the buffer length. */ - size_required = 6 + url->host.slen + 10 + - url->user.slen + url->passwd.slen + 2 + - url->user_param.slen + 6 + - url->method_param.slen + 8 + - url->transport_param.slen + 11 + - 9 + 5 + - url->maddr_param.slen + 7 + - 3 + - url->other_param.slen + - url->header_param.slen; - if (size < size_required) { - return -1; - } - - /* Print scheme ("sip:" or "sips:") */ - scheme = pjsip_uri_get_scheme(url); - copy_advance_no_check(buf, *scheme); - *buf++ = ':'; - - /* Print "user:password@", if any. */ - if (url->user.slen) { - copy_advance_no_check(buf, url->user); - if (url->passwd.slen) { - *buf++ = ':'; - copy_advance_no_check(buf, url->passwd); - } - - *buf++ = '@'; - } - - /* Print host. */ - pj_assert(url->host.slen != 0); - copy_advance_no_check(buf, url->host); - - /* Only print port if it is explicitly specified. - * Port is not allowed in To and From header. - */ - /* Unfortunately some UA requires us to send back the port - * number exactly as it was sent. We don't remember whether an - * UA has sent us port, so we'll just send the port indiscrimately - */ - PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER) - if (url->port /*&& context != PJSIP_URI_IN_FROMTO_HDR*/) { - *buf++ = ':'; - printed = pj_utoa(url->port, buf); - buf += printed; - } - - /* User param is allowed in all contexes */ - copy_advance_pair_no_check(buf, ";user=", 6, url->user_param); - - /* Method param is only allowed in external/other context. */ - if (context == PJSIP_URI_IN_OTHER) { - copy_advance_pair_no_check(buf, ";method=", 8, url->method_param); - } - - /* Transport is not allowed in From/To header. */ - if (context != PJSIP_URI_IN_FROMTO_HDR) { - copy_advance_pair_no_check(buf, ";transport=", 11, url->transport_param); - } - - /* TTL param is not allowed in From, To, Route, and Record-Route header. */ - if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR && - context != PJSIP_URI_IN_ROUTING_HDR) - { - pj_memcpy(buf, ";ttl=", 5); - printed = pj_utoa(url->ttl_param, buf+5); - buf += printed + 5; - } - - /* maddr param is not allowed in From and To header. */ - if (context != PJSIP_URI_IN_FROMTO_HDR) { - copy_advance_pair_no_check(buf, ";maddr=", 7, url->maddr_param); - } - - /* lr param is not allowed in From, To, and Contact header. */ - if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR && - context != PJSIP_URI_IN_CONTACT_HDR) - { - pj_str_t lr = { ";lr", 3 }; - copy_advance_no_check(buf, lr); - } - - /* Other param. */ - if (url->other_param.slen) { - copy_advance_no_check(buf, url->other_param); - } - - /* Header param. */ - if (url->header_param.slen) { - copy_advance_no_check(buf, url->header_param); - } - - *buf = '\0'; - return buf-startbuf; -} - -static int pjsip_url_compare( pjsip_uri_context_e context, - const pjsip_url *url1, const pjsip_url *url2) -{ - /* The easiest (and probably the most efficient) way to compare two URLs - are to print them, and compare them bytes per bytes. This technique - works quite well with RFC3261, as the RFC (unlike RFC2543) defines that - components specified in one URL does NOT match its default value if - it is not specified in the second URL. For example, parameter "user=ip" - does NOT match if it is omited in second URL. - - HOWEVER, THE SAME CAN NOT BE APPLIED FOR other-param NOR header-param. - For these, each of the parameters must be compared one by one. Parameter - that exists in one URL will match the comparison. But parameter that - exists in both URLs and doesn't match wont match the URL comparison. - - The solution for this is to compare 'standard' URL components with - bytes-to-bytes comparison, and compare other-param and header-param with - more intelligent comparison. - */ - char str_url1[PJSIP_MAX_URL_SIZE]; - char str_url2[PJSIP_MAX_URL_SIZE]; - int len1, len2; - - /* Must compare scheme first, as the second URI may not be SIP URL. */ - if (pj_stricmp(pjsip_uri_get_scheme(url1), pjsip_uri_get_scheme(url2))) - return -1; - - len1 = pjsip_url_print(context, url1, str_url1, sizeof(str_url1)); - if (len1 < 1) { - pj_assert(0); - return -1; - } - len2 = pjsip_url_print(context, url2, str_url2, sizeof(str_url2)); - if (len2 < 1) { - pj_assert(0); - return -1; - } - - if (len1 != len2) { - /* Not equal. */ - return -1; - } - - if (strcmp(str_url1, str_url2)) { - /* Not equal */ - return -1; - } - - /* TODO: compare other-param and header-param in more intelligent manner. */ - PJ_TODO(HPARAM_AND_OTHER_PARAM_COMPARISON_IN_URL_COMPARISON) - - if (pj_strcmp(&url1->other_param, &url2->other_param)) { - /* Not equal. */ - return -1; - } - if (pj_strcmp(&url1->header_param, &url2->header_param)) { - /* Not equal. */ - return -1; - } - - /* Seems to be equal, isn't it. */ - return 0; - -} - - -PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, - const pjsip_url *rhs) -{ - pj_strdup( pool, &url->user, &rhs->user); - pj_strdup( pool, &url->passwd, &rhs->passwd); - pj_strdup( pool, &url->host, &rhs->host); - url->port = rhs->port; - pj_strdup( pool, &url->user_param, &rhs->user_param); - pj_strdup( pool, &url->method_param, &rhs->method_param); - pj_strdup( pool, &url->transport_param, &rhs->transport_param); - url->ttl_param = rhs->ttl_param; - pj_strdup( pool, &url->maddr_param, &rhs->maddr_param); - pj_strdup( pool, &url->other_param, &rhs->other_param); - pj_strdup( pool, &url->header_param, &rhs->header_param); - url->lr_param = rhs->lr_param; -} - -static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs) -{ - pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); - if (!url) - return NULL; - - pjsip_url_init(url, IS_SIPS(rhs)); - pjsip_url_assign(pool, url, rhs); - return url; -} - -static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name) -{ - pj_assert(name->uri != NULL); - return pjsip_uri_get_scheme(name->uri); -} - -PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name) -{ - name->vptr = &name_addr_vptr; - name->uri = NULL; - name->display.slen = 0; -} - -PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool) -{ - pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); - pjsip_name_addr_init(name_addr); - return name_addr; -} - -static int pjsip_name_addr_print( pjsip_uri_context_e context, - const pjsip_name_addr *name, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - pj_assert(name->uri != NULL); - - if (context != PJSIP_URI_IN_REQ_URI) { - copy_advance(buf, name->display); - if (name->display.slen) { - *buf++ = ' '; - } - *buf++ = '<'; - } - - printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf)); - if (printed < 1) - return -1; - buf += printed; - - if (context != PJSIP_URI_IN_REQ_URI) { - *buf++ = '>'; - } - - *buf = '\0'; - return buf-startbuf; -} - -PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst, - const pjsip_name_addr *src) -{ - pj_strdup( pool, &dst->display, &src->display); - dst->uri = pjsip_uri_clone(pool, src->uri); -} - -static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, - const pjsip_name_addr *rhs) -{ - pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); - if (!addr) - return NULL; - - pjsip_name_addr_init(addr); - pjsip_name_addr_assign(pool, addr, rhs); - return addr; -} - -static int pjsip_name_addr_compare( pjsip_uri_context_e context, - const pjsip_name_addr *naddr1, - const pjsip_name_addr *naddr2) -{ - int d; - - /* I'm not sure whether display name is included in the comparison. */ - if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) { - return -1; - } - - pj_assert( naddr1->uri != NULL ); - pj_assert( naddr2->uri != NULL ); - - /* Compare name-addr as URL */ - d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri); - if (d) - return d; - - return 0; -} - +/* $Header: /pjproject/pjsip/src/pjsip/sip_uri.c 8 8/31/05 9:05p Bennylp $ */ +#include +#include +#include +#include +#include + +#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr) + +static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* ); +static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* ); +static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * ); +static void *pjsip_get_uri( pjsip_uri *uri ); +static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ); + +static pj_str_t sip_str = { "sip", 3 }; +static pj_str_t sips_str = { "sips", 4 }; + +#ifdef __GNUC__ +# define HAPPY_FLAG (void*) +#else +# define HAPPY_FLAG +#endif + +static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, + const pjsip_name_addr *rhs); +static int pjsip_name_addr_print( pjsip_uri_context_e context, + const pjsip_name_addr *name, + char *buf, pj_size_t size); +static int pjsip_name_addr_compare( pjsip_uri_context_e context, + const pjsip_name_addr *naddr1, + const pjsip_name_addr *naddr2); +static int pjsip_url_print( pjsip_uri_context_e context, + const pjsip_url *url, + char *buf, pj_size_t size); +static int pjsip_url_compare( pjsip_uri_context_e context, + const pjsip_url *url1, const pjsip_url *url2); +static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs); + +static pjsip_uri_vptr sip_url_vptr = +{ + HAPPY_FLAG &pjsip_url_get_scheme, + HAPPY_FLAG &pjsip_get_uri, + HAPPY_FLAG &pjsip_url_print, + HAPPY_FLAG &pjsip_url_compare, + HAPPY_FLAG &pjsip_url_clone +}; + +static pjsip_uri_vptr sips_url_vptr = +{ + HAPPY_FLAG &pjsips_url_get_scheme, + HAPPY_FLAG &pjsip_get_uri, + HAPPY_FLAG &pjsip_url_print, + HAPPY_FLAG &pjsip_url_compare, + HAPPY_FLAG &pjsip_url_clone +}; + +static pjsip_uri_vptr name_addr_vptr = +{ + HAPPY_FLAG &pjsip_name_addr_get_scheme, + HAPPY_FLAG &pjsip_name_addr_get_uri, + HAPPY_FLAG &pjsip_name_addr_print, + HAPPY_FLAG &pjsip_name_addr_compare, + HAPPY_FLAG &pjsip_name_addr_clone +}; + +static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url) +{ + PJ_UNUSED_ARG(url) + return &sip_str; +} + +static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url) +{ + PJ_UNUSED_ARG(url) + return &sips_str; +} + +static void *pjsip_get_uri( pjsip_uri *uri ) +{ + return uri; +} + +static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ) +{ + return name->uri; +} + +PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure) +{ + pj_memset(url, 0, sizeof(*url)); + url->ttl_param = -1; + url->vptr = secure ? &sips_url_vptr : &sip_url_vptr; +} + +PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure ) +{ + pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); + pjsip_url_init(url, secure); + return url; +} + +static int pjsip_url_print( pjsip_uri_context_e context, + const pjsip_url *url, + char *buf, pj_size_t size) +{ + int printed; + pj_size_t size_required; + char *startbuf = buf; + const pj_str_t *scheme; + *buf = '\0'; + + /* Check the buffer length. */ + size_required = 6 + url->host.slen + 10 + + url->user.slen + url->passwd.slen + 2 + + url->user_param.slen + 6 + + url->method_param.slen + 8 + + url->transport_param.slen + 11 + + 9 + 5 + + url->maddr_param.slen + 7 + + 3 + + url->other_param.slen + + url->header_param.slen; + if (size < size_required) { + return -1; + } + + /* Print scheme ("sip:" or "sips:") */ + scheme = pjsip_uri_get_scheme(url); + copy_advance_no_check(buf, *scheme); + *buf++ = ':'; + + /* Print "user:password@", if any. */ + if (url->user.slen) { + copy_advance_no_check(buf, url->user); + if (url->passwd.slen) { + *buf++ = ':'; + copy_advance_no_check(buf, url->passwd); + } + + *buf++ = '@'; + } + + /* Print host. */ + pj_assert(url->host.slen != 0); + copy_advance_no_check(buf, url->host); + + /* Only print port if it is explicitly specified. + * Port is not allowed in To and From header. + */ + /* Unfortunately some UA requires us to send back the port + * number exactly as it was sent. We don't remember whether an + * UA has sent us port, so we'll just send the port indiscrimately + */ + PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER) + if (url->port /*&& context != PJSIP_URI_IN_FROMTO_HDR*/) { + *buf++ = ':'; + printed = pj_utoa(url->port, buf); + buf += printed; + } + + /* User param is allowed in all contexes */ + copy_advance_pair_no_check(buf, ";user=", 6, url->user_param); + + /* Method param is only allowed in external/other context. */ + if (context == PJSIP_URI_IN_OTHER) { + copy_advance_pair_no_check(buf, ";method=", 8, url->method_param); + } + + /* Transport is not allowed in From/To header. */ + if (context != PJSIP_URI_IN_FROMTO_HDR) { + copy_advance_pair_no_check(buf, ";transport=", 11, url->transport_param); + } + + /* TTL param is not allowed in From, To, Route, and Record-Route header. */ + if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR && + context != PJSIP_URI_IN_ROUTING_HDR) + { + pj_memcpy(buf, ";ttl=", 5); + printed = pj_utoa(url->ttl_param, buf+5); + buf += printed + 5; + } + + /* maddr param is not allowed in From and To header. */ + if (context != PJSIP_URI_IN_FROMTO_HDR) { + copy_advance_pair_no_check(buf, ";maddr=", 7, url->maddr_param); + } + + /* lr param is not allowed in From, To, and Contact header. */ + if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR && + context != PJSIP_URI_IN_CONTACT_HDR) + { + pj_str_t lr = { ";lr", 3 }; + copy_advance_no_check(buf, lr); + } + + /* Other param. */ + if (url->other_param.slen) { + copy_advance_no_check(buf, url->other_param); + } + + /* Header param. */ + if (url->header_param.slen) { + copy_advance_no_check(buf, url->header_param); + } + + *buf = '\0'; + return buf-startbuf; +} + +static int pjsip_url_compare( pjsip_uri_context_e context, + const pjsip_url *url1, const pjsip_url *url2) +{ + /* The easiest (and probably the most efficient) way to compare two URLs + are to print them, and compare them bytes per bytes. This technique + works quite well with RFC3261, as the RFC (unlike RFC2543) defines that + components specified in one URL does NOT match its default value if + it is not specified in the second URL. For example, parameter "user=ip" + does NOT match if it is omited in second URL. + + HOWEVER, THE SAME CAN NOT BE APPLIED FOR other-param NOR header-param. + For these, each of the parameters must be compared one by one. Parameter + that exists in one URL will match the comparison. But parameter that + exists in both URLs and doesn't match wont match the URL comparison. + + The solution for this is to compare 'standard' URL components with + bytes-to-bytes comparison, and compare other-param and header-param with + more intelligent comparison. + */ + char str_url1[PJSIP_MAX_URL_SIZE]; + char str_url2[PJSIP_MAX_URL_SIZE]; + int len1, len2; + + /* Must compare scheme first, as the second URI may not be SIP URL. */ + if (pj_stricmp(pjsip_uri_get_scheme(url1), pjsip_uri_get_scheme(url2))) + return -1; + + len1 = pjsip_url_print(context, url1, str_url1, sizeof(str_url1)); + if (len1 < 1) { + pj_assert(0); + return -1; + } + len2 = pjsip_url_print(context, url2, str_url2, sizeof(str_url2)); + if (len2 < 1) { + pj_assert(0); + return -1; + } + + if (len1 != len2) { + /* Not equal. */ + return -1; + } + + if (strcmp(str_url1, str_url2)) { + /* Not equal */ + return -1; + } + + /* TODO: compare other-param and header-param in more intelligent manner. */ + PJ_TODO(HPARAM_AND_OTHER_PARAM_COMPARISON_IN_URL_COMPARISON) + + if (pj_strcmp(&url1->other_param, &url2->other_param)) { + /* Not equal. */ + return -1; + } + if (pj_strcmp(&url1->header_param, &url2->header_param)) { + /* Not equal. */ + return -1; + } + + /* Seems to be equal, isn't it. */ + return 0; + +} + + +PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, + const pjsip_url *rhs) +{ + pj_strdup( pool, &url->user, &rhs->user); + pj_strdup( pool, &url->passwd, &rhs->passwd); + pj_strdup( pool, &url->host, &rhs->host); + url->port = rhs->port; + pj_strdup( pool, &url->user_param, &rhs->user_param); + pj_strdup( pool, &url->method_param, &rhs->method_param); + pj_strdup( pool, &url->transport_param, &rhs->transport_param); + url->ttl_param = rhs->ttl_param; + pj_strdup( pool, &url->maddr_param, &rhs->maddr_param); + pj_strdup( pool, &url->other_param, &rhs->other_param); + pj_strdup( pool, &url->header_param, &rhs->header_param); + url->lr_param = rhs->lr_param; +} + +static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs) +{ + pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); + if (!url) + return NULL; + + pjsip_url_init(url, IS_SIPS(rhs)); + pjsip_url_assign(pool, url, rhs); + return url; +} + +static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name) +{ + pj_assert(name->uri != NULL); + return pjsip_uri_get_scheme(name->uri); +} + +PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name) +{ + name->vptr = &name_addr_vptr; + name->uri = NULL; + name->display.slen = 0; +} + +PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool) +{ + pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); + pjsip_name_addr_init(name_addr); + return name_addr; +} + +static int pjsip_name_addr_print( pjsip_uri_context_e context, + const pjsip_name_addr *name, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf + size; + + pj_assert(name->uri != NULL); + + if (context != PJSIP_URI_IN_REQ_URI) { + copy_advance(buf, name->display); + if (name->display.slen) { + *buf++ = ' '; + } + *buf++ = '<'; + } + + printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf)); + if (printed < 1) + return -1; + buf += printed; + + if (context != PJSIP_URI_IN_REQ_URI) { + *buf++ = '>'; + } + + *buf = '\0'; + return buf-startbuf; +} + +PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst, + const pjsip_name_addr *src) +{ + pj_strdup( pool, &dst->display, &src->display); + dst->uri = pjsip_uri_clone(pool, src->uri); +} + +static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, + const pjsip_name_addr *rhs) +{ + pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); + if (!addr) + return NULL; + + pjsip_name_addr_init(addr); + pjsip_name_addr_assign(pool, addr, rhs); + return addr; +} + +static int pjsip_name_addr_compare( pjsip_uri_context_e context, + const pjsip_name_addr *naddr1, + const pjsip_name_addr *naddr2) +{ + int d; + + /* I'm not sure whether display name is included in the comparison. */ + if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) { + return -1; + } + + pj_assert( naddr1->uri != NULL ); + pj_assert( naddr2->uri != NULL ); + + /* Compare name-addr as URL */ + d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri); + if (d) + return d; + + return 0; +} + diff --git a/pjsip/src/pjsip/sip_uri.h b/pjsip/src/pjsip/sip_uri.h index a84079449..6a759a951 100644 --- a/pjsip/src/pjsip/sip_uri.h +++ b/pjsip/src/pjsip/sip_uri.h @@ -1,293 +1,293 @@ -/* $Header: /pjproject/pjsip/src/pjsip/sip_uri.h 7 6/19/05 6:12p Bennylp $ */ -#ifndef __PJSIP_SIP_URI_H__ -#define __PJSIP_SIP_URI_H__ - -/** - * @file sip_uri.h - * @brief SIP URL Structures and Manipulations - */ - -#include -#include - -PJ_BEGIN_DECL - - -/** - * @defgroup PJSIP_URL URL Structures - * @brief SIP Url, tel: Url, and generic URI. - * @ingroup PJSIP_MSG - * @{ - */ - -/** - * URI context. - */ -typedef enum pjsip_uri_context_e -{ - PJSIP_URI_IN_REQ_URI, /**< The URI is in Request URI. */ - PJSIP_URI_IN_FROMTO_HDR, /**< The URI is in From/To header. */ - PJSIP_URI_IN_CONTACT_HDR, /**< The URI is in Contact header. */ - PJSIP_URI_IN_ROUTING_HDR, /**< The URI is in Route/Record-Route header. */ - PJSIP_URI_IN_OTHER, /**< Other context (web page, business card, etc.) */ -} pjsip_uri_context_e; - -/** - * URI 'virtual' function table. - * All types of URI in this library (such as sip:, sips:, tel:, and name-addr) - * will have pointer to this table as their first struct member. This table - * provides polimorphic behaviour to the URI. - */ -typedef struct pjsip_uri_vptr -{ - /** - * Get URI scheme. - * @param uri the URI (self). - * @return the URI scheme. - */ - const pj_str_t* (*p_get_scheme)(const void *uri); - - /** - * Get the URI object contained by this URI, or the URI itself if - * it doesn't contain another URI. - * @param uri the URI (self). - */ - void* (*p_get_uri)(void *uri); - - /** - * Print URI components to the buffer, following the rule of which - * components are allowed for the context. - * @param context the context where the URI will be placed. - * @param uri the URI (self). - * @param buf the buffer. - * @param size the size of the buffer. - * @return the length printed. - */ - int (*p_print)(pjsip_uri_context_e context, - const void *uri, - char *buf, pj_size_t size); - - /** - * Compare two URIs according to the context. - * @param context the context. - * @param uri1 the first URI (self). - * @param uri2 the second URI. - * @return zero if equal. - */ - int (*p_compare)(pjsip_uri_context_e context, - const void *uri1, const void *uri2); - - /** - * Clone URI. - * @param pool the pool. - * @param the URI to clone (self). - * @return new URI. - */ - void *(*p_clone)(pj_pool_t *pool, const void *uri); - -} pjsip_uri_vptr; - - -/** - * The declaration of 'base class' for all URI scheme. - */ -struct pjsip_uri -{ - /** All URIs must have URI virtual function table as their first member. */ - pjsip_uri_vptr *vptr; -}; - -/** - * This macro checks that the URL is a "sip:" or "sips:" URL. - * @param url The URL (pointer to) - * @return non-zero if TRUE. - */ -#define PJSIP_URI_SCHEME_IS_SIP(url) \ - (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0) - -/** - * This macro checks that the URL is a "sips:" URL (not SIP). - * @param url The URL (pointer to) - * @return non-zero if TRUE. - */ -#define PJSIP_URI_SCHEME_IS_SIPS(url) \ - (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0) - -/** - * This macro checks that the URL is a "tel:" URL. - * @param url The URL (pointer to) - * @return non-zero if TRUE. - */ -#define PJSIP_URI_SCHEME_IS_TEL(url) \ - (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0) - - - -/** - * SIP and SIPS URL scheme. - */ -typedef struct pjsip_url -{ - pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/ - pj_str_t user; /**< Optional user part. */ - pj_str_t passwd; /**< Optional password part. */ - pj_str_t host; /**< Host part, always exists. */ - int port; /**< Optional port number, or zero. */ - pj_str_t user_param; /**< Optional user parameter */ - pj_str_t method_param; /**< Optional method parameter. */ - pj_str_t transport_param; /**< Optional transport parameter. */ - int ttl_param; /**< Optional TTL param, or -1. */ - int lr_param; /**< Optional loose routing param, or zero */ - pj_str_t maddr_param; /**< Optional maddr param */ - pj_str_t other_param; /**< Other parameters grouped together. */ - pj_str_t header_param; /**< Optional header parameter. */ -} pjsip_url; - - -/** - * SIP name-addr, which typically appear in From, To, and Contact header. - * The SIP name-addr contains a generic URI and a display name. - */ -typedef struct pjsip_name_addr -{ - /** Pointer to virtual function table. */ - pjsip_uri_vptr *vptr; - - /** Optional display name. */ - pj_str_t display; - - /** URI part. */ - pjsip_uri *uri; - -} pjsip_name_addr; - - -/** - * Generic function to get the URI scheme. - * @param uri the URI object. - * @return the URI scheme. - */ -PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri) -{ - return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri); -} - -/** - * Generic function to get the URI object contained by this URI, or the URI - * itself if it doesn't contain another URI. - * - * @param uri the URI. - * @return the URI. - */ -PJ_INLINE(void*) pjsip_uri_get_uri(void *uri) -{ - return (*((pjsip_uri*)uri)->vptr->p_get_uri)(uri); -} - -/** - * Generic function to compare two URIs. - * - * @param context Comparison context. - * @param uri1 The first URI. - * @param uri2 The second URI. - * @return Zero if equal. - */ -PJ_INLINE(int) pjsip_uri_cmp(pjsip_uri_context_e context, - const void *uri1, const void *uri2) -{ - return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2); -} - -/** - * Generic function to print an URI object. - * - * @param context Print context. - * @param uri The URI to print. - * @param buf The buffer. - * @param size Size of the buffer. - * @return Length printed. - */ -PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context, - const void *uri, - char *buf, pj_size_t size) -{ - return (*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size); -} - -/** - * Generic function to clone an URI object. - * - * @param pool Pool. - * @param uri URI to clone. - * @return New URI. - */ -PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri ) -{ - return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri); -} - - -/** - * Create new SIP URL and initialize all fields with zero or NULL. - * @param pool The pool. - * @param secure Tlag to indicate whether secure transport should be used. - * @return SIP URL. - */ -PJ_DECL(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure ); - -/** - * Create new SIPS URL and initialize all fields with zero or NULL. - * @param pool The pool. - * @return SIPS URL. - */ -PJ_DECL(pjsip_url*) pjsips_url_create( pj_pool_t *pool ); - -/** - * Initialize SIP URL (all fields are set to NULL or zero). - * @param url The URL. - */ -PJ_DECL(void) pjsip_url_init(pjsip_url *url, int secure); - -/** - * Perform full assignment to the SIP URL. - * @param pool The pool. - * @param url Destination URL. - * @param rhs The source URL. - */ -PJ_DECL(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, const pjsip_url *rhs); - -/** - * Create new instance of name address and initialize all fields with zero or - * NULL. - * @param pool The pool. - * @return New SIP name address. - */ -PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool); - -/** - * Initialize with default value. - * @param name_addr The name address. - */ -PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr); - -/** - * Perform full assignment to the name address. - * @param pool The pool. - * @param addr The destination name address. - * @param rhs The source name address. - */ -PJ_DECL(void) pjsip_name_addr_assign(pj_pool_t *pool, - pjsip_name_addr *addr, - const pjsip_name_addr *rhs); - - - - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_URL_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip/sip_uri.h 7 6/19/05 6:12p Bennylp $ */ +#ifndef __PJSIP_SIP_URI_H__ +#define __PJSIP_SIP_URI_H__ + +/** + * @file sip_uri.h + * @brief SIP URL Structures and Manipulations + */ + +#include +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJSIP_URL URL Structures + * @brief SIP Url, tel: Url, and generic URI. + * @ingroup PJSIP_MSG + * @{ + */ + +/** + * URI context. + */ +typedef enum pjsip_uri_context_e +{ + PJSIP_URI_IN_REQ_URI, /**< The URI is in Request URI. */ + PJSIP_URI_IN_FROMTO_HDR, /**< The URI is in From/To header. */ + PJSIP_URI_IN_CONTACT_HDR, /**< The URI is in Contact header. */ + PJSIP_URI_IN_ROUTING_HDR, /**< The URI is in Route/Record-Route header. */ + PJSIP_URI_IN_OTHER, /**< Other context (web page, business card, etc.) */ +} pjsip_uri_context_e; + +/** + * URI 'virtual' function table. + * All types of URI in this library (such as sip:, sips:, tel:, and name-addr) + * will have pointer to this table as their first struct member. This table + * provides polimorphic behaviour to the URI. + */ +typedef struct pjsip_uri_vptr +{ + /** + * Get URI scheme. + * @param uri the URI (self). + * @return the URI scheme. + */ + const pj_str_t* (*p_get_scheme)(const void *uri); + + /** + * Get the URI object contained by this URI, or the URI itself if + * it doesn't contain another URI. + * @param uri the URI (self). + */ + void* (*p_get_uri)(void *uri); + + /** + * Print URI components to the buffer, following the rule of which + * components are allowed for the context. + * @param context the context where the URI will be placed. + * @param uri the URI (self). + * @param buf the buffer. + * @param size the size of the buffer. + * @return the length printed. + */ + int (*p_print)(pjsip_uri_context_e context, + const void *uri, + char *buf, pj_size_t size); + + /** + * Compare two URIs according to the context. + * @param context the context. + * @param uri1 the first URI (self). + * @param uri2 the second URI. + * @return zero if equal. + */ + int (*p_compare)(pjsip_uri_context_e context, + const void *uri1, const void *uri2); + + /** + * Clone URI. + * @param pool the pool. + * @param the URI to clone (self). + * @return new URI. + */ + void *(*p_clone)(pj_pool_t *pool, const void *uri); + +} pjsip_uri_vptr; + + +/** + * The declaration of 'base class' for all URI scheme. + */ +struct pjsip_uri +{ + /** All URIs must have URI virtual function table as their first member. */ + pjsip_uri_vptr *vptr; +}; + +/** + * This macro checks that the URL is a "sip:" or "sips:" URL. + * @param url The URL (pointer to) + * @return non-zero if TRUE. + */ +#define PJSIP_URI_SCHEME_IS_SIP(url) \ + (pj_strnicmp2(pjsip_uri_get_scheme(url), "sip", 3)==0) + +/** + * This macro checks that the URL is a "sips:" URL (not SIP). + * @param url The URL (pointer to) + * @return non-zero if TRUE. + */ +#define PJSIP_URI_SCHEME_IS_SIPS(url) \ + (pj_strnicmp2(pjsip_uri_get_scheme(url), "sips", 4)==0) + +/** + * This macro checks that the URL is a "tel:" URL. + * @param url The URL (pointer to) + * @return non-zero if TRUE. + */ +#define PJSIP_URI_SCHEME_IS_TEL(url) \ + (pj_strnicmp2(pjsip_uri_get_scheme(url), "tel", 3)==0) + + + +/** + * SIP and SIPS URL scheme. + */ +typedef struct pjsip_url +{ + pjsip_uri_vptr *vptr; /**< Pointer to virtual function table.*/ + pj_str_t user; /**< Optional user part. */ + pj_str_t passwd; /**< Optional password part. */ + pj_str_t host; /**< Host part, always exists. */ + int port; /**< Optional port number, or zero. */ + pj_str_t user_param; /**< Optional user parameter */ + pj_str_t method_param; /**< Optional method parameter. */ + pj_str_t transport_param; /**< Optional transport parameter. */ + int ttl_param; /**< Optional TTL param, or -1. */ + int lr_param; /**< Optional loose routing param, or zero */ + pj_str_t maddr_param; /**< Optional maddr param */ + pj_str_t other_param; /**< Other parameters grouped together. */ + pj_str_t header_param; /**< Optional header parameter. */ +} pjsip_url; + + +/** + * SIP name-addr, which typically appear in From, To, and Contact header. + * The SIP name-addr contains a generic URI and a display name. + */ +typedef struct pjsip_name_addr +{ + /** Pointer to virtual function table. */ + pjsip_uri_vptr *vptr; + + /** Optional display name. */ + pj_str_t display; + + /** URI part. */ + pjsip_uri *uri; + +} pjsip_name_addr; + + +/** + * Generic function to get the URI scheme. + * @param uri the URI object. + * @return the URI scheme. + */ +PJ_INLINE(const pj_str_t*) pjsip_uri_get_scheme(const void *uri) +{ + return (*((pjsip_uri*)uri)->vptr->p_get_scheme)(uri); +} + +/** + * Generic function to get the URI object contained by this URI, or the URI + * itself if it doesn't contain another URI. + * + * @param uri the URI. + * @return the URI. + */ +PJ_INLINE(void*) pjsip_uri_get_uri(void *uri) +{ + return (*((pjsip_uri*)uri)->vptr->p_get_uri)(uri); +} + +/** + * Generic function to compare two URIs. + * + * @param context Comparison context. + * @param uri1 The first URI. + * @param uri2 The second URI. + * @return Zero if equal. + */ +PJ_INLINE(int) pjsip_uri_cmp(pjsip_uri_context_e context, + const void *uri1, const void *uri2) +{ + return (*((const pjsip_uri*)uri1)->vptr->p_compare)(context, uri1, uri2); +} + +/** + * Generic function to print an URI object. + * + * @param context Print context. + * @param uri The URI to print. + * @param buf The buffer. + * @param size Size of the buffer. + * @return Length printed. + */ +PJ_INLINE(int) pjsip_uri_print(pjsip_uri_context_e context, + const void *uri, + char *buf, pj_size_t size) +{ + return (*((const pjsip_uri*)uri)->vptr->p_print)(context, uri, buf, size); +} + +/** + * Generic function to clone an URI object. + * + * @param pool Pool. + * @param uri URI to clone. + * @return New URI. + */ +PJ_INLINE(void*) pjsip_uri_clone( pj_pool_t *pool, const void *uri ) +{ + return (*((const pjsip_uri*)uri)->vptr->p_clone)(pool, uri); +} + + +/** + * Create new SIP URL and initialize all fields with zero or NULL. + * @param pool The pool. + * @param secure Tlag to indicate whether secure transport should be used. + * @return SIP URL. + */ +PJ_DECL(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure ); + +/** + * Create new SIPS URL and initialize all fields with zero or NULL. + * @param pool The pool. + * @return SIPS URL. + */ +PJ_DECL(pjsip_url*) pjsips_url_create( pj_pool_t *pool ); + +/** + * Initialize SIP URL (all fields are set to NULL or zero). + * @param url The URL. + */ +PJ_DECL(void) pjsip_url_init(pjsip_url *url, int secure); + +/** + * Perform full assignment to the SIP URL. + * @param pool The pool. + * @param url Destination URL. + * @param rhs The source URL. + */ +PJ_DECL(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, const pjsip_url *rhs); + +/** + * Create new instance of name address and initialize all fields with zero or + * NULL. + * @param pool The pool. + * @return New SIP name address. + */ +PJ_DECL(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool); + +/** + * Initialize with default value. + * @param name_addr The name address. + */ +PJ_DECL(void) pjsip_name_addr_init(pjsip_name_addr *name_addr); + +/** + * Perform full assignment to the name address. + * @param pool The pool. + * @param addr The destination name address. + * @param rhs The source name address. + */ +PJ_DECL(void) pjsip_name_addr_assign(pj_pool_t *pool, + pjsip_name_addr *addr, + const pjsip_name_addr *rhs); + + + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_URL_H__ */ + diff --git a/pjsip/src/pjsip_auth.h b/pjsip/src/pjsip_auth.h index cd6934de4..310b13f10 100644 --- a/pjsip/src/pjsip_auth.h +++ b/pjsip/src/pjsip_auth.h @@ -1,20 +1,20 @@ -/* $Header: /pjproject/pjsip/src/pjsip_auth.h 2 6/05/05 12:13p Bennylp $ */ -#ifndef __PJSIP_AUTH_H__ -#define __PJSIP_AUTH_H__ - -/** - * @defgroup PJSIP_AUTH SIP Authorization module - */ - -/** - * @file pjsip_auth.h - * @brief SIP Authorization Module. - */ - - -#include -#include -#include - -#endif /* __PJSIP_AUTH_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip_auth.h 2 6/05/05 12:13p Bennylp $ */ +#ifndef __PJSIP_AUTH_H__ +#define __PJSIP_AUTH_H__ + +/** + * @defgroup PJSIP_AUTH SIP Authorization module + */ + +/** + * @file pjsip_auth.h + * @brief SIP Authorization Module. + */ + + +#include +#include +#include + +#endif /* __PJSIP_AUTH_H__ */ + diff --git a/pjsip/src/pjsip_core.h b/pjsip/src/pjsip_core.h index 6a81eb170..bf6aec8ce 100644 --- a/pjsip/src/pjsip_core.h +++ b/pjsip/src/pjsip_core.h @@ -1,19 +1,19 @@ -/* $Header: /pjproject/pjsip/src/pjsip_core.h 2 6/19/05 6:12p Bennylp $ */ -#ifndef __PJSIP_CORE_H__ -#define __PJSIP_CORE_H__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#endif /* __PJSIP_CORE_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip_core.h 2 6/19/05 6:12p Bennylp $ */ +#ifndef __PJSIP_CORE_H__ +#define __PJSIP_CORE_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* __PJSIP_CORE_H__ */ + diff --git a/pjsip/src/pjsip_mod_ua/sip_dialog.c b/pjsip/src/pjsip_mod_ua/sip_dialog.c index 1cb54fe3e..8b979182e 100644 --- a/pjsip/src/pjsip_mod_ua/sip_dialog.c +++ b/pjsip/src/pjsip_mod_ua/sip_dialog.c @@ -1,1784 +1,1784 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip_mod_ua/sip_dialog.c 27 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* TLS to keep dialog lock record (initialized by UA) */ -int pjsip_dlg_lock_tls_id; - -struct dialog_lock_data -{ - struct dialog_lock_data *prev; - pjsip_dlg *dlg; - int is_alive; -}; - -/* - * Static function prototypes. - */ -static void dlg_create_request_throw( pjsip_tx_data **p_tdata, - pjsip_dlg *dlg, - const pjsip_method *method, - int cseq ); -static int dlg_on_all_state_pre( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_all_state_post( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_null( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_incoming( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_calling( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_proceeding( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_connecting( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_established( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_disconnected( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_terminated( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); - -/* - * Dialog state handlers. - */ -static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *, - pjsip_event *) = -{ - &dlg_on_state_null, - &dlg_on_state_incoming, - &dlg_on_state_calling, - &dlg_on_state_proceeding, - &dlg_on_state_connecting, - &dlg_on_state_established, - &dlg_on_state_disconnected, - &dlg_on_state_terminated, -}; - -/* - * Dialog state names. - */ -static const char* dlg_state_names[] = -{ - "STATE_NULL", - "STATE_INCOMING", - "STATE_CALLING", - "STATE_PROCEEDING", - "STATE_CONNECTING", - "STATE_ESTABLISHED", - "STATE_DISCONNECTED", - "STATE_TERMINATED", -}; - - -/* - * Get the dialog string state, normally for logging purpose. - */ -const char *pjsip_dlg_state_str(pjsip_dlg_state_e state) -{ - return dlg_state_names[state]; -} - -/* Lock dialog mutex. */ -static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) -{ - struct dialog_lock_data *prev; - - pj_mutex_lock(dlg->mutex); - prev = pj_thread_local_get(pjsip_dlg_lock_tls_id); - lck->prev = prev; - lck->dlg = dlg; - lck->is_alive = 1; - pj_thread_local_set(pjsip_dlg_lock_tls_id, lck); -} - -/* Carefully unlock dialog mutex, protect against situation when the dialog - * has already been destroyed. - */ -static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) -{ - pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck); - pj_assert(dlg == lck->dlg); - - pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev); - if (lck->is_alive) - pj_mutex_unlock(dlg->mutex); - - return lck->is_alive ? 0 : -1; -} - -/* - * This is called by dialog's FSM to change dialog's state. - */ -static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state, - pjsip_event *event) -{ - PJ_UNUSED_ARG(event); - - PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)", - pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state), - event ? pjsip_event_str(event->type) : "", - event ? pjsip_event_str(event->src_type) : "", - event ? event->src.data : NULL)); - - dlg->state = state; - dlg->handle_tsx_event = dlg_state_handlers[state]; -} - -/* - * Invoke dialog's callback. - * This function is called by dialog's FSM, and interpret the event and call - * the corresponding callback registered by application. - */ -static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, - pjsip_event *event ) -{ - pjsip_dlg_callback *cb = dlg->ua->dlg_cb; - if (!cb) { - PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered).")); - return; - } - - /* Low level event: call the all-events callback. */ - if (cb->on_all_events) { - (*cb->on_all_events)(dlg, dlg_event, event); - } - - /* Low level event: call the tx/rx callback if this is a tx/rx event. */ - if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx) - { - (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data); - } - else if (event->type == PJSIP_EVENT_TX_MSG && - event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg) - { - (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata); - } - else if (event->type == PJSIP_EVENT_RX_MSG && - event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) { - (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata); - } - - /* Now trigger high level events. - * High level event should only occurs when dialog's state has changed, - * except for on_provisional, which may be called multiple times whenever - * response message is sent - */ - if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING && - (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) && - event->obj.tsx == dlg->invite_tsx) - { - /* Sent/received provisional responses. */ - if (cb->on_provisional) - (*cb->on_provisional)(dlg, event->obj.tsx, event); - } - - if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) { - if (cb->on_mid_call_events) - (*cb->on_mid_call_events)(dlg, event); - return; - } - - if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED) - return; - - if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) { - - /* New incoming dialog. */ - pj_assert (event->src_type == PJSIP_EVENT_RX_MSG); - (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata); - - } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) { - - /* Dialog has just sent the first INVITE. */ - if (cb->on_calling) { - (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata); - } - - } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) { - - if (cb->on_disconnected) - (*cb->on_disconnected)(dlg, event); - - } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) { - - if (cb->on_terminated) - (*cb->on_terminated)(dlg); - - pjsip_ua_destroy_dialog(dlg); - - } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) { - - if (cb->on_connecting) - (*cb->on_connecting)(dlg, event); - - } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) { - - if (cb->on_established) - (*cb->on_established)(dlg, event); - } -} - -/* - * This callback receives event from the transaction layer (via User Agent), - * or from dialog timer (for 200/INVITE or ACK retransmission). - */ -void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - int status = 0; - struct dialog_lock_data lck; - - PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)", - pjsip_dlg_state_str(dlg->state), - pjsip_event_str(event->type), - pjsip_event_str(event->src_type))); - - - lock_dialog(dlg, &lck); - - status = dlg_on_all_state_pre( dlg, tsx, event); - - if (status==0) { - status = (*dlg->handle_tsx_event)(dlg, tsx, event); - } - if (status==0) { - status = dlg_on_all_state_post( dlg, tsx, event); - } - - unlock_dialog(dlg, &lck); -} - -/* - * This function contains common processing in all states, and it is called - * before the FSM is invoked. - */ -static int dlg_on_all_state_pre( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(event) - - if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED) - return 0; - - if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING || - tsx->state==PJSIP_TSX_STATE_TRYING)) - { - ++dlg->pending_tsx_count; - - } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) - { - --dlg->pending_tsx_count; - if (tsx == dlg->invite_tsx) - dlg->invite_tsx = NULL; - } - - if (tsx->method.id == PJSIP_INVITE_METHOD) { - tsx->handle_ack = 1; - } - return 0; -} - - -/* - * This function contains common processing in all states, and it is called - * after the FSM is invoked. - */ -static int dlg_on_all_state_post( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(event) - - if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) { - if (dlg->pending_tsx_count == 0 && - dlg->state != PJSIP_DIALOG_STATE_CONNECTING && - dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED && - dlg->state != PJSIP_DIALOG_STATE_TERMINATED) - { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - return -1; - } - } - - return 0; -} - - -/* - * Internal function to initialize dialog, contains common initialization - * for both UAS and UAC dialog. - */ -static pj_status_t dlg_init( pjsip_dlg *dlg ) -{ - /* Init mutex. */ - dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); - if (!dlg->mutex) { - PJ_PERROR((dlg->obj_name, "pj_mutex_create()")); - return -1; - } - - /* Init route-set (Initially empty) */ - pj_list_init(&dlg->route_set); - - /* Init auth credential list. */ - pj_list_init(&dlg->auth_sess); - - return PJ_SUCCESS; -} - -/* - * This one is called just before dialog is destroyed. - * It is called while mutex is held. - */ -PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ) -{ - struct dialog_lock_data *lck; - - //PJ_TODO(CHECK_THIS); - pj_assert(dlg->pending_tsx_count == 0); - - /* Mark dialog as dead. */ - lck = pj_thread_local_get(pjsip_dlg_lock_tls_id); - while (lck) { - if (lck->dlg == dlg) - lck->is_alive = 0; - lck = lck->prev; - } -} - -/* - * Initialize dialog from the received request. - * This is an internal function which is called by the User Agent (sip_ua.c), - * and it will initialize most of dialog's properties with values from the - * received message. - */ -pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata ) -{ - pjsip_msg *msg = rdata->msg; - pjsip_to_hdr *to; - pjsip_contact_hdr *contact; - pjsip_name_addr *name_addr; - pjsip_url *url; - unsigned flag; - pjsip_event event; - - pj_assert(dlg && rdata); - - PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata)); - - /* Must be an INVITE request. */ - pj_assert(msg->type == PJSIP_REQUEST_MSG && - msg->line.req.method.id == PJSIP_INVITE_METHOD); - - /* Init general dialog data. */ - if (dlg_init(dlg) != PJ_SUCCESS) { - return -1; - } - - /* Get the To header. */ - to = rdata->to; - - /* Copy URI in the To header as our local URI. */ - dlg->local.info = pjsip_hdr_clone( dlg->pool, to); - - /* Set tag in the local info. */ - dlg->local.info->tag = dlg->local.tag; - - /* Create local Contact to be advertised in the response. - * At the moment, just copy URI from the local URI as our contact. - */ - dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); - dlg->local.contact->star = 0; - name_addr = (pjsip_name_addr *)dlg->local.info->uri; - dlg->local.contact->uri = (pjsip_uri*) name_addr; - url = (pjsip_url*) name_addr->uri; - //url->port = rdata->via->sent_by.port; - //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) ); - - /* Save remote URI. */ - dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from ); - pjsip_fromto_set_to( dlg->remote.info ); - pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag ); - - /* Save remote Contact. */ - contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); - if (contact) { - dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); - } else { - PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s", - pj_sockaddr_get_str_addr(&rdata->addr))); - dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool ); - dlg->remote.contact->uri = dlg->remote.info->uri; - } - - /* Save Call-ID. */ - dlg->call_id = pjsip_cid_hdr_create(dlg->pool); - pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id ); - - /* Initialize local CSeq and save remote CSeq.*/ - dlg->local.cseq = rdata->timestamp.sec & 0xFFFF; - dlg->remote.cseq = rdata->cseq->cseq; - - /* Secure? */ - flag = pjsip_transport_get_flag(rdata->transport); - dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0; - - /* Initial state is NULL. */ - event.type = event.src_type = PJSIP_EVENT_RX_MSG; - event.src.rdata = rdata; - dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); - - PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata)); - return PJ_SUCCESS; -} - -/* - * Set the contact details. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg, - const pj_str_t *contact ) -{ - pjsip_uri *local_uri; - pj_str_t tmp; - - pj_strdup_with_null(dlg->pool, &tmp, contact); - local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (local_uri == NULL) { - PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI")); - return -1; - } - - dlg->local.contact->star = 0; - dlg->local.contact->uri = local_uri; - return 0; -} - -/* - * Set route set. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg, - const pjsip_route_hdr *route_set ) -{ - pjsip_route_hdr *hdr; - - pj_list_init(&dlg->route_set); - hdr = route_set->next; - while (hdr != route_set) { - pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr); - pj_list_insert_before( &dlg->route_set, cloned); - hdr = hdr->next; - } - return 0; -} - -/* - * Set route set without cloning the header. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg, - pjsip_route_hdr *route_set) -{ - pjsip_route_hdr *hdr; - - pj_list_init(&dlg->route_set); - hdr = route_set->next; - while (hdr != route_set) { - pj_list_insert_before( &dlg->route_set, hdr); - hdr = hdr->next; - } - return 0; -} - -/* - * Application calls this function when it wants to initiate an outgoing - * dialog (incoming dialogs are created automatically by UA when it receives - * INVITE, by calling pjsip_dlg_init_from_rdata()). - * This function should initialize most of the dialog's properties. - */ -PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg, - const pj_str_t *c_local_info, - const pj_str_t *c_remote_info, - const pj_str_t *c_target) -{ - pj_time_val tv; - pjsip_event event; - pj_str_t buf; - - if (!dlg || !c_local_info || !c_remote_info) { - pj_assert(dlg && c_local_info && c_remote_info); - return -1; - } - - PJ_LOG(5, (dlg->obj_name, "initializing")); - - /* Init general dialog */ - if (dlg_init(dlg) != PJ_SUCCESS) { - return -1; - } - - /* Duplicate local info. */ - pj_strdup_with_null( dlg->pool, &buf, c_local_info); - - /* Build local URI. */ - dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (dlg->local.target == NULL) { - PJ_LOG(2, (dlg->obj_name, - "pjsip_dlg_init: invalid local URI %s", buf.ptr)); - return -1; - } - - /* Set local URI. */ - dlg->local.info = pjsip_from_hdr_create(dlg->pool); - dlg->local.info->uri = dlg->local.target; - dlg->local.info->tag = dlg->local.tag; - - /* Create local Contact to be advertised in the response. */ - dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); - dlg->local.contact->star = 0; - dlg->local.contact->uri = dlg->local.target; - - /* Set remote URI. */ - dlg->remote.info = pjsip_to_hdr_create(dlg->pool); - - /* Duplicate to buffer. */ - pj_strdup_with_null( dlg->pool, &buf, c_remote_info); - - /* Build remote info. */ - dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (dlg->remote.info->uri == NULL) { - PJ_LOG(2, (dlg->obj_name, - "pjsip_dlg_init: invalid remote URI %s", buf.ptr)); - return -1; - } - - /* Set remote Contact initially equal to the remote URI. */ - dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); - dlg->remote.contact->star = 0; - dlg->remote.contact->uri = dlg->remote.info->uri; - - /* Set initial remote target. */ - if (c_target != NULL) { - pj_strdup_with_null( dlg->pool, &buf, c_target); - dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0); - if (dlg->remote.target == NULL) { - PJ_LOG(2, (dlg->obj_name, - "pjsip_dlg_init: invalid remote target %s", buf.ptr)); - return -1; - } - } else { - dlg->remote.target = dlg->remote.info->uri; - } - - /* Create globally unique Call-ID */ - dlg->call_id = pjsip_cid_hdr_create(dlg->pool); - pj_create_unique_string( dlg->pool, &dlg->call_id->id ); - - /* Local and remote CSeq */ - pj_gettimeofday(&tv); - dlg->local.cseq = tv.sec & 0xFFFF; - dlg->remote.cseq = 0; - - /* Initial state is NULL. */ - event.type = event.src_type = PJSIP_EVENT_TX_MSG; - event.src.data = NULL; - dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); - - /* Done. */ - PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s", - pjsip_role_name(dlg->role), - c_local_info->slen, c_local_info->ptr, - c_remote_info->slen, c_remote_info->ptr)); - - return PJ_SUCCESS; -} - -/* - * Set credentials. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg, - int count, - const pjsip_cred_info *cred) -{ - if (count > 0) { - dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info)); - pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info)); - } - dlg->cred_count = count; - return 0; -} - -/* - * Create a new request within dialog (i.e. after the dialog session has been - * established). The construction of such requests follows the rule in - * RFC3261 section 12.2.1. - */ -static void dlg_create_request_throw( pjsip_tx_data **p_tdata, - pjsip_dlg *dlg, - const pjsip_method *method, - int cseq ) -{ - pjsip_tx_data *tdata; - pjsip_contact_hdr *contact; - pjsip_route_hdr *route, *end_list; - - /* Contact Header field. - * Contact can only be present in requests that establish dialog (in the - * core SIP spec, only INVITE). - */ - if (method->id == PJSIP_INVITE_METHOD) - contact = dlg->local.contact; - else - contact = NULL; - - tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt, - method, - dlg->remote.target, - dlg->local.info, - dlg->remote.info, - contact, - dlg->call_id, - cseq, - NULL); - if (!tdata) { - PJ_THROW(1); - return; - } - - /* Just copy dialog route-set to Route header. - * The transaction will do the processing as specified in Section 12.2.1 - * of RFC 3261 in function tsx_process_route() in sip_transaction.c. - */ - route = dlg->route_set.next; - end_list = &dlg->route_set; - for (; route != end_list; route = route->next ) { - pjsip_route_hdr *r; - r = pjsip_hdr_shallow_clone( tdata->pool, route ); - pjsip_routing_hdr_set_route(r); - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r); - } - - /* Copy authorization headers. */ - pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess, - dlg->cred_count, dlg->cred_info); - - *p_tdata = tdata; -} - - -/* - * This function is called by application to create new outgoing request - * message for this dialog. After the request is created, application can - * modify the message (such adding headers), and eventually send the request. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg, - const pjsip_method *method, - int cseq) -{ - PJ_USE_EXCEPTION; - struct dialog_lock_data lck; - pjsip_tx_data *tdata = NULL; - - pj_assert(dlg != NULL && method != NULL); - if (!dlg || !method) { - return NULL; - } - - PJ_LOG(5, (dlg->obj_name, "Creating request")); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Use outgoing CSeq and increment it by one. */ - if (cseq < 0) - cseq = dlg->local.cseq + 1; - - PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d", - method->name.slen, method->name.ptr, cseq)); - - /* Create the request. */ - PJ_TRY { - dlg_create_request_throw(&tdata, dlg, method, cseq); - PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name)); - } - PJ_DEFAULT { - /* Failed! Delete transmit data. */ - if (tdata) { - pjsip_tx_data_dec_ref( tdata ); - tdata = NULL; - } - } - PJ_END; - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return tdata; -} - -/* - * Sends request. - * Select the transport for the request message - */ -static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata ) -{ - pjsip_transaction *tsx; - pj_status_t status = PJ_SUCCESS; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL && tdata != NULL); - if (!dlg || !tdata) { - return -1; - } - - PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name)); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Create a new transaction. */ - tsx = pjsip_endpt_create_tsx( dlg->ua->endpt ); - if (!tsx) { - unlock_dialog(dlg, &lck); - return -1; - } - - PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name)); - - /* Initialize transaction */ - tsx->module_data[dlg->ua->mod_id] = dlg; - status = pjsip_tsx_init_uac( tsx, tdata ); - if (status != PJ_SUCCESS) { - unlock_dialog(dlg, &lck); - pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx ); - return -1; - } - pjsip_endpt_register_tsx( dlg->ua->endpt, tsx ); - - /* Start the transaction. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return status; -} - -/* - * This function can be called by application to send ANY outgoing message - * to remote party. - */ -PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata ) -{ - pj_status_t status; - int tsx_status; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL && tdata != NULL); - if (!dlg || !tdata) { - return -1; - } - - lock_dialog(dlg, &lck); - - if (tdata->msg->type == PJSIP_REQUEST_MSG) { - int request_cseq; - pjsip_msg *msg = tdata->msg; - pjsip_cseq_hdr *cseq_hdr; - - switch (msg->line.req.method.id) { - case PJSIP_CANCEL_METHOD: - - /* Check the INVITE transaction state. */ - tsx_status = dlg->invite_tsx->status_code; - if (tsx_status >= 200) { - /* Already terminated. Can't cancel. */ - status = -1; - goto on_return; - } - - /* If we've got provisional response, then send CANCEL and wait for - * the response to INVITE to arrive. Otherwise just send CANCEL and - * terminate the INVITE. - */ - if (tsx_status < 100) { - pjsip_tsx_terminate( dlg->invite_tsx, - PJSIP_SC_REQUEST_TERMINATED); - status = 0; - goto on_return; - } - - status = 0; - request_cseq = dlg->invite_tsx->cseq; - break; - - case PJSIP_ACK_METHOD: - /* Sending ACK outside of transaction is not supported at present! */ - pj_assert(0); - status = 0; - request_cseq = dlg->local.cseq; - break; - - case PJSIP_INVITE_METHOD: - /* For an initial INVITE, reset dialog state to NULL so we get - * 'normal' UAC notifications such as on_provisional(), etc. - * Initial INVITE is the request that is sent when the dialog has - * not been established yet. It's not necessarily the first INVITE - * sent, as when the Authorization fails, subsequent INVITE are also - * considered as an initial INVITE. - */ - if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { - /* Set state to NULL. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL); - - } else { - /* This is a re-INVITE */ - } - status = 0; - request_cseq = dlg->local.cseq + 1; - break; - - default: - status = 0; - request_cseq = dlg->local.cseq + 1; - break; - } - - if (status != 0) - goto on_return; - - /* Update dialog's local CSeq, if necessary. */ - if (request_cseq != dlg->local.cseq) - dlg->local.cseq = request_cseq; - - /* Update CSeq header in the request. */ - cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, - PJSIP_H_CSEQ, NULL); - pj_assert(cseq_hdr != NULL); - - /* Update the CSeq */ - cseq_hdr->cseq = request_cseq; - - /* Force the whole message to be re-printed. */ - pjsip_tx_data_invalidate_msg( tdata ); - - /* Now send the request. */ - status = dlg_send_request(dlg, tdata); - - } else { - /* - * This is only valid for sending response to INVITE! - */ - pjsip_cseq_hdr *cseq_hdr; - - if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) { - status = -1; - goto on_return; - } - - cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, - PJSIP_H_CSEQ, NULL); - pj_assert(cseq_hdr); - - if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) { - status = -1; - goto on_return; - } - - pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq); - - pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata); - status = 0; - } - -on_return: - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - /* Whatever happen delete the message. */ - pjsip_tx_data_dec_ref( tdata ); - - return status; -} - -/* - * Sends outgoing invitation. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg ) -{ - pjsip_method method; - struct dialog_lock_data lck; - const pjsip_allow_hdr *allow_hdr; - pjsip_tx_data *tdata; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "request to send invitation")); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Create request. */ - pjsip_method_set( &method, PJSIP_INVITE_METHOD); - tdata = pjsip_dlg_create_request( dlg, &method, -1 ); - if (tdata == NULL) { - unlock_dialog(dlg, &lck); - return NULL; - } - - /* Invite SHOULD contain "Allow" header. */ - allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); - if (allow_hdr) { - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); - } - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return tdata; -} - -/* - * Cancel pending outgoing dialog invitation. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg ) -{ - pjsip_tx_data *tdata = NULL; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "request to cancel invitation")); - - lock_dialog(dlg, &lck); - - /* Check the INVITE transaction. */ - if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " - "no INVITE transaction found")); - goto on_return; - } - - /* Construct the CANCEL request. */ - tdata = pjsip_endpt_create_cancel( dlg->ua->endpt, - dlg->invite_tsx->last_tx ); - if (tdata == NULL) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " - "unable to construct request")); - goto on_return; - } - - /* Add reference counter to tdata. */ - pjsip_tx_data_add_ref(tdata); - -on_return: - unlock_dialog(dlg, &lck); - return tdata; -} - - -/* - * Answer incoming dialog invitation, with either provisional responses - * or a final response. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code ) -{ - pjsip_tx_data *tdata = NULL; - pjsip_msg *msg; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code)); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Must have pending INVITE. */ - if (dlg->invite_tsx == NULL) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found")); - goto on_return; - } - /* Must be UAS. */ - if (dlg->role != PJSIP_ROLE_UAS) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS")); - goto on_return; - } - /* Must have not answered with final response before. */ - if (dlg->invite_tsx->status_code >= 200) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated " - "with status %d", dlg->invite_tsx->status_code)); - goto on_return; - } - - /* Get transmit data and the message. - * We will rewrite the message with a new status code. - */ - tdata = dlg->invite_tsx->last_tx; - msg = tdata->msg; - - /* Set status code and reason phrase. */ - if (code < 100 || code >= 700) code = 500; - msg->line.status.code = code; - msg->line.status.reason = *pjsip_get_status_text(code); - - /* For 2xx response, Contact and Record-Route must be added. */ - if (PJSIP_IS_STATUS_IN_CLASS(code,200)) { - const pjsip_allow_hdr *allow_hdr; - - if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) { - pjsip_contact_hdr *contact; - contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact); - pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact ); - } - - /* 2xx response MUST contain "Allow" header. */ - allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); - if (allow_hdr) { - pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); - } - } - - /* for all but 100 responses, To-tag must be set. */ - if (code != 100) { - pjsip_to_hdr *to; - to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL); - to->tag = dlg->local.tag; - } - - /* Reset packet buffer. */ - pjsip_tx_data_invalidate_msg(tdata); - - /* Add reference counter */ - pjsip_tx_data_add_ref(tdata); - -on_return: - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return tdata; -} - - -/* - * Send BYE request to terminate the dialog's session. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg ) -{ - pjsip_method method; - struct dialog_lock_data lck; - pjsip_tx_data *tdata; - - if (!dlg) { - pj_assert(dlg != NULL); - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "request to terminate session")); - - lock_dialog(dlg, &lck); - - pjsip_method_set( &method, PJSIP_BYE_METHOD); - tdata = pjsip_dlg_create_request( dlg, &method, -1 ); - - unlock_dialog(dlg, &lck); - - return tdata; -} - -/* - * High level function to disconnect dialog's session. Depending on dialog's - * state, this function will either send CANCEL, final response, or BYE to - * trigger the disconnection. A status code must be supplied, which will be - * sent if dialog will be transmitting a final response to INVITE. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, - int status_code ) -{ - pjsip_tx_data *tdata = NULL; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - switch (dlg->state) { - case PJSIP_DIALOG_STATE_INCOMING: - tdata = pjsip_dlg_answer(dlg, status_code); - break; - - case PJSIP_DIALOG_STATE_CALLING: - tdata = pjsip_dlg_cancel(dlg); - break; - - case PJSIP_DIALOG_STATE_PROCEEDING: - if (dlg->role == PJSIP_ROLE_UAC) { - tdata = pjsip_dlg_cancel(dlg); - } else { - tdata = pjsip_dlg_answer(dlg, status_code); - } - break; - - case PJSIP_DIALOG_STATE_ESTABLISHED: - tdata = pjsip_dlg_bye(dlg); - break; - - default: - PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()", - dlg_state_names[dlg->state])); - break; - } - - return tdata; -} - -/* - * Handling of the receipt of 2xx/INVITE response. - */ -static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg, - pjsip_event *event ) -{ - pjsip_msg *msg; - pjsip_contact_hdr *contact; - pjsip_hdr *hdr, *end_hdr; - pjsip_method method; - pjsip_tx_data *ack_tdata; - - /* Get the message */ - msg = event->src.rdata->msg; - - /* Update remote's tag information. */ - pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag); - - /* Copy Contact information in the 2xx/INVITE response to dialog's. - * remote contact - */ - contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); - if (contact) { - dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); - } else { - /* duplicate contact from "From" header (?) */ - PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!")); - dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); - dlg->remote.contact->uri = dlg->remote.info->uri; - } - - /* Copy Record-Route header (in reverse order) as dialog's route-set, - * overwriting previous route-set, if any, even if the received route-set - * is empty. - */ - pj_list_init(&dlg->route_set); - end_hdr = &msg->hdr; - for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) { - if (hdr->type == PJSIP_H_RECORD_ROUTE) { - pjsip_route_hdr *r; - r = pjsip_hdr_clone(dlg->pool, hdr); - pjsip_routing_hdr_set_route(r); - pj_list_insert_before(&dlg->route_set, r); - } - } - - /* On receipt of 200/INVITE response, send ACK. - * This ack must be saved and retransmitted whenever we receive - * 200/INVITE retransmission, until 64*T1 seconds elapsed. - */ - pjsip_method_set( &method, PJSIP_ACK_METHOD); - ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq); - if (ack_tdata == NULL) { - //PJ_TODO(HANDLE_CREATE_ACK_FAILURE) - PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request")); - return; - } - - /* Send with the transaction. */ - pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata); - - /* Decrement reference counter because pjsip_dlg_create_request - * automatically increments the request. - */ - pjsip_tx_data_dec_ref( ack_tdata ); -} - -/* - * State NULL, before any events have been received. - */ -static int dlg_on_state_null( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG) - { - pjsip_hdr *hdr, *hdr_list; - - pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); - - /* Save the INVITE transaction. */ - dlg->invite_tsx = tsx; - - /* Change state to INCOMING */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event); - - /* Create response buffer. */ - tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100); - pjsip_tx_data_add_ref(tsx->last_tx); - - /* Copy the Record-Route headers into dialog's route_set, maintaining - * the order. - */ - pj_list_init(&dlg->route_set); - hdr_list = &event->src.rdata->msg->hdr; - hdr = hdr_list->next; - while (hdr != hdr_list) { - if (hdr->type == PJSIP_H_RECORD_ROUTE) { - pjsip_route_hdr *route; - route = pjsip_hdr_clone(dlg->pool, hdr); - pjsip_routing_hdr_set_route(route); - pj_list_insert_before(&dlg->route_set, route); - } - hdr = hdr->next; - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_TX_MSG) - { - pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); - - /* Save the INVITE transaction. */ - dlg->invite_tsx = tsx; - - /* Change state to CALLING. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return 0; -} - -/* - * State INCOMING is after the (callee) dialog has been initialized with - * the incoming request, but before any responses is sent by the dialog. - */ -static int dlg_on_state_incoming( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - return dlg_on_state_proceeding_callee( dlg, tsx, event ); -} - -/* - * State CALLING is after the (caller) dialog has sent outgoing invitation - * but before any responses are received. - */ -static int dlg_on_state_calling( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (tsx == dlg->invite_tsx) { - return dlg_on_state_proceeding_caller( dlg, tsx, event ); - } - return 0; -} - -/* - * State PROCEEDING is after provisional response is received. - * Since the processing is similar to state CALLING, this function is also - * called for CALLING state. - */ -static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - int dlg_is_terminated = 0; - - /* We only care about our INVITE transaction. - * Ignore other transaction progression (such as CANCEL). - */ - if (tsx != dlg->invite_tsx) { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - return 0; - } - - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) { - switch (tsx->state) { - case PJSIP_TSX_STATE_PROCEEDING: - if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) { - /* Change state to PROCEEDING */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - } else { - /* Also notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - break; - - case PJSIP_TSX_STATE_COMPLETED: - /* Change dialog state. */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - /* Update remote target, take it from the contact hdr. */ - pjsip_contact_hdr *contact; - contact = pjsip_msg_find_hdr(event->src.rdata->msg, - PJSIP_H_CONTACT, NULL); - if (contact) { - dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri); - } else { - PJ_LOG(4,(dlg->obj_name, - "Warning: found no Contact hdr in 200/OK")); - } - dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); - } else if (tsx->status_code==401 || tsx->status_code==407) { - /* Handle Authentication challenge. */ - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( dlg->ua->endpt, - dlg->pool, &dlg->auth_sess, - dlg->cred_count, dlg->cred_info, - tsx->last_tx, event->src.rdata); - if (tdata) { - /* Re-use original request, with a new transaction. - * Need not to worry about CSeq, dialog will take care. - */ - pjsip_dlg_send_msg(dlg, tdata); - return 0; - } else { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - } else { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* Send ACK when dialog is connected. */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - pj_assert(event->src_type == PJSIP_EVENT_RX_MSG); - dlg_on_recv_2xx_invite(dlg, event); - } - break; - - case PJSIP_TSX_STATE_TERMINATED: - /* - * Transaction is terminated because of timeout or transport error. - * To let the application go to normal state progression, call the - * callback twice. First is to emulate disconnection, and then call - * again (with state TERMINATED) to destroy the dialog. - */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* The INVITE transaction will be destroyed, so release reference - * to it. - */ - dlg->invite_tsx = NULL; - - /* We should terminate the dialog now. - * But it's possible that we have other pending transactions (for - * example, outgoing CANCEL is in progress). - * So destroy the dialog only if there's no other transaction. - */ - if (dlg->pending_tsx_count == 0) { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - dlg_is_terminated = 1; - } - break; - - default: - pj_assert(0); - break; - } - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - return dlg_is_terminated ? -1 : 0; -} - -/* - * State PROCEEDING for UAS is after the callee send provisional response. - * This function is also called for INCOMING state. - */ -static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - int dlg_is_terminated = 0; - - pj_assert( dlg->invite_tsx != NULL ); - - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_TX_MSG && - tsx == dlg->invite_tsx) - { - switch (tsx->state) { - case PJSIP_TSX_STATE_PROCEEDING: - /* Change state to PROCEEDING */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - break; - - case PJSIP_TSX_STATE_COMPLETED: - case PJSIP_TSX_STATE_TERMINATED: - /* Change dialog state. */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); - } else { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* If transaction is terminated in non-2xx situation, - * terminate dialog as well. This happens when something unexpected - * occurs, such as transport error. - */ - if (tsx->state == PJSIP_TSX_STATE_TERMINATED && - !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) - { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - dlg_is_terminated = 1; - } - break; - - default: - pj_assert(0); - break; - } - - } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->method.id == PJSIP_CANCEL_METHOD) - { - pjsip_tx_data *tdata; - - /* Check if sequence number matches the pending INVITE. */ - if (dlg->invite_tsx==NULL || - pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0) - { - PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE")); - - /* No matching INVITE transaction found. */ - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - PJSIP_SC_CALL_TSX_DOES_NOT_EXIST ); - pjsip_tsx_on_tx_msg(tsx, tdata); - return 0; - } - - /* Always respond the CANCEL with 200/CANCEL no matter what. */ - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - 200 ); - pjsip_tsx_on_tx_msg( tsx, tdata ); - - /* Respond the INVITE transaction with 487, only if transaction has - * not completed. - */ - if (dlg->invite_tsx->last_tx) { - if (dlg->invite_tsx->status_code < 200) { - tdata = dlg->invite_tsx->last_tx; - tdata->msg->line.status.code = 487; - tdata->msg->line.status.reason = *pjsip_get_status_text(487); - /* Reset packet buffer. */ - pjsip_tx_data_invalidate_msg(tdata); - pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); - } else { - PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, " - "Transaction already terminated " - "with status %d", - dlg->invite_tsx->status_code)); - } - } else { - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - 487); - pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); - } - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return dlg_is_terminated ? -1 : 0; -} - -static int dlg_on_state_proceeding( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (dlg->role == PJSIP_ROLE_UAC) { - return dlg_on_state_proceeding_caller( dlg, tsx, event ); - } else { - return dlg_on_state_proceeding_callee( dlg, tsx, event ); - } -} - -static int dlg_on_state_connecting( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (tsx == dlg->invite_tsx) { - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - (tsx->state == PJSIP_TSX_STATE_TERMINATED || - tsx->state == PJSIP_TSX_STATE_COMPLETED || - tsx->state == PJSIP_TSX_STATE_CONFIRMED)) - { - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event); - } else { - /* Probably because we never get the ACK, or transport error - * when sending ACK. - */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - } else { - /* Handle case when transaction is started when dialog is connecting - * (e.g. BYE requests cross wire. - */ - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->role == PJSIP_ROLE_UAS) - { - pjsip_tx_data *response; - - if (tsx->status_code >= 200) - return 0; - - if (tsx->method.id == PJSIP_BYE_METHOD) { - /* Set state to DISCONNECTED. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - response = pjsip_endpt_create_response( dlg->ua->endpt, - event->src.rdata, 200); - } else { - response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, - PJSIP_SC_INTERNAL_SERVER_ERROR); - } - - if (response) - pjsip_tsx_on_tx_msg(tsx, response); - - return 0; - } - } - return 0; -} - -static int dlg_on_state_established( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(tsx) - - if (tsx && tsx->method.id == PJSIP_BYE_METHOD) { - /* Set state to DISCONNECTED. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* Answer with 200/BYE. */ - if (event->src_type == PJSIP_EVENT_RX_MSG) { - pjsip_tx_data *tdata; - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - 200 ); - if (tdata) - pjsip_tsx_on_tx_msg( tsx, tdata ); - } - } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) { - pjsip_method_e method = event->src.rdata->cseq->method.id; - - PJ_TODO(PROPERLY_HANDLE_REINVITATION) - - /* Reinvitation. The message may be INVITE or an ACK. */ - if (method == PJSIP_INVITE_METHOD) { - if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) { - /* Section 14.2: A UAS that receives a second INVITE before it - * sends the final response to a first INVITE with a lower - * CSeq sequence number on the same dialog MUST return a 500 - * (Server Internal Error) response to the second INVITE and - * MUST include a Retry-After header field with a randomly - * chosen value of between 0 and 10 seconds. - */ - pjsip_retry_after_hdr *hdr; - pjsip_tx_data *tdata = - pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, 500); - - if (!tdata) - return 0; - - /* Add Retry-After. */ - hdr = pjsip_retry_after_hdr_create(tdata->pool); - hdr->ivalue = 9; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); - - /* Send. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - return 0; - } - - /* Keep this as our current INVITE transaction. */ - dlg->invite_tsx = tsx; - - /* Create response buffer. */ - tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, - event->src.rdata, 100); - pjsip_tx_data_add_ref(tsx->last_tx); - - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event); - - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return 0; -} - -static int dlg_on_state_disconnected( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(tsx) - - /* Handle case when transaction is started when dialog is disconnected - * (e.g. BYE requests cross wire. - */ - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->role == PJSIP_ROLE_UAS) - { - pjsip_tx_data *response = NULL; - - if (tsx->status_code >= 200) - return 0; - - if (tsx->method.id == PJSIP_BYE_METHOD) { - response = pjsip_endpt_create_response( dlg->ua->endpt, - event->src.rdata, 200); - } else { - response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, - PJSIP_SC_INTERNAL_SERVER_ERROR); - } - if (response) - pjsip_tsx_on_tx_msg(tsx, response); - - return 0; - } - /* Handle case when outgoing BYE was rejected with 401/407 */ - else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->role == PJSIP_ROLE_UAC) - { - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool, - &dlg->auth_sess, - dlg->cred_count, dlg->cred_info, - tsx->last_tx, event->src.rdata); - if (tdata) { - pjsip_dlg_send_msg(dlg, tdata); - } - } - } - - - if (dlg->pending_tsx_count == 0) { - /* Set state to TERMINATED. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - return -1; - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return 0; -} - -static int dlg_on_state_terminated( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(dlg) - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(event) - - return -1; -} - +/* $Header: /pjproject-0.3/pjsip/src/pjsip_mod_ua/sip_dialog.c 27 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TLS to keep dialog lock record (initialized by UA) */ +int pjsip_dlg_lock_tls_id; + +struct dialog_lock_data +{ + struct dialog_lock_data *prev; + pjsip_dlg *dlg; + int is_alive; +}; + +/* + * Static function prototypes. + */ +static void dlg_create_request_throw( pjsip_tx_data **p_tdata, + pjsip_dlg *dlg, + const pjsip_method *method, + int cseq ); +static int dlg_on_all_state_pre( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_all_state_post( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_null( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_incoming( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_calling( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_proceeding( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_connecting( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_established( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_disconnected( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); +static int dlg_on_state_terminated( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); + +/* + * Dialog state handlers. + */ +static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *, + pjsip_event *) = +{ + &dlg_on_state_null, + &dlg_on_state_incoming, + &dlg_on_state_calling, + &dlg_on_state_proceeding, + &dlg_on_state_connecting, + &dlg_on_state_established, + &dlg_on_state_disconnected, + &dlg_on_state_terminated, +}; + +/* + * Dialog state names. + */ +static const char* dlg_state_names[] = +{ + "STATE_NULL", + "STATE_INCOMING", + "STATE_CALLING", + "STATE_PROCEEDING", + "STATE_CONNECTING", + "STATE_ESTABLISHED", + "STATE_DISCONNECTED", + "STATE_TERMINATED", +}; + + +/* + * Get the dialog string state, normally for logging purpose. + */ +const char *pjsip_dlg_state_str(pjsip_dlg_state_e state) +{ + return dlg_state_names[state]; +} + +/* Lock dialog mutex. */ +static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) +{ + struct dialog_lock_data *prev; + + pj_mutex_lock(dlg->mutex); + prev = pj_thread_local_get(pjsip_dlg_lock_tls_id); + lck->prev = prev; + lck->dlg = dlg; + lck->is_alive = 1; + pj_thread_local_set(pjsip_dlg_lock_tls_id, lck); +} + +/* Carefully unlock dialog mutex, protect against situation when the dialog + * has already been destroyed. + */ +static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) +{ + pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck); + pj_assert(dlg == lck->dlg); + + pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev); + if (lck->is_alive) + pj_mutex_unlock(dlg->mutex); + + return lck->is_alive ? 0 : -1; +} + +/* + * This is called by dialog's FSM to change dialog's state. + */ +static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state, + pjsip_event *event) +{ + PJ_UNUSED_ARG(event); + + PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)", + pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state), + event ? pjsip_event_str(event->type) : "", + event ? pjsip_event_str(event->src_type) : "", + event ? event->src.data : NULL)); + + dlg->state = state; + dlg->handle_tsx_event = dlg_state_handlers[state]; +} + +/* + * Invoke dialog's callback. + * This function is called by dialog's FSM, and interpret the event and call + * the corresponding callback registered by application. + */ +static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, + pjsip_event *event ) +{ + pjsip_dlg_callback *cb = dlg->ua->dlg_cb; + if (!cb) { + PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered).")); + return; + } + + /* Low level event: call the all-events callback. */ + if (cb->on_all_events) { + (*cb->on_all_events)(dlg, dlg_event, event); + } + + /* Low level event: call the tx/rx callback if this is a tx/rx event. */ + if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx) + { + (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data); + } + else if (event->type == PJSIP_EVENT_TX_MSG && + event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg) + { + (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata); + } + else if (event->type == PJSIP_EVENT_RX_MSG && + event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) { + (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata); + } + + /* Now trigger high level events. + * High level event should only occurs when dialog's state has changed, + * except for on_provisional, which may be called multiple times whenever + * response message is sent + */ + if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING && + (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) && + event->obj.tsx == dlg->invite_tsx) + { + /* Sent/received provisional responses. */ + if (cb->on_provisional) + (*cb->on_provisional)(dlg, event->obj.tsx, event); + } + + if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) { + if (cb->on_mid_call_events) + (*cb->on_mid_call_events)(dlg, event); + return; + } + + if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED) + return; + + if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) { + + /* New incoming dialog. */ + pj_assert (event->src_type == PJSIP_EVENT_RX_MSG); + (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata); + + } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) { + + /* Dialog has just sent the first INVITE. */ + if (cb->on_calling) { + (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata); + } + + } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) { + + if (cb->on_disconnected) + (*cb->on_disconnected)(dlg, event); + + } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) { + + if (cb->on_terminated) + (*cb->on_terminated)(dlg); + + pjsip_ua_destroy_dialog(dlg); + + } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) { + + if (cb->on_connecting) + (*cb->on_connecting)(dlg, event); + + } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) { + + if (cb->on_established) + (*cb->on_established)(dlg, event); + } +} + +/* + * This callback receives event from the transaction layer (via User Agent), + * or from dialog timer (for 200/INVITE or ACK retransmission). + */ +void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + int status = 0; + struct dialog_lock_data lck; + + PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)", + pjsip_dlg_state_str(dlg->state), + pjsip_event_str(event->type), + pjsip_event_str(event->src_type))); + + + lock_dialog(dlg, &lck); + + status = dlg_on_all_state_pre( dlg, tsx, event); + + if (status==0) { + status = (*dlg->handle_tsx_event)(dlg, tsx, event); + } + if (status==0) { + status = dlg_on_all_state_post( dlg, tsx, event); + } + + unlock_dialog(dlg, &lck); +} + +/* + * This function contains common processing in all states, and it is called + * before the FSM is invoked. + */ +static int dlg_on_all_state_pre( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(event) + + if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED) + return 0; + + if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING || + tsx->state==PJSIP_TSX_STATE_TRYING)) + { + ++dlg->pending_tsx_count; + + } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) + { + --dlg->pending_tsx_count; + if (tsx == dlg->invite_tsx) + dlg->invite_tsx = NULL; + } + + if (tsx->method.id == PJSIP_INVITE_METHOD) { + tsx->handle_ack = 1; + } + return 0; +} + + +/* + * This function contains common processing in all states, and it is called + * after the FSM is invoked. + */ +static int dlg_on_all_state_post( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(event) + + if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) { + if (dlg->pending_tsx_count == 0 && + dlg->state != PJSIP_DIALOG_STATE_CONNECTING && + dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED && + dlg->state != PJSIP_DIALOG_STATE_TERMINATED) + { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + return -1; + } + } + + return 0; +} + + +/* + * Internal function to initialize dialog, contains common initialization + * for both UAS and UAC dialog. + */ +static pj_status_t dlg_init( pjsip_dlg *dlg ) +{ + /* Init mutex. */ + dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); + if (!dlg->mutex) { + PJ_PERROR((dlg->obj_name, "pj_mutex_create()")); + return -1; + } + + /* Init route-set (Initially empty) */ + pj_list_init(&dlg->route_set); + + /* Init auth credential list. */ + pj_list_init(&dlg->auth_sess); + + return PJ_SUCCESS; +} + +/* + * This one is called just before dialog is destroyed. + * It is called while mutex is held. + */ +PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ) +{ + struct dialog_lock_data *lck; + + //PJ_TODO(CHECK_THIS); + pj_assert(dlg->pending_tsx_count == 0); + + /* Mark dialog as dead. */ + lck = pj_thread_local_get(pjsip_dlg_lock_tls_id); + while (lck) { + if (lck->dlg == dlg) + lck->is_alive = 0; + lck = lck->prev; + } +} + +/* + * Initialize dialog from the received request. + * This is an internal function which is called by the User Agent (sip_ua.c), + * and it will initialize most of dialog's properties with values from the + * received message. + */ +pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata ) +{ + pjsip_msg *msg = rdata->msg; + pjsip_to_hdr *to; + pjsip_contact_hdr *contact; + pjsip_name_addr *name_addr; + pjsip_url *url; + unsigned flag; + pjsip_event event; + + pj_assert(dlg && rdata); + + PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata)); + + /* Must be an INVITE request. */ + pj_assert(msg->type == PJSIP_REQUEST_MSG && + msg->line.req.method.id == PJSIP_INVITE_METHOD); + + /* Init general dialog data. */ + if (dlg_init(dlg) != PJ_SUCCESS) { + return -1; + } + + /* Get the To header. */ + to = rdata->to; + + /* Copy URI in the To header as our local URI. */ + dlg->local.info = pjsip_hdr_clone( dlg->pool, to); + + /* Set tag in the local info. */ + dlg->local.info->tag = dlg->local.tag; + + /* Create local Contact to be advertised in the response. + * At the moment, just copy URI from the local URI as our contact. + */ + dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); + dlg->local.contact->star = 0; + name_addr = (pjsip_name_addr *)dlg->local.info->uri; + dlg->local.contact->uri = (pjsip_uri*) name_addr; + url = (pjsip_url*) name_addr->uri; + //url->port = rdata->via->sent_by.port; + //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) ); + + /* Save remote URI. */ + dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from ); + pjsip_fromto_set_to( dlg->remote.info ); + pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag ); + + /* Save remote Contact. */ + contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); + if (contact) { + dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); + } else { + PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s", + pj_sockaddr_get_str_addr(&rdata->addr))); + dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool ); + dlg->remote.contact->uri = dlg->remote.info->uri; + } + + /* Save Call-ID. */ + dlg->call_id = pjsip_cid_hdr_create(dlg->pool); + pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id ); + + /* Initialize local CSeq and save remote CSeq.*/ + dlg->local.cseq = rdata->timestamp.sec & 0xFFFF; + dlg->remote.cseq = rdata->cseq->cseq; + + /* Secure? */ + flag = pjsip_transport_get_flag(rdata->transport); + dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0; + + /* Initial state is NULL. */ + event.type = event.src_type = PJSIP_EVENT_RX_MSG; + event.src.rdata = rdata; + dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); + + PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata)); + return PJ_SUCCESS; +} + +/* + * Set the contact details. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg, + const pj_str_t *contact ) +{ + pjsip_uri *local_uri; + pj_str_t tmp; + + pj_strdup_with_null(dlg->pool, &tmp, contact); + local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (local_uri == NULL) { + PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI")); + return -1; + } + + dlg->local.contact->star = 0; + dlg->local.contact->uri = local_uri; + return 0; +} + +/* + * Set route set. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg, + const pjsip_route_hdr *route_set ) +{ + pjsip_route_hdr *hdr; + + pj_list_init(&dlg->route_set); + hdr = route_set->next; + while (hdr != route_set) { + pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr); + pj_list_insert_before( &dlg->route_set, cloned); + hdr = hdr->next; + } + return 0; +} + +/* + * Set route set without cloning the header. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg, + pjsip_route_hdr *route_set) +{ + pjsip_route_hdr *hdr; + + pj_list_init(&dlg->route_set); + hdr = route_set->next; + while (hdr != route_set) { + pj_list_insert_before( &dlg->route_set, hdr); + hdr = hdr->next; + } + return 0; +} + +/* + * Application calls this function when it wants to initiate an outgoing + * dialog (incoming dialogs are created automatically by UA when it receives + * INVITE, by calling pjsip_dlg_init_from_rdata()). + * This function should initialize most of the dialog's properties. + */ +PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg, + const pj_str_t *c_local_info, + const pj_str_t *c_remote_info, + const pj_str_t *c_target) +{ + pj_time_val tv; + pjsip_event event; + pj_str_t buf; + + if (!dlg || !c_local_info || !c_remote_info) { + pj_assert(dlg && c_local_info && c_remote_info); + return -1; + } + + PJ_LOG(5, (dlg->obj_name, "initializing")); + + /* Init general dialog */ + if (dlg_init(dlg) != PJ_SUCCESS) { + return -1; + } + + /* Duplicate local info. */ + pj_strdup_with_null( dlg->pool, &buf, c_local_info); + + /* Build local URI. */ + dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (dlg->local.target == NULL) { + PJ_LOG(2, (dlg->obj_name, + "pjsip_dlg_init: invalid local URI %s", buf.ptr)); + return -1; + } + + /* Set local URI. */ + dlg->local.info = pjsip_from_hdr_create(dlg->pool); + dlg->local.info->uri = dlg->local.target; + dlg->local.info->tag = dlg->local.tag; + + /* Create local Contact to be advertised in the response. */ + dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); + dlg->local.contact->star = 0; + dlg->local.contact->uri = dlg->local.target; + + /* Set remote URI. */ + dlg->remote.info = pjsip_to_hdr_create(dlg->pool); + + /* Duplicate to buffer. */ + pj_strdup_with_null( dlg->pool, &buf, c_remote_info); + + /* Build remote info. */ + dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (dlg->remote.info->uri == NULL) { + PJ_LOG(2, (dlg->obj_name, + "pjsip_dlg_init: invalid remote URI %s", buf.ptr)); + return -1; + } + + /* Set remote Contact initially equal to the remote URI. */ + dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); + dlg->remote.contact->star = 0; + dlg->remote.contact->uri = dlg->remote.info->uri; + + /* Set initial remote target. */ + if (c_target != NULL) { + pj_strdup_with_null( dlg->pool, &buf, c_target); + dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0); + if (dlg->remote.target == NULL) { + PJ_LOG(2, (dlg->obj_name, + "pjsip_dlg_init: invalid remote target %s", buf.ptr)); + return -1; + } + } else { + dlg->remote.target = dlg->remote.info->uri; + } + + /* Create globally unique Call-ID */ + dlg->call_id = pjsip_cid_hdr_create(dlg->pool); + pj_create_unique_string( dlg->pool, &dlg->call_id->id ); + + /* Local and remote CSeq */ + pj_gettimeofday(&tv); + dlg->local.cseq = tv.sec & 0xFFFF; + dlg->remote.cseq = 0; + + /* Initial state is NULL. */ + event.type = event.src_type = PJSIP_EVENT_TX_MSG; + event.src.data = NULL; + dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); + + /* Done. */ + PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s", + pjsip_role_name(dlg->role), + c_local_info->slen, c_local_info->ptr, + c_remote_info->slen, c_remote_info->ptr)); + + return PJ_SUCCESS; +} + +/* + * Set credentials. + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg, + int count, + const pjsip_cred_info *cred) +{ + if (count > 0) { + dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info)); + pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info)); + } + dlg->cred_count = count; + return 0; +} + +/* + * Create a new request within dialog (i.e. after the dialog session has been + * established). The construction of such requests follows the rule in + * RFC3261 section 12.2.1. + */ +static void dlg_create_request_throw( pjsip_tx_data **p_tdata, + pjsip_dlg *dlg, + const pjsip_method *method, + int cseq ) +{ + pjsip_tx_data *tdata; + pjsip_contact_hdr *contact; + pjsip_route_hdr *route, *end_list; + + /* Contact Header field. + * Contact can only be present in requests that establish dialog (in the + * core SIP spec, only INVITE). + */ + if (method->id == PJSIP_INVITE_METHOD) + contact = dlg->local.contact; + else + contact = NULL; + + tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt, + method, + dlg->remote.target, + dlg->local.info, + dlg->remote.info, + contact, + dlg->call_id, + cseq, + NULL); + if (!tdata) { + PJ_THROW(1); + return; + } + + /* Just copy dialog route-set to Route header. + * The transaction will do the processing as specified in Section 12.2.1 + * of RFC 3261 in function tsx_process_route() in sip_transaction.c. + */ + route = dlg->route_set.next; + end_list = &dlg->route_set; + for (; route != end_list; route = route->next ) { + pjsip_route_hdr *r; + r = pjsip_hdr_shallow_clone( tdata->pool, route ); + pjsip_routing_hdr_set_route(r); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r); + } + + /* Copy authorization headers. */ + pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess, + dlg->cred_count, dlg->cred_info); + + *p_tdata = tdata; +} + + +/* + * This function is called by application to create new outgoing request + * message for this dialog. After the request is created, application can + * modify the message (such adding headers), and eventually send the request. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg, + const pjsip_method *method, + int cseq) +{ + PJ_USE_EXCEPTION; + struct dialog_lock_data lck; + pjsip_tx_data *tdata = NULL; + + pj_assert(dlg != NULL && method != NULL); + if (!dlg || !method) { + return NULL; + } + + PJ_LOG(5, (dlg->obj_name, "Creating request")); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Use outgoing CSeq and increment it by one. */ + if (cseq < 0) + cseq = dlg->local.cseq + 1; + + PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d", + method->name.slen, method->name.ptr, cseq)); + + /* Create the request. */ + PJ_TRY { + dlg_create_request_throw(&tdata, dlg, method, cseq); + PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name)); + } + PJ_DEFAULT { + /* Failed! Delete transmit data. */ + if (tdata) { + pjsip_tx_data_dec_ref( tdata ); + tdata = NULL; + } + } + PJ_END; + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return tdata; +} + +/* + * Sends request. + * Select the transport for the request message + */ +static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata ) +{ + pjsip_transaction *tsx; + pj_status_t status = PJ_SUCCESS; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL && tdata != NULL); + if (!dlg || !tdata) { + return -1; + } + + PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name)); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Create a new transaction. */ + tsx = pjsip_endpt_create_tsx( dlg->ua->endpt ); + if (!tsx) { + unlock_dialog(dlg, &lck); + return -1; + } + + PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name)); + + /* Initialize transaction */ + tsx->module_data[dlg->ua->mod_id] = dlg; + status = pjsip_tsx_init_uac( tsx, tdata ); + if (status != PJ_SUCCESS) { + unlock_dialog(dlg, &lck); + pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx ); + return -1; + } + pjsip_endpt_register_tsx( dlg->ua->endpt, tsx ); + + /* Start the transaction. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return status; +} + +/* + * This function can be called by application to send ANY outgoing message + * to remote party. + */ +PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata ) +{ + pj_status_t status; + int tsx_status; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL && tdata != NULL); + if (!dlg || !tdata) { + return -1; + } + + lock_dialog(dlg, &lck); + + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + int request_cseq; + pjsip_msg *msg = tdata->msg; + pjsip_cseq_hdr *cseq_hdr; + + switch (msg->line.req.method.id) { + case PJSIP_CANCEL_METHOD: + + /* Check the INVITE transaction state. */ + tsx_status = dlg->invite_tsx->status_code; + if (tsx_status >= 200) { + /* Already terminated. Can't cancel. */ + status = -1; + goto on_return; + } + + /* If we've got provisional response, then send CANCEL and wait for + * the response to INVITE to arrive. Otherwise just send CANCEL and + * terminate the INVITE. + */ + if (tsx_status < 100) { + pjsip_tsx_terminate( dlg->invite_tsx, + PJSIP_SC_REQUEST_TERMINATED); + status = 0; + goto on_return; + } + + status = 0; + request_cseq = dlg->invite_tsx->cseq; + break; + + case PJSIP_ACK_METHOD: + /* Sending ACK outside of transaction is not supported at present! */ + pj_assert(0); + status = 0; + request_cseq = dlg->local.cseq; + break; + + case PJSIP_INVITE_METHOD: + /* For an initial INVITE, reset dialog state to NULL so we get + * 'normal' UAC notifications such as on_provisional(), etc. + * Initial INVITE is the request that is sent when the dialog has + * not been established yet. It's not necessarily the first INVITE + * sent, as when the Authorization fails, subsequent INVITE are also + * considered as an initial INVITE. + */ + if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { + /* Set state to NULL. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL); + + } else { + /* This is a re-INVITE */ + } + status = 0; + request_cseq = dlg->local.cseq + 1; + break; + + default: + status = 0; + request_cseq = dlg->local.cseq + 1; + break; + } + + if (status != 0) + goto on_return; + + /* Update dialog's local CSeq, if necessary. */ + if (request_cseq != dlg->local.cseq) + dlg->local.cseq = request_cseq; + + /* Update CSeq header in the request. */ + cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, + PJSIP_H_CSEQ, NULL); + pj_assert(cseq_hdr != NULL); + + /* Update the CSeq */ + cseq_hdr->cseq = request_cseq; + + /* Force the whole message to be re-printed. */ + pjsip_tx_data_invalidate_msg( tdata ); + + /* Now send the request. */ + status = dlg_send_request(dlg, tdata); + + } else { + /* + * This is only valid for sending response to INVITE! + */ + pjsip_cseq_hdr *cseq_hdr; + + if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) { + status = -1; + goto on_return; + } + + cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, + PJSIP_H_CSEQ, NULL); + pj_assert(cseq_hdr); + + if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) { + status = -1; + goto on_return; + } + + pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq); + + pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata); + status = 0; + } + +on_return: + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + /* Whatever happen delete the message. */ + pjsip_tx_data_dec_ref( tdata ); + + return status; +} + +/* + * Sends outgoing invitation. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg ) +{ + pjsip_method method; + struct dialog_lock_data lck; + const pjsip_allow_hdr *allow_hdr; + pjsip_tx_data *tdata; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "request to send invitation")); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Create request. */ + pjsip_method_set( &method, PJSIP_INVITE_METHOD); + tdata = pjsip_dlg_create_request( dlg, &method, -1 ); + if (tdata == NULL) { + unlock_dialog(dlg, &lck); + return NULL; + } + + /* Invite SHOULD contain "Allow" header. */ + allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); + if (allow_hdr) { + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); + } + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return tdata; +} + +/* + * Cancel pending outgoing dialog invitation. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg ) +{ + pjsip_tx_data *tdata = NULL; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "request to cancel invitation")); + + lock_dialog(dlg, &lck); + + /* Check the INVITE transaction. */ + if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " + "no INVITE transaction found")); + goto on_return; + } + + /* Construct the CANCEL request. */ + tdata = pjsip_endpt_create_cancel( dlg->ua->endpt, + dlg->invite_tsx->last_tx ); + if (tdata == NULL) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " + "unable to construct request")); + goto on_return; + } + + /* Add reference counter to tdata. */ + pjsip_tx_data_add_ref(tdata); + +on_return: + unlock_dialog(dlg, &lck); + return tdata; +} + + +/* + * Answer incoming dialog invitation, with either provisional responses + * or a final response. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code ) +{ + pjsip_tx_data *tdata = NULL; + pjsip_msg *msg; + struct dialog_lock_data lck; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code)); + + /* Lock dialog. */ + lock_dialog(dlg, &lck); + + /* Must have pending INVITE. */ + if (dlg->invite_tsx == NULL) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found")); + goto on_return; + } + /* Must be UAS. */ + if (dlg->role != PJSIP_ROLE_UAS) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS")); + goto on_return; + } + /* Must have not answered with final response before. */ + if (dlg->invite_tsx->status_code >= 200) { + PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated " + "with status %d", dlg->invite_tsx->status_code)); + goto on_return; + } + + /* Get transmit data and the message. + * We will rewrite the message with a new status code. + */ + tdata = dlg->invite_tsx->last_tx; + msg = tdata->msg; + + /* Set status code and reason phrase. */ + if (code < 100 || code >= 700) code = 500; + msg->line.status.code = code; + msg->line.status.reason = *pjsip_get_status_text(code); + + /* For 2xx response, Contact and Record-Route must be added. */ + if (PJSIP_IS_STATUS_IN_CLASS(code,200)) { + const pjsip_allow_hdr *allow_hdr; + + if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) { + pjsip_contact_hdr *contact; + contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact); + pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact ); + } + + /* 2xx response MUST contain "Allow" header. */ + allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); + if (allow_hdr) { + pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); + } + } + + /* for all but 100 responses, To-tag must be set. */ + if (code != 100) { + pjsip_to_hdr *to; + to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL); + to->tag = dlg->local.tag; + } + + /* Reset packet buffer. */ + pjsip_tx_data_invalidate_msg(tdata); + + /* Add reference counter */ + pjsip_tx_data_add_ref(tdata); + +on_return: + + /* Unlock dialog. */ + unlock_dialog(dlg, &lck); + + return tdata; +} + + +/* + * Send BYE request to terminate the dialog's session. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg ) +{ + pjsip_method method; + struct dialog_lock_data lck; + pjsip_tx_data *tdata; + + if (!dlg) { + pj_assert(dlg != NULL); + return NULL; + } + + PJ_LOG(4, (dlg->obj_name, "request to terminate session")); + + lock_dialog(dlg, &lck); + + pjsip_method_set( &method, PJSIP_BYE_METHOD); + tdata = pjsip_dlg_create_request( dlg, &method, -1 ); + + unlock_dialog(dlg, &lck); + + return tdata; +} + +/* + * High level function to disconnect dialog's session. Depending on dialog's + * state, this function will either send CANCEL, final response, or BYE to + * trigger the disconnection. A status code must be supplied, which will be + * sent if dialog will be transmitting a final response to INVITE. + */ +PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, + int status_code ) +{ + pjsip_tx_data *tdata = NULL; + + pj_assert(dlg != NULL); + if (!dlg) { + return NULL; + } + + switch (dlg->state) { + case PJSIP_DIALOG_STATE_INCOMING: + tdata = pjsip_dlg_answer(dlg, status_code); + break; + + case PJSIP_DIALOG_STATE_CALLING: + tdata = pjsip_dlg_cancel(dlg); + break; + + case PJSIP_DIALOG_STATE_PROCEEDING: + if (dlg->role == PJSIP_ROLE_UAC) { + tdata = pjsip_dlg_cancel(dlg); + } else { + tdata = pjsip_dlg_answer(dlg, status_code); + } + break; + + case PJSIP_DIALOG_STATE_ESTABLISHED: + tdata = pjsip_dlg_bye(dlg); + break; + + default: + PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()", + dlg_state_names[dlg->state])); + break; + } + + return tdata; +} + +/* + * Handling of the receipt of 2xx/INVITE response. + */ +static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg, + pjsip_event *event ) +{ + pjsip_msg *msg; + pjsip_contact_hdr *contact; + pjsip_hdr *hdr, *end_hdr; + pjsip_method method; + pjsip_tx_data *ack_tdata; + + /* Get the message */ + msg = event->src.rdata->msg; + + /* Update remote's tag information. */ + pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag); + + /* Copy Contact information in the 2xx/INVITE response to dialog's. + * remote contact + */ + contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); + if (contact) { + dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); + } else { + /* duplicate contact from "From" header (?) */ + PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!")); + dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); + dlg->remote.contact->uri = dlg->remote.info->uri; + } + + /* Copy Record-Route header (in reverse order) as dialog's route-set, + * overwriting previous route-set, if any, even if the received route-set + * is empty. + */ + pj_list_init(&dlg->route_set); + end_hdr = &msg->hdr; + for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) { + if (hdr->type == PJSIP_H_RECORD_ROUTE) { + pjsip_route_hdr *r; + r = pjsip_hdr_clone(dlg->pool, hdr); + pjsip_routing_hdr_set_route(r); + pj_list_insert_before(&dlg->route_set, r); + } + } + + /* On receipt of 200/INVITE response, send ACK. + * This ack must be saved and retransmitted whenever we receive + * 200/INVITE retransmission, until 64*T1 seconds elapsed. + */ + pjsip_method_set( &method, PJSIP_ACK_METHOD); + ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq); + if (ack_tdata == NULL) { + //PJ_TODO(HANDLE_CREATE_ACK_FAILURE) + PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request")); + return; + } + + /* Send with the transaction. */ + pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata); + + /* Decrement reference counter because pjsip_dlg_create_request + * automatically increments the request. + */ + pjsip_tx_data_dec_ref( ack_tdata ); +} + +/* + * State NULL, before any events have been received. + */ +static int dlg_on_state_null( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG) + { + pjsip_hdr *hdr, *hdr_list; + + pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); + + /* Save the INVITE transaction. */ + dlg->invite_tsx = tsx; + + /* Change state to INCOMING */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event); + + /* Create response buffer. */ + tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100); + pjsip_tx_data_add_ref(tsx->last_tx); + + /* Copy the Record-Route headers into dialog's route_set, maintaining + * the order. + */ + pj_list_init(&dlg->route_set); + hdr_list = &event->src.rdata->msg->hdr; + hdr = hdr_list->next; + while (hdr != hdr_list) { + if (hdr->type == PJSIP_H_RECORD_ROUTE) { + pjsip_route_hdr *route; + route = pjsip_hdr_clone(dlg->pool, hdr); + pjsip_routing_hdr_set_route(route); + pj_list_insert_before(&dlg->route_set, route); + } + hdr = hdr->next; + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_TX_MSG) + { + pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); + + /* Save the INVITE transaction. */ + dlg->invite_tsx = tsx; + + /* Change state to CALLING. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return 0; +} + +/* + * State INCOMING is after the (callee) dialog has been initialized with + * the incoming request, but before any responses is sent by the dialog. + */ +static int dlg_on_state_incoming( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + return dlg_on_state_proceeding_callee( dlg, tsx, event ); +} + +/* + * State CALLING is after the (caller) dialog has sent outgoing invitation + * but before any responses are received. + */ +static int dlg_on_state_calling( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (tsx == dlg->invite_tsx) { + return dlg_on_state_proceeding_caller( dlg, tsx, event ); + } + return 0; +} + +/* + * State PROCEEDING is after provisional response is received. + * Since the processing is similar to state CALLING, this function is also + * called for CALLING state. + */ +static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + int dlg_is_terminated = 0; + + /* We only care about our INVITE transaction. + * Ignore other transaction progression (such as CANCEL). + */ + if (tsx != dlg->invite_tsx) { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + return 0; + } + + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) { + switch (tsx->state) { + case PJSIP_TSX_STATE_PROCEEDING: + if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) { + /* Change state to PROCEEDING */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + } else { + /* Also notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + break; + + case PJSIP_TSX_STATE_COMPLETED: + /* Change dialog state. */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + /* Update remote target, take it from the contact hdr. */ + pjsip_contact_hdr *contact; + contact = pjsip_msg_find_hdr(event->src.rdata->msg, + PJSIP_H_CONTACT, NULL); + if (contact) { + dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri); + } else { + PJ_LOG(4,(dlg->obj_name, + "Warning: found no Contact hdr in 200/OK")); + } + dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); + } else if (tsx->status_code==401 || tsx->status_code==407) { + /* Handle Authentication challenge. */ + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( dlg->ua->endpt, + dlg->pool, &dlg->auth_sess, + dlg->cred_count, dlg->cred_info, + tsx->last_tx, event->src.rdata); + if (tdata) { + /* Re-use original request, with a new transaction. + * Need not to worry about CSeq, dialog will take care. + */ + pjsip_dlg_send_msg(dlg, tdata); + return 0; + } else { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + } else { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* Send ACK when dialog is connected. */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + pj_assert(event->src_type == PJSIP_EVENT_RX_MSG); + dlg_on_recv_2xx_invite(dlg, event); + } + break; + + case PJSIP_TSX_STATE_TERMINATED: + /* + * Transaction is terminated because of timeout or transport error. + * To let the application go to normal state progression, call the + * callback twice. First is to emulate disconnection, and then call + * again (with state TERMINATED) to destroy the dialog. + */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* The INVITE transaction will be destroyed, so release reference + * to it. + */ + dlg->invite_tsx = NULL; + + /* We should terminate the dialog now. + * But it's possible that we have other pending transactions (for + * example, outgoing CANCEL is in progress). + * So destroy the dialog only if there's no other transaction. + */ + if (dlg->pending_tsx_count == 0) { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + dlg_is_terminated = 1; + } + break; + + default: + pj_assert(0); + break; + } + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + return dlg_is_terminated ? -1 : 0; +} + +/* + * State PROCEEDING for UAS is after the callee send provisional response. + * This function is also called for INCOMING state. + */ +static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + int dlg_is_terminated = 0; + + pj_assert( dlg->invite_tsx != NULL ); + + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_TX_MSG && + tsx == dlg->invite_tsx) + { + switch (tsx->state) { + case PJSIP_TSX_STATE_PROCEEDING: + /* Change state to PROCEEDING */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + break; + + case PJSIP_TSX_STATE_COMPLETED: + case PJSIP_TSX_STATE_TERMINATED: + /* Change dialog state. */ + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); + } else { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* If transaction is terminated in non-2xx situation, + * terminate dialog as well. This happens when something unexpected + * occurs, such as transport error. + */ + if (tsx->state == PJSIP_TSX_STATE_TERMINATED && + !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) + { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + dlg_is_terminated = 1; + } + break; + + default: + pj_assert(0); + break; + } + + } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->method.id == PJSIP_CANCEL_METHOD) + { + pjsip_tx_data *tdata; + + /* Check if sequence number matches the pending INVITE. */ + if (dlg->invite_tsx==NULL || + pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0) + { + PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE")); + + /* No matching INVITE transaction found. */ + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST ); + pjsip_tsx_on_tx_msg(tsx, tdata); + return 0; + } + + /* Always respond the CANCEL with 200/CANCEL no matter what. */ + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + 200 ); + pjsip_tsx_on_tx_msg( tsx, tdata ); + + /* Respond the INVITE transaction with 487, only if transaction has + * not completed. + */ + if (dlg->invite_tsx->last_tx) { + if (dlg->invite_tsx->status_code < 200) { + tdata = dlg->invite_tsx->last_tx; + tdata->msg->line.status.code = 487; + tdata->msg->line.status.reason = *pjsip_get_status_text(487); + /* Reset packet buffer. */ + pjsip_tx_data_invalidate_msg(tdata); + pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); + } else { + PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, " + "Transaction already terminated " + "with status %d", + dlg->invite_tsx->status_code)); + } + } else { + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + 487); + pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); + } + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return dlg_is_terminated ? -1 : 0; +} + +static int dlg_on_state_proceeding( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (dlg->role == PJSIP_ROLE_UAC) { + return dlg_on_state_proceeding_caller( dlg, tsx, event ); + } else { + return dlg_on_state_proceeding_callee( dlg, tsx, event ); + } +} + +static int dlg_on_state_connecting( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + if (tsx == dlg->invite_tsx) { + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + (tsx->state == PJSIP_TSX_STATE_TERMINATED || + tsx->state == PJSIP_TSX_STATE_COMPLETED || + tsx->state == PJSIP_TSX_STATE_CONFIRMED)) + { + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { + dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event); + } else { + /* Probably because we never get the ACK, or transport error + * when sending ACK. + */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + } + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + } else { + /* Handle case when transaction is started when dialog is connecting + * (e.g. BYE requests cross wire. + */ + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->role == PJSIP_ROLE_UAS) + { + pjsip_tx_data *response; + + if (tsx->status_code >= 200) + return 0; + + if (tsx->method.id == PJSIP_BYE_METHOD) { + /* Set state to DISCONNECTED. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + response = pjsip_endpt_create_response( dlg->ua->endpt, + event->src.rdata, 200); + } else { + response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, + PJSIP_SC_INTERNAL_SERVER_ERROR); + } + + if (response) + pjsip_tsx_on_tx_msg(tsx, response); + + return 0; + } + } + return 0; +} + +static int dlg_on_state_established( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(tsx) + + if (tsx && tsx->method.id == PJSIP_BYE_METHOD) { + /* Set state to DISCONNECTED. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + /* Answer with 200/BYE. */ + if (event->src_type == PJSIP_EVENT_RX_MSG) { + pjsip_tx_data *tdata; + tdata = pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, + 200 ); + if (tdata) + pjsip_tsx_on_tx_msg( tsx, tdata ); + } + } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) { + pjsip_method_e method = event->src.rdata->cseq->method.id; + + PJ_TODO(PROPERLY_HANDLE_REINVITATION) + + /* Reinvitation. The message may be INVITE or an ACK. */ + if (method == PJSIP_INVITE_METHOD) { + if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) { + /* Section 14.2: A UAS that receives a second INVITE before it + * sends the final response to a first INVITE with a lower + * CSeq sequence number on the same dialog MUST return a 500 + * (Server Internal Error) response to the second INVITE and + * MUST include a Retry-After header field with a randomly + * chosen value of between 0 and 10 seconds. + */ + pjsip_retry_after_hdr *hdr; + pjsip_tx_data *tdata = + pjsip_endpt_create_response(dlg->ua->endpt, + event->src.rdata, 500); + + if (!tdata) + return 0; + + /* Add Retry-After. */ + hdr = pjsip_retry_after_hdr_create(tdata->pool); + hdr->ivalue = 9; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); + + /* Send. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + return 0; + } + + /* Keep this as our current INVITE transaction. */ + dlg->invite_tsx = tsx; + + /* Create response buffer. */ + tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, + event->src.rdata, 100); + pjsip_tx_data_add_ref(tsx->last_tx); + + } + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event); + + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return 0; +} + +static int dlg_on_state_disconnected( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(tsx) + + /* Handle case when transaction is started when dialog is disconnected + * (e.g. BYE requests cross wire. + */ + if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->role == PJSIP_ROLE_UAS) + { + pjsip_tx_data *response = NULL; + + if (tsx->status_code >= 200) + return 0; + + if (tsx->method.id == PJSIP_BYE_METHOD) { + response = pjsip_endpt_create_response( dlg->ua->endpt, + event->src.rdata, 200); + } else { + response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, + PJSIP_SC_INTERNAL_SERVER_ERROR); + } + if (response) + pjsip_tsx_on_tx_msg(tsx, response); + + return 0; + } + /* Handle case when outgoing BYE was rejected with 401/407 */ + else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && + event->src_type == PJSIP_EVENT_RX_MSG && + tsx->role == PJSIP_ROLE_UAC) + { + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool, + &dlg->auth_sess, + dlg->cred_count, dlg->cred_info, + tsx->last_tx, event->src.rdata); + if (tdata) { + pjsip_dlg_send_msg(dlg, tdata); + } + } + } + + + if (dlg->pending_tsx_count == 0) { + /* Set state to TERMINATED. */ + dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); + + /* Notify application. */ + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); + + return -1; + } else { + dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); + } + + return 0; +} + +static int dlg_on_state_terminated( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event) +{ + PJ_UNUSED_ARG(dlg) + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(event) + + return -1; +} + diff --git a/pjsip/src/pjsip_mod_ua/sip_dialog.h b/pjsip/src/pjsip_mod_ua/sip_dialog.h index 7d4101edb..d1d942d0b 100644 --- a/pjsip/src/pjsip_mod_ua/sip_dialog.h +++ b/pjsip/src/pjsip_mod_ua/sip_dialog.h @@ -1,618 +1,618 @@ -/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_dialog.h 13 8/31/05 9:05p Bennylp $ */ -#ifndef __PJSIP_DIALOG_H__ -#define __PJSIP_DIALOG_H__ - -/** - * @file dialog.h - * @brief SIP Dialog abstraction - */ - -#include -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSUA_DIALOG SIP Dialog - * @ingroup PJSUA - * @{ - * \brief - * This file contains SIP dialog, a higher level abstraction of SIP session. - * - * \par Overview - * A SIP dialog is an abstraction of communication session between two user - * agents that persist for some time. The dialog facilitates sequencing of - * messages between the user agents and proper routing of requests between both - * of them. The dialog represents a context in which to interpret SIP messages. - * However method independent User Agent processing for requests and responses - * outside of a dialog exists, hence a dialog is not necessary for message - * processing. - * - * A dialog is identified at each User Agent with a dialog Id, which consists - * of a Call-Id value, a local tag and a remote tag. - * - * A dialog contains certain pieces of data needed for further message - * transmissions within the dialog. This data consists of: - * - Dialog Id - used to identify the dialog. - * - Local sequence number - used to order requests from the UA to its peer. - * - Remote sequence number - used to order requests from its peer to the UA. - * - Local URI - the address of the local party. - * - Remote URI - the address of the remote party. - * - Remote target - the address from the Contact header field of the request - * or response or refresh request or response. - * - "secure" boolean - determines if the dialog is secure. - * - Route set - an ordered list of URIs. The route set is the list of servers - * that need to be traversed to send a request to the peer. - * - Authentication info - array of authentication credentials to be used - * by the dialog to authenticate to proxies and servers. - * - * \par Manipulating Dialog - * Application should use functions declared in this file to do something with - * the dialog. Among other things, application can: - * - create outgoing dialog (#pjsip_dlg_init()). - * - sends outgoing invitation (#pjsip_dlg_invite()). - * - sends response (provisional and final) to incoming invitation - * (#pjsip_dlg_answer()) - * - disconnect dialog (#pjsip_dlg_disconnect()). - * - send other request (#pjsip_dlg_create_request() and #pjsip_dlg_send_msg()) - * - * \par Getting Dialog's Notification - * Dialog emits notification about various things that's happening to it (e.g. - * a message is received, dialog state has changed, etc.). Normally it is in - * the interest of the application to capture these notifications, by - * supplying the function to be called when the event occurs in #pjsip_dlg_callback - * structure, and register this structure to user agent by calling - * #pjsip_ua_set_dialog_callback(). - * - * \par Incoming Invitation - * Upon receiving a new incoming invitation, user agent will automatically create - * a new dialog, and inform application via \b pjsip_dlg_callback. - */ - -/** Forward declaration for user agent structure. */ -typedef struct pjsip_user_agent pjsip_user_agent; - -/** Forward declaration for dialog structure. */ -typedef struct pjsip_dlg pjsip_dlg; - -/** - * \brief Type of events that are reported by the dialog to the application callback - * function. - */ -typedef enum pjsip_dlg_event_e -{ - /** Dialog state has changed. */ - PJSIP_DIALOG_EVENT_STATE_CHANGED, - - /** Any mid-call messages (reinvitation, message, etc.). */ - PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, - - /** Other events (low level events). */ - PJSIP_DIALOG_EVENT_OTHER, - -} pjsip_dlg_event_e; - - -/** - * \brief Structure registered by applications to receive dialog notifications - * from the User Agent. - * - * Applications registers this structure to get notifications from the User Agent - * about dialog state changes and other events. Application can set any of - * the callback function to NULL if it doesn't want to handle the notification, - * however, setting some callbacks to NULL probably will cause some undesired - * result (such as setting \b on_incoming to NULL will cause the creation of - * a lot of dialogs with no owner). - */ -struct pjsip_dlg_callback -{ - /** - * This is a low level, uninterpreted callback that is called by framework - * for all kinds of events, such as transaction events, dialog events, etc. - * @param dlg The dialog. - * @param dlg_event The type of dialog event. - * @param event The event descriptor. - */ - void (*on_all_events)(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, - pjsip_event *event ); - - /** - * This is a low level callback that is called by the framework when the - * underlying transaction is about to send outgoing message. This callback - * is provided to allow application to modify the message before it is - * transmitted. - * @param dlg The dialog. - * @param tsx The transaction that transmits the message. - * @param tdata The transmission data, which contains the message. - * @param retransmission The number of times this message has been sent. - * Zero indicates the message is about to be sent the first time, - * one indicates this is the first retransmission, etc. - */ - void (*on_before_tx)(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata, pj_bool_t retransmission); - - /** - * This is a low level callback that is called by the framework when the dialog - * has sent a message. Note that a receive of retransmission will not trigger - * this callback since retransmission is handled internally by transaction. - * @param dlg The dialog. - * @param tsx The transaction that transmits the message. - * @param tdata The transmission data, which contains the message. - */ - void (*on_tx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata); - - /** - * This is a low level callback that is called by the framework when the - * dialog has received a message. Note that a receipt of retransmission - * will not trigger this callback since retransmission is handled internally - * by transaction. - * @param dlg The dialog. - * @param tsx The transaction that receives the message. - * @param rdata The receive data, which contains the message. - */ - void (*on_rx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata); - - /** - * This callback is called by the framework when the user agent - * instance receives an incoming INVITE message. - * @param dlg The new dialog that's just created to handle the incoming call. - * @param tsx The INVITE transaction that's just created. - * @param rdata The receive data, which contains the INVITE message. - */ - void (*on_incoming)(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata); - - /** - * This callback is called by the framework when the dialog is sending - * the first outgoing INVITE message. - * @param dlg The dialog. - * @param tsx The INVITE transaction. - * @param tdata The transmit data, which contains the INVITE message. - */ - void (*on_calling)(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata); - - /** - * This callback is called by the framework when the initial INVITE - * transaction has sent/received provisional response. - * @param dlg The dialog. - * @param tsx The transaction. - * @param event The event, which src_type will always be either - * PJSIP_EVENT_RX_MSG or PJSIP_EVENT_TX_MSG. The provisional - * response message itself will be in either \b rdata or \b tdata. - * @see pjsip_event. - */ - void (*on_provisional)(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_event *event); - - /** - * This callback is called for both UAS and UAC dialog when 200 response - * to INVITE is sent or received. - * @param dlg The dialog. - * @param event The event, which src_type can only be either - * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG. - * @see pjsip_event - */ - void (*on_connecting)(pjsip_dlg *dlg, pjsip_event *event); - - /** - * This callback is called for both UAS and UAC when an ACK request is - * sent or received by the dialog. - * @param dlg The dialog. - * @param event The event, which src_type can only be either - * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG. - * @see pjsip_event - */ - void (*on_established)(pjsip_dlg *dlg, pjsip_event *event); - - /** - * This callback is called when the dialog is disconnected, i.e. upon - * sending/receiving non-200 response to INVITE, sending/receiving - * CANCEL to initial INVITE, and sending/receiving BYE. - * - * @param dlg The dialog. - * @param event The event. - * @see pjsip_event - */ - void (*on_disconnected)(pjsip_dlg *dlg, pjsip_event *event); - - /** - * This callback is called when the dialog is about to be destroyed. - * @param dlg The dialog. - */ - void (*on_terminated)(pjsip_dlg *dlg); - - /** - * This callback will be called when the dialog receives mid call events - * such as re-invitation or incoming pager. - * - * @param dlg The dialog. - * @param event The event. - */ - void (*on_mid_call_events)(pjsip_dlg *dlg, pjsip_event *event); - -}; /* struct pjsip_dlg_callback */ - - - -/** - * Dialog state. - */ -typedef enum pjsip_dlg_state_e -{ - /** - * State NULL is after the dialog is instantiated but before any - * initialization is done. - */ - PJSIP_DIALOG_STATE_NULL, - - /** - * State INCOMING is after the (callee) dialog has been initialized with - * the incoming request, but before any responses is sent by the dialog. - */ - PJSIP_DIALOG_STATE_INCOMING, - - /** - * State CALLING is after the (caller) dialog has sent outgoing invitation - * but before any responses are received. - */ - PJSIP_DIALOG_STATE_CALLING, - - /** - * State PROCEEDING is after the dialog sent/received provisional - * responses, but before final response is sent/received. - */ - PJSIP_DIALOG_STATE_PROCEEDING, - - /** - * State CONNECTING is after the dialog has sent/received final response - * to the invitation, but before acknowledgement is sent. - */ - PJSIP_DIALOG_STATE_CONNECTING, - - /** - * State ESTABLISHED occurs after the invitation has been accepted and - * acknowledged. - */ - PJSIP_DIALOG_STATE_ESTABLISHED, - - /** - * State DISCONNECTED occurs after either party successfully disconnect - * the session. - */ - PJSIP_DIALOG_STATE_DISCONNECTED, - - /** - * State TERMINATE occurs when the dialog is ready to be destroyed. - */ - PJSIP_DIALOG_STATE_TERMINATED, - -} pjsip_dlg_state_e; - - -/** - * Get the dialog string state. - * - * @param state Dialog state. - * @return The string describing the state. - */ -const char *pjsip_dlg_state_str(pjsip_dlg_state_e state); - -/** - * This structure is used to describe dialog's participants, which in this - * case is local party (i.e. us) and remote party. - */ -typedef struct pjsip_dlg_party -{ - pjsip_uri *target; /**< Target URL. */ - pjsip_fromto_hdr *info; /**< URL in From/To header. */ - pj_str_t tag; /**< Tag. */ - pjsip_contact_hdr *contact; /**< URL in Contact. */ - pj_sockaddr_in addr; /**< The current transport address. */ - int cseq; /**< Sequence number counter. */ -} pjsip_dlg_party; - - -/** - * This structure describes the dialog structure. - */ -struct pjsip_dlg -{ - PJ_DECL_LIST_MEMBER(struct pjsip_dlg) - - char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification. */ - - pjsip_user_agent *ua; /**< User agent instance. */ - pj_pool_t *pool; /**< Dialog's pool. */ - pjsip_dlg_state_e state; /**< Dialog's call state. */ - pjsip_role_e role; /**< Dialog's role. */ - pj_mutex_t *mutex; /**< Dialog's mutex. */ - - pjsip_dlg_party local; /**< Local party info. */ - pjsip_dlg_party remote; /**< Remote party info. */ - - pjsip_cid_hdr *call_id; /**< Call-ID */ - pj_bool_t secure; /**< Use secure transport? */ - - pjsip_route_hdr route_set; /**< Dialog's route set. */ - pjsip_transaction *invite_tsx; /**< Current INVITE transaction. */ - int pending_tsx_count; /**< Total pending tsx count. */ - - int cred_count; /**< Number of credentials. */ - pjsip_cred_info *cred_info; /**< Array of credentials. */ - - pjsip_auth_session auth_sess; /**< List of auth session. */ - - pjsip_msg_body *body; - - void *user_data; /**< Application's data. */ - - int (*handle_tsx_event)(struct pjsip_dlg *, /**< Internal state handler.*/ - pjsip_transaction *, - pjsip_event *); -}; - - -/** - * Initialize dialog with local and remote info. This function is normally - * called after application creates the dialog with #pjsip_ua_create_dialog - * for UAC dialogs. - * - * This function will initialize local and remote info from the URL, generate - * a globally unique Call-ID, initialize CSeq, and initialize other dialog's - * internal attributes. - * - * @param dlg The dialog to initialize. - * @param local_info URI/name address to be used as local info - * (From and Contact headers). - * @param remote_info URI/name address to be used as remote info (To header). - * @param target URI for initial remote's target, or NULL to set the - * initial target the same as remote_info. - * - * @return zero on success. - */ -PJ_DECL(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg, - const pj_str_t *local_info, - const pj_str_t *remote_info, - const pj_str_t *target); - - -/** - * Set authentication credentials to be used by this dialog. - * - * If authentication credentials are set for the dialog, the dialog will try to - * perform authentication automatically using the credentials supplied, and - * also cache the last Authorization or Proxy-Authorization headers for next - * requests. - * - * If none of the credentials are suitable or accepted by remote, then - * the dialog will just pass the authorization failure response back to - * application. - * - * @param dlg The dialog. - * @param count Number of credentials in the array. - * @param cred Array of credentials. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg, - int count, - const pjsip_cred_info cred[]); - -/** - * Override local contact details. - * - * Call this function to change the contact details to be advertised in Contact - * header. Application normally need to call this function for incoming calls - * before answering the call with 200/OK, because for an incoming dialogs, the - * initial local contact info are generated from the To header, which is - * normally not the appropriate one. - * - * @param dlg The dialog. - * @param contact The contact to use. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg, - const pj_str_t *contact ); - - -/** - * Set initial route set to be used by the dialog. This initial route set - * governs where and how the initial INVITE request will be routed. This - * initial route set will be overwritten with the route set found in the - * 2xx response of INVITE. - * - * Application only needs to call this function if it wants to have custom - * route for individual dialogs. If only a single route for all dialogs is - * needed, then application can set the global route by calling function - * #pjsip_endpt_set_proxies(). - * - * @param dlg The dialog. - * @param route_set The route set list. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg, - const pjsip_route_hdr *route_set ); - - -/** - * Variation of #pjsip_dlg_set_route_set where the headers will be used - * as it is (i.e. without cloned). - * - * @param dlg The dialog. - * @param route_set The route set list. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg, - pjsip_route_hdr *route_set); - -/** - * Create initial outgoing INVITE message. - * - * This function is just a simple wrapper to #pjsip_dlg_create_request(), - * so it follows the same rule there. In addition, this function also adds - * \b Allow header to the outgoing request. - * - * After the message is successfully created, application must call - * #pjsip_dlg_send_msg() to actually send the message and update the dialog's - * state. Note that upon return the reference counter of the transmit data - * will be set to one. - * - * @param dlg The dialog. - * - * @return The dialog transmit data, or NULL. - */ -PJ_DECL(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg ); - - -/** - * Answer incoming dialog invitation, with either provisional responses - * or a final response. Application can only call this function when there's - * a pending invitation to be answered. - * - * After the message is successfully created, application must call - * #pjsip_dlg_send_msg() to actually send the message and update the dialog's - * state. Note that upon return the reference counter of the transmit data - * will be set to one. - * - * @param dlg The dialog. - * @param code The response code, which can be: - * - 100-199 Provisional response (application can issue multiple - * provisional responses). - * - 200-299 To answer the invitation (normally status code 200 - * is sent). - * - 300-699 To reject the invitation. - * @return Transmit data if successfull. - */ -PJ_DECL(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code ); - - -/** - * High level function to create message to disconnect dialog. Depending - * on dialog's state, this function will either create CANCEL, final response, - * or BYE message. A status code must be supplied, which will be set if dialog - * will be transmitting a final response to INVITE. - * - * After the message is successfully created, application must call - * #pjsip_dlg_send_msg to actually send the message and update the dialog's - * state. Note that upon return the reference counter of the transmit data - * will be set to one. - * - * @param dlg The dialog. - * @param status_code The status code for disconnection. - * @return Transmit data if successfull. - */ -PJ_DECL(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, int status_code); - -/** - * Create CANCEL message to cancel pending outgoing dialog invitation. - * Normally application should call #pjsip_dlg_disconnect() instead, because - * that function will create the correct message regardless of the state of - * the dialog. - * - * Application can call this function at anytime after it issues outgoing - * invitation and before receiving final response. However, there's no - * guarantee that the invitation will be successfully cancelled, since the - * CANCEL request and the final response can pass over in the wire. So the - * application must prepare to have the dialog connected even after the - * dialog is cancelled. - * - * The final state of the dialog will be reported in the dialog callback. - * If the CANCEL request succeeded, then the dialog will be disconnected with - * status code \a PJSIP_SC_REQUEST_TERMINATED. - * - * After the message is successfully created, application must call - * #pjsip_dlg_send_msg() to actually send the message and update the dialog's - * state. - * - * Upon return of this function, the reference counter of the transmit data - * will be set to one. - * - * @param dlg The dialog. - * @return The dialog transmit data containing the CANCEL message, - * or NULL. - */ -PJ_DECL(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg ); - - -/** - * Create BYE message. Application shouldn't normally need to use this function, - * but rather it's preferable to use #pjsip_dlg_disconnect() instead because - * that function will work to disconnect the session no matter what the state - * is. - * - * After the message is successfully created, application must call - * #pjsip_dlg_send_msg() to actually send the message and update the dialog's - * state. Note that upon return the reference counter of the transmit data - * will be set to one. - * - * @param dlg The dialog. - * @return The BYE message or NULL. - */ -PJ_DECL(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg ); - -/** - * This function is called by application to create new outgoing request - * message for this dialog. After the request is created, application can - * modify the message (such adding headers), and eventually send the request - * by calling #pjsip_dlg_send_msg(). - * - * This function will initialize the request message with dialog's properties - * as follows: - * - the request line is initialized with the method and the target is - * initialized from current remote target. - * - \b From, \b To, \b Contact, and \b Call-Id headers will be added. - * - An initial \b CSeq header will be provided (although the value will be - * verified again when the message is actually sent with #pjsip_dlg_send_msg(). - * - \b Route headers will be added from dialog's route set. - * - Authentication headers (\b Authorization or \b Proxy-Authorization) will - * be added from dialog's authorization cache. - * - * Note that upon return the reference counter of the transmit data - * will be set to one. When the message is sent, #pjsip_dlg_send_msg() will - * decrement the reference counter, and when the reference counter reach zero, - * the message will be deleted. - * - * @param dlg The dialog. - * @param method The request method. - * @param cseq Specify CSeq, or -1 to let the dialog specify CSeq. - * - * @return Transmit data for the new request. - * - * @see pjsip_dlg_send_msg() - */ -PJ_DECL(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg, - const pjsip_method *method, - int cseq); - - -/** - * This function can be called by application to send outgoing message (request - * or response) to remote party. Note that after calling this function, the - * transmit data will be deleted regardless of the return status. To prevent - * deletion, application must increase the reference count, but then it will - * be responsible to delete this transmit data itself (by decreasing the - * reference count). - * - * @param dlg The dialog. - * @param tdata The transmit data, which contains the request message. - * @return zero on success. - */ -PJ_DECL(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, - pjsip_tx_data *tdata ); - - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_DIALOG_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_dialog.h 13 8/31/05 9:05p Bennylp $ */ +#ifndef __PJSIP_DIALOG_H__ +#define __PJSIP_DIALOG_H__ + +/** + * @file dialog.h + * @brief SIP Dialog abstraction + */ + +#include +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSUA_DIALOG SIP Dialog + * @ingroup PJSUA + * @{ + * \brief + * This file contains SIP dialog, a higher level abstraction of SIP session. + * + * \par Overview + * A SIP dialog is an abstraction of communication session between two user + * agents that persist for some time. The dialog facilitates sequencing of + * messages between the user agents and proper routing of requests between both + * of them. The dialog represents a context in which to interpret SIP messages. + * However method independent User Agent processing for requests and responses + * outside of a dialog exists, hence a dialog is not necessary for message + * processing. + * + * A dialog is identified at each User Agent with a dialog Id, which consists + * of a Call-Id value, a local tag and a remote tag. + * + * A dialog contains certain pieces of data needed for further message + * transmissions within the dialog. This data consists of: + * - Dialog Id - used to identify the dialog. + * - Local sequence number - used to order requests from the UA to its peer. + * - Remote sequence number - used to order requests from its peer to the UA. + * - Local URI - the address of the local party. + * - Remote URI - the address of the remote party. + * - Remote target - the address from the Contact header field of the request + * or response or refresh request or response. + * - "secure" boolean - determines if the dialog is secure. + * - Route set - an ordered list of URIs. The route set is the list of servers + * that need to be traversed to send a request to the peer. + * - Authentication info - array of authentication credentials to be used + * by the dialog to authenticate to proxies and servers. + * + * \par Manipulating Dialog + * Application should use functions declared in this file to do something with + * the dialog. Among other things, application can: + * - create outgoing dialog (#pjsip_dlg_init()). + * - sends outgoing invitation (#pjsip_dlg_invite()). + * - sends response (provisional and final) to incoming invitation + * (#pjsip_dlg_answer()) + * - disconnect dialog (#pjsip_dlg_disconnect()). + * - send other request (#pjsip_dlg_create_request() and #pjsip_dlg_send_msg()) + * + * \par Getting Dialog's Notification + * Dialog emits notification about various things that's happening to it (e.g. + * a message is received, dialog state has changed, etc.). Normally it is in + * the interest of the application to capture these notifications, by + * supplying the function to be called when the event occurs in #pjsip_dlg_callback + * structure, and register this structure to user agent by calling + * #pjsip_ua_set_dialog_callback(). + * + * \par Incoming Invitation + * Upon receiving a new incoming invitation, user agent will automatically create + * a new dialog, and inform application via \b pjsip_dlg_callback. + */ + +/** Forward declaration for user agent structure. */ +typedef struct pjsip_user_agent pjsip_user_agent; + +/** Forward declaration for dialog structure. */ +typedef struct pjsip_dlg pjsip_dlg; + +/** + * \brief Type of events that are reported by the dialog to the application callback + * function. + */ +typedef enum pjsip_dlg_event_e +{ + /** Dialog state has changed. */ + PJSIP_DIALOG_EVENT_STATE_CHANGED, + + /** Any mid-call messages (reinvitation, message, etc.). */ + PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, + + /** Other events (low level events). */ + PJSIP_DIALOG_EVENT_OTHER, + +} pjsip_dlg_event_e; + + +/** + * \brief Structure registered by applications to receive dialog notifications + * from the User Agent. + * + * Applications registers this structure to get notifications from the User Agent + * about dialog state changes and other events. Application can set any of + * the callback function to NULL if it doesn't want to handle the notification, + * however, setting some callbacks to NULL probably will cause some undesired + * result (such as setting \b on_incoming to NULL will cause the creation of + * a lot of dialogs with no owner). + */ +struct pjsip_dlg_callback +{ + /** + * This is a low level, uninterpreted callback that is called by framework + * for all kinds of events, such as transaction events, dialog events, etc. + * @param dlg The dialog. + * @param dlg_event The type of dialog event. + * @param event The event descriptor. + */ + void (*on_all_events)(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, + pjsip_event *event ); + + /** + * This is a low level callback that is called by the framework when the + * underlying transaction is about to send outgoing message. This callback + * is provided to allow application to modify the message before it is + * transmitted. + * @param dlg The dialog. + * @param tsx The transaction that transmits the message. + * @param tdata The transmission data, which contains the message. + * @param retransmission The number of times this message has been sent. + * Zero indicates the message is about to be sent the first time, + * one indicates this is the first retransmission, etc. + */ + void (*on_before_tx)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata, pj_bool_t retransmission); + + /** + * This is a low level callback that is called by the framework when the dialog + * has sent a message. Note that a receive of retransmission will not trigger + * this callback since retransmission is handled internally by transaction. + * @param dlg The dialog. + * @param tsx The transaction that transmits the message. + * @param tdata The transmission data, which contains the message. + */ + void (*on_tx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); + + /** + * This is a low level callback that is called by the framework when the + * dialog has received a message. Note that a receipt of retransmission + * will not trigger this callback since retransmission is handled internally + * by transaction. + * @param dlg The dialog. + * @param tsx The transaction that receives the message. + * @param rdata The receive data, which contains the message. + */ + void (*on_rx_msg)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); + + /** + * This callback is called by the framework when the user agent + * instance receives an incoming INVITE message. + * @param dlg The new dialog that's just created to handle the incoming call. + * @param tsx The INVITE transaction that's just created. + * @param rdata The receive data, which contains the INVITE message. + */ + void (*on_incoming)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); + + /** + * This callback is called by the framework when the dialog is sending + * the first outgoing INVITE message. + * @param dlg The dialog. + * @param tsx The INVITE transaction. + * @param tdata The transmit data, which contains the INVITE message. + */ + void (*on_calling)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); + + /** + * This callback is called by the framework when the initial INVITE + * transaction has sent/received provisional response. + * @param dlg The dialog. + * @param tsx The transaction. + * @param event The event, which src_type will always be either + * PJSIP_EVENT_RX_MSG or PJSIP_EVENT_TX_MSG. The provisional + * response message itself will be in either \b rdata or \b tdata. + * @see pjsip_event. + */ + void (*on_provisional)(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_event *event); + + /** + * This callback is called for both UAS and UAC dialog when 200 response + * to INVITE is sent or received. + * @param dlg The dialog. + * @param event The event, which src_type can only be either + * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG. + * @see pjsip_event + */ + void (*on_connecting)(pjsip_dlg *dlg, pjsip_event *event); + + /** + * This callback is called for both UAS and UAC when an ACK request is + * sent or received by the dialog. + * @param dlg The dialog. + * @param event The event, which src_type can only be either + * PJSIP_EVENT_TX_MSG or PJSIP_EVENT_RX_MSG. + * @see pjsip_event + */ + void (*on_established)(pjsip_dlg *dlg, pjsip_event *event); + + /** + * This callback is called when the dialog is disconnected, i.e. upon + * sending/receiving non-200 response to INVITE, sending/receiving + * CANCEL to initial INVITE, and sending/receiving BYE. + * + * @param dlg The dialog. + * @param event The event. + * @see pjsip_event + */ + void (*on_disconnected)(pjsip_dlg *dlg, pjsip_event *event); + + /** + * This callback is called when the dialog is about to be destroyed. + * @param dlg The dialog. + */ + void (*on_terminated)(pjsip_dlg *dlg); + + /** + * This callback will be called when the dialog receives mid call events + * such as re-invitation or incoming pager. + * + * @param dlg The dialog. + * @param event The event. + */ + void (*on_mid_call_events)(pjsip_dlg *dlg, pjsip_event *event); + +}; /* struct pjsip_dlg_callback */ + + + +/** + * Dialog state. + */ +typedef enum pjsip_dlg_state_e +{ + /** + * State NULL is after the dialog is instantiated but before any + * initialization is done. + */ + PJSIP_DIALOG_STATE_NULL, + + /** + * State INCOMING is after the (callee) dialog has been initialized with + * the incoming request, but before any responses is sent by the dialog. + */ + PJSIP_DIALOG_STATE_INCOMING, + + /** + * State CALLING is after the (caller) dialog has sent outgoing invitation + * but before any responses are received. + */ + PJSIP_DIALOG_STATE_CALLING, + + /** + * State PROCEEDING is after the dialog sent/received provisional + * responses, but before final response is sent/received. + */ + PJSIP_DIALOG_STATE_PROCEEDING, + + /** + * State CONNECTING is after the dialog has sent/received final response + * to the invitation, but before acknowledgement is sent. + */ + PJSIP_DIALOG_STATE_CONNECTING, + + /** + * State ESTABLISHED occurs after the invitation has been accepted and + * acknowledged. + */ + PJSIP_DIALOG_STATE_ESTABLISHED, + + /** + * State DISCONNECTED occurs after either party successfully disconnect + * the session. + */ + PJSIP_DIALOG_STATE_DISCONNECTED, + + /** + * State TERMINATE occurs when the dialog is ready to be destroyed. + */ + PJSIP_DIALOG_STATE_TERMINATED, + +} pjsip_dlg_state_e; + + +/** + * Get the dialog string state. + * + * @param state Dialog state. + * @return The string describing the state. + */ +const char *pjsip_dlg_state_str(pjsip_dlg_state_e state); + +/** + * This structure is used to describe dialog's participants, which in this + * case is local party (i.e. us) and remote party. + */ +typedef struct pjsip_dlg_party +{ + pjsip_uri *target; /**< Target URL. */ + pjsip_fromto_hdr *info; /**< URL in From/To header. */ + pj_str_t tag; /**< Tag. */ + pjsip_contact_hdr *contact; /**< URL in Contact. */ + pj_sockaddr_in addr; /**< The current transport address. */ + int cseq; /**< Sequence number counter. */ +} pjsip_dlg_party; + + +/** + * This structure describes the dialog structure. + */ +struct pjsip_dlg +{ + PJ_DECL_LIST_MEMBER(struct pjsip_dlg) + + char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification. */ + + pjsip_user_agent *ua; /**< User agent instance. */ + pj_pool_t *pool; /**< Dialog's pool. */ + pjsip_dlg_state_e state; /**< Dialog's call state. */ + pjsip_role_e role; /**< Dialog's role. */ + pj_mutex_t *mutex; /**< Dialog's mutex. */ + + pjsip_dlg_party local; /**< Local party info. */ + pjsip_dlg_party remote; /**< Remote party info. */ + + pjsip_cid_hdr *call_id; /**< Call-ID */ + pj_bool_t secure; /**< Use secure transport? */ + + pjsip_route_hdr route_set; /**< Dialog's route set. */ + pjsip_transaction *invite_tsx; /**< Current INVITE transaction. */ + int pending_tsx_count; /**< Total pending tsx count. */ + + int cred_count; /**< Number of credentials. */ + pjsip_cred_info *cred_info; /**< Array of credentials. */ + + pjsip_auth_session auth_sess; /**< List of auth session. */ + + pjsip_msg_body *body; + + void *user_data; /**< Application's data. */ + + int (*handle_tsx_event)(struct pjsip_dlg *, /**< Internal state handler.*/ + pjsip_transaction *, + pjsip_event *); +}; + + +/** + * Initialize dialog with local and remote info. This function is normally + * called after application creates the dialog with #pjsip_ua_create_dialog + * for UAC dialogs. + * + * This function will initialize local and remote info from the URL, generate + * a globally unique Call-ID, initialize CSeq, and initialize other dialog's + * internal attributes. + * + * @param dlg The dialog to initialize. + * @param local_info URI/name address to be used as local info + * (From and Contact headers). + * @param remote_info URI/name address to be used as remote info (To header). + * @param target URI for initial remote's target, or NULL to set the + * initial target the same as remote_info. + * + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg, + const pj_str_t *local_info, + const pj_str_t *remote_info, + const pj_str_t *target); + + +/** + * Set authentication credentials to be used by this dialog. + * + * If authentication credentials are set for the dialog, the dialog will try to + * perform authentication automatically using the credentials supplied, and + * also cache the last Authorization or Proxy-Authorization headers for next + * requests. + * + * If none of the credentials are suitable or accepted by remote, then + * the dialog will just pass the authorization failure response back to + * application. + * + * @param dlg The dialog. + * @param count Number of credentials in the array. + * @param cred Array of credentials. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg, + int count, + const pjsip_cred_info cred[]); + +/** + * Override local contact details. + * + * Call this function to change the contact details to be advertised in Contact + * header. Application normally need to call this function for incoming calls + * before answering the call with 200/OK, because for an incoming dialogs, the + * initial local contact info are generated from the To header, which is + * normally not the appropriate one. + * + * @param dlg The dialog. + * @param contact The contact to use. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg, + const pj_str_t *contact ); + + +/** + * Set initial route set to be used by the dialog. This initial route set + * governs where and how the initial INVITE request will be routed. This + * initial route set will be overwritten with the route set found in the + * 2xx response of INVITE. + * + * Application only needs to call this function if it wants to have custom + * route for individual dialogs. If only a single route for all dialogs is + * needed, then application can set the global route by calling function + * #pjsip_endpt_set_proxies(). + * + * @param dlg The dialog. + * @param route_set The route set list. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg, + const pjsip_route_hdr *route_set ); + + +/** + * Variation of #pjsip_dlg_set_route_set where the headers will be used + * as it is (i.e. without cloned). + * + * @param dlg The dialog. + * @param route_set The route set list. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg, + pjsip_route_hdr *route_set); + +/** + * Create initial outgoing INVITE message. + * + * This function is just a simple wrapper to #pjsip_dlg_create_request(), + * so it follows the same rule there. In addition, this function also adds + * \b Allow header to the outgoing request. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * + * @return The dialog transmit data, or NULL. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg ); + + +/** + * Answer incoming dialog invitation, with either provisional responses + * or a final response. Application can only call this function when there's + * a pending invitation to be answered. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @param code The response code, which can be: + * - 100-199 Provisional response (application can issue multiple + * provisional responses). + * - 200-299 To answer the invitation (normally status code 200 + * is sent). + * - 300-699 To reject the invitation. + * @return Transmit data if successfull. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code ); + + +/** + * High level function to create message to disconnect dialog. Depending + * on dialog's state, this function will either create CANCEL, final response, + * or BYE message. A status code must be supplied, which will be set if dialog + * will be transmitting a final response to INVITE. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @param status_code The status code for disconnection. + * @return Transmit data if successfull. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, int status_code); + +/** + * Create CANCEL message to cancel pending outgoing dialog invitation. + * Normally application should call #pjsip_dlg_disconnect() instead, because + * that function will create the correct message regardless of the state of + * the dialog. + * + * Application can call this function at anytime after it issues outgoing + * invitation and before receiving final response. However, there's no + * guarantee that the invitation will be successfully cancelled, since the + * CANCEL request and the final response can pass over in the wire. So the + * application must prepare to have the dialog connected even after the + * dialog is cancelled. + * + * The final state of the dialog will be reported in the dialog callback. + * If the CANCEL request succeeded, then the dialog will be disconnected with + * status code \a PJSIP_SC_REQUEST_TERMINATED. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. + * + * Upon return of this function, the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @return The dialog transmit data containing the CANCEL message, + * or NULL. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg ); + + +/** + * Create BYE message. Application shouldn't normally need to use this function, + * but rather it's preferable to use #pjsip_dlg_disconnect() instead because + * that function will work to disconnect the session no matter what the state + * is. + * + * After the message is successfully created, application must call + * #pjsip_dlg_send_msg() to actually send the message and update the dialog's + * state. Note that upon return the reference counter of the transmit data + * will be set to one. + * + * @param dlg The dialog. + * @return The BYE message or NULL. + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg ); + +/** + * This function is called by application to create new outgoing request + * message for this dialog. After the request is created, application can + * modify the message (such adding headers), and eventually send the request + * by calling #pjsip_dlg_send_msg(). + * + * This function will initialize the request message with dialog's properties + * as follows: + * - the request line is initialized with the method and the target is + * initialized from current remote target. + * - \b From, \b To, \b Contact, and \b Call-Id headers will be added. + * - An initial \b CSeq header will be provided (although the value will be + * verified again when the message is actually sent with #pjsip_dlg_send_msg(). + * - \b Route headers will be added from dialog's route set. + * - Authentication headers (\b Authorization or \b Proxy-Authorization) will + * be added from dialog's authorization cache. + * + * Note that upon return the reference counter of the transmit data + * will be set to one. When the message is sent, #pjsip_dlg_send_msg() will + * decrement the reference counter, and when the reference counter reach zero, + * the message will be deleted. + * + * @param dlg The dialog. + * @param method The request method. + * @param cseq Specify CSeq, or -1 to let the dialog specify CSeq. + * + * @return Transmit data for the new request. + * + * @see pjsip_dlg_send_msg() + */ +PJ_DECL(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg, + const pjsip_method *method, + int cseq); + + +/** + * This function can be called by application to send outgoing message (request + * or response) to remote party. Note that after calling this function, the + * transmit data will be deleted regardless of the return status. To prevent + * deletion, application must increase the reference count, but then it will + * be responsible to delete this transmit data itself (by decreasing the + * reference count). + * + * @param dlg The dialog. + * @param tdata The transmit data, which contains the request message. + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, + pjsip_tx_data *tdata ); + + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_DIALOG_H__ */ + diff --git a/pjsip/src/pjsip_mod_ua/sip_reg.c b/pjsip/src/pjsip_mod_ua/sip_reg.c index bf886f442..23b486374 100644 --- a/pjsip/src/pjsip_mod_ua/sip_reg.c +++ b/pjsip/src/pjsip_mod_ua/sip_reg.c @@ -1,494 +1,494 @@ -/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_reg.c 14 8/31/05 9:05p Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REFRESH_TIMER 1 -#define DELAY_BEFORE_REFRESH 5 -#define THIS_FILE "sip_regc.c" - -/** - * SIP client registration structure. - */ -struct pjsip_regc -{ - pj_pool_t *pool; - pjsip_endpoint *endpt; - pj_bool_t _delete_flag; - int pending_tsx; - - void *token; - pjsip_regc_cb *cb; - - pj_str_t str_srv_url; - pjsip_uri *srv_url; - pjsip_cid_hdr *cid_hdr; - pjsip_cseq_hdr *cseq_hdr; - pjsip_from_hdr *from_hdr; - pjsip_to_hdr *to_hdr; - char *contact_buf; - pjsip_generic_string_hdr *contact_hdr; - pjsip_expires_hdr *expires_hdr; - pjsip_contact_hdr *unreg_contact_hdr; - pjsip_expires_hdr *unreg_expires_hdr; - pj_uint32_t expires; - - /* Credentials. */ - int cred_count; - pjsip_cred_info *cred_info; - - /* Authorization sessions. */ - pjsip_auth_session auth_sess_list; - - /* Auto refresh registration. */ - pj_bool_t auto_reg; - pj_timer_entry timer; -}; - - - -PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, - pjsip_regc_cb *cb) -{ - pj_pool_t *pool; - pjsip_regc *regc; - - if (cb == NULL) - return NULL; - - pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024); - regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc)); - - regc->pool = pool; - regc->endpt = endpt; - regc->token = token; - regc->cb = cb; - regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE); - regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; - - pj_list_init(®c->auth_sess_list); - - return regc; -} - - -PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc) -{ - if (regc->pending_tsx) { - regc->_delete_flag = 1; - regc->cb = NULL; - } else { - pjsip_endpt_destroy_pool(regc->endpt, regc->pool); - } -} - - -PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc) -{ - return regc->pool; -} - -static void set_expires( pjsip_regc *regc, pj_uint32_t expires) -{ - if (expires != regc->expires) { - regc->expires_hdr = pjsip_expires_hdr_create(regc->pool); - regc->expires_hdr->ivalue = expires; - } else { - regc->expires_hdr = NULL; - } -} - - -static pj_status_t set_contact( pjsip_regc *regc, - int contact_cnt, - const pj_str_t contact[] ) -{ - int i; - char *s; - const pj_str_t contact_STR = { "Contact", 7}; - - /* Concatenate contacts. */ - for (i=0, s=regc->contact_buf; icontact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) { - return -1; - } - pj_memcpy(s, contact[i].ptr, contact[i].slen); - s += contact[i].slen; - - if (i != contact_cnt - 1) { - *s++ = ','; - *s++ = ' '; - } - } - - /* Set "Contact" header. */ - regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR); - regc->contact_hdr->hvalue.ptr = regc->contact_buf; - regc->contact_hdr->hvalue.slen = (s - regc->contact_buf); - - return 0; -} - - -PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, - const pj_str_t *srv_url, - const pj_str_t *from_url, - const pj_str_t *to_url, - int contact_cnt, - const pj_str_t contact[], - pj_uint32_t expires) -{ - pj_str_t tmp; - - /* Copy server URL. */ - pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); - - /* Set server URL. */ - tmp = regc->str_srv_url; - regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0); - if (regc->srv_url == NULL) { - return -1; - } - - /* Set "From" header. */ - pj_strdup_with_null(regc->pool, &tmp, from_url); - regc->from_hdr = pjsip_from_hdr_create(regc->pool); - regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (!regc->from_hdr->uri) { - PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr)); - return -1; - } - - /* Set "To" header. */ - pj_strdup_with_null(regc->pool, &tmp, to_url); - regc->to_hdr = pjsip_to_hdr_create(regc->pool); - regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (!regc->to_hdr->uri) { - PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr)); - return -1; - } - - - /* Set "Contact" header. */ - if (set_contact( regc, contact_cnt, contact) != 0) - return -1; - - /* Set "Expires" header, if required. */ - set_expires( regc, expires); - - /* Set "Call-ID" header. */ - regc->cid_hdr = pjsip_cid_hdr_create(regc->pool); - pj_create_unique_string(regc->pool, ®c->cid_hdr->id); - - /* Set "CSeq" header. */ - regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool); - regc->cseq_hdr->cseq = 0; - pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD); - - /* Create "Contact" header used in unregistration. */ - regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool); - regc->unreg_contact_hdr->star = 1; - - /* Create "Expires" header used in unregistration. */ - regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool); - regc->unreg_expires_hdr->ivalue = 0; - - /* Done. */ - return 0; -} - -PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, - int count, - const pjsip_cred_info cred[] ) -{ - if (count > 0) { - regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info)); - pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info)); - } - regc->cred_count = count; - return 0; -} - -static pjsip_tx_data *create_request(pjsip_regc *regc) -{ - pjsip_tx_data *tdata; - pjsip_msg *msg; - - /* Create transmit data. */ - tdata = pjsip_endpt_create_tdata(regc->endpt); - if (!tdata) { - return NULL; - } - - /* Create request message. */ - msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); - tdata->msg = msg; - - /* Initialize request line. */ - pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD); - msg->line.req.uri = regc->srv_url; - - /* Add headers. */ - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr); - - /* Add cached authorization headers. */ - pjsip_auth_init_req( regc->pool, tdata, ®c->auth_sess_list, - regc->cred_count, regc->cred_info ); - - /* Add reference counter to transmit data. */ - pjsip_tx_data_add_ref(tdata); - - return tdata; -} - - -PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg) -{ - pjsip_msg *msg; - pjsip_tx_data *tdata; - - tdata = create_request(regc); - if (!tdata) - return NULL; - - msg = tdata->msg; - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr); - if (regc->expires_hdr) - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr); - - if (regc->timer.id != 0) { - pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); - regc->timer.id = 0; - } - - regc->auto_reg = autoreg; - - return tdata; -} - - -PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc) -{ - pjsip_tx_data *tdata; - pjsip_msg *msg; - - if (regc->timer.id != 0) { - pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); - regc->timer.id = 0; - } - - tdata = create_request(regc); - if (!tdata) - return NULL; - - msg = tdata->msg; - pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr); - pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); - - return tdata; -} - - -PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, - int contact_cnt, - const pj_str_t contact[] ) -{ - return set_contact( regc, contact_cnt, contact ); -} - - -PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, - pj_uint32_t expires ) -{ - set_expires( regc, expires ); - return 0; -} - - -static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason, - pjsip_rx_data *rdata, pj_int32_t expiration, - int contact_cnt, pjsip_contact_hdr *contact[]) -{ - struct pjsip_regc_cbparam cbparam; - - - cbparam.regc = regc; - cbparam.token = regc->token; - cbparam.code = status; - cbparam.reason = *reason; - cbparam.rdata = rdata; - cbparam.contact_cnt = contact_cnt; - cbparam.expiration = expiration; - if (contact_cnt) { - pj_memcpy( cbparam.contact, contact, - contact_cnt*sizeof(pjsip_contact_hdr*)); - } - - (*regc->cb)(&cbparam); -} - -static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, - struct pj_timer_entry *entry) -{ - pjsip_regc *regc = entry->user_data; - pjsip_tx_data *tdata; - - PJ_UNUSED_ARG(timer_heap) - - entry->id = 0; - tdata = pjsip_regc_register(regc, 1); - if (tdata) { - pjsip_regc_send(regc, tdata); - } else { - pj_str_t reason = pj_str("Unable to create txdata"); - call_callback(regc, -1, &reason, NULL, -1, 0, NULL); - } -} - -static void tsx_callback(void *token, pjsip_event *event) -{ - pjsip_regc *regc = token; - pjsip_transaction *tsx = event->obj.tsx; - - /* If registration data has been deleted by user then remove registration - * data from transaction's callback, and don't call callback. - */ - if (regc->_delete_flag) { - --regc->pending_tsx; - - } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || - tsx->status_code == PJSIP_SC_UNAUTHORIZED) - { - pjsip_rx_data *rdata = event->src.rdata; - pjsip_tx_data *tdata; - - tdata = pjsip_auth_reinit_req( regc->endpt, - regc->pool, ®c->auth_sess_list, - regc->cred_count, regc->cred_info, - tsx->last_tx, event->src.rdata ); - - if (tdata) { - --regc->pending_tsx; - pjsip_regc_send(regc, tdata); - return; - } else { - call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason, - rdata, -1, 0, NULL); - --regc->pending_tsx; - } - } else { - int contact_cnt = 0; - pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; - pjsip_rx_data *rdata; - pj_int32_t expiration = 0xFFFF; - - if (tsx->status_code/100 == 2) { - int i; - pjsip_contact_hdr *hdr; - pjsip_msg *msg; - pjsip_expires_hdr *expires; - - rdata = event->src.rdata; - msg = rdata->msg; - hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); - while (hdr) { - contact[contact_cnt++] = hdr; - hdr = hdr->next; - if (hdr == (void*)&msg->hdr) - break; - hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr); - } - - expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); - - if (expires) - expiration = expires->ivalue; - - for (i=0; iexpires >= 0 && hdr->expires < expiration) - expiration = contact[i]->expires; - } - - if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) { - pj_time_val delay = { 0, 0}; - - delay.sec = expiration - DELAY_BEFORE_REFRESH; - if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && - delay.sec > (pj_int32_t)regc->expires) - { - delay.sec = regc->expires; - } - if (delay.sec < DELAY_BEFORE_REFRESH) - delay.sec = DELAY_BEFORE_REFRESH; - regc->timer.cb = ®c_refresh_timer_cb; - regc->timer.id = REFRESH_TIMER; - regc->timer.user_data = regc; - pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay); - } - - } else { - rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL; - } - - - /* Call callback. */ - if (expiration == 0xFFFF) expiration = -1; - call_callback(regc, tsx->status_code, - (rdata ? &rdata->msg->line.status.reason - : pjsip_get_status_text(tsx->status_code)), - rdata, expiration, - contact_cnt, contact); - - --regc->pending_tsx; - } - - /* Delete the record if user destroy regc during the callback. */ - if (regc->_delete_flag && regc->pending_tsx==0) { - pjsip_regc_destroy(regc); - } -} - -PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) -{ - int status; - - /* Make sure we don't have pending transaction. */ - if (regc->pending_tsx) { - pj_str_t reason = pj_str("Transaction in progress"); - call_callback(regc, -1, &reason, NULL, -1, 0, NULL); - pjsip_tx_data_dec_ref( tdata ); - return; - } - - /* Invalidate message buffer. */ - pjsip_tx_data_invalidate_msg(tdata); - - /* Increment CSeq */ - regc->cseq_hdr->cseq++; - - /* Send. */ - status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback); - if (status==0) - ++regc->pending_tsx; - else { - pj_str_t reason = pj_str("Unable to send request."); - call_callback(regc, status, &reason, NULL, -1, 0, NULL); - } -} - - +/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_reg.c 14 8/31/05 9:05p Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REFRESH_TIMER 1 +#define DELAY_BEFORE_REFRESH 5 +#define THIS_FILE "sip_regc.c" + +/** + * SIP client registration structure. + */ +struct pjsip_regc +{ + pj_pool_t *pool; + pjsip_endpoint *endpt; + pj_bool_t _delete_flag; + int pending_tsx; + + void *token; + pjsip_regc_cb *cb; + + pj_str_t str_srv_url; + pjsip_uri *srv_url; + pjsip_cid_hdr *cid_hdr; + pjsip_cseq_hdr *cseq_hdr; + pjsip_from_hdr *from_hdr; + pjsip_to_hdr *to_hdr; + char *contact_buf; + pjsip_generic_string_hdr *contact_hdr; + pjsip_expires_hdr *expires_hdr; + pjsip_contact_hdr *unreg_contact_hdr; + pjsip_expires_hdr *unreg_expires_hdr; + pj_uint32_t expires; + + /* Credentials. */ + int cred_count; + pjsip_cred_info *cred_info; + + /* Authorization sessions. */ + pjsip_auth_session auth_sess_list; + + /* Auto refresh registration. */ + pj_bool_t auto_reg; + pj_timer_entry timer; +}; + + + +PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, + pjsip_regc_cb *cb) +{ + pj_pool_t *pool; + pjsip_regc *regc; + + if (cb == NULL) + return NULL; + + pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024); + regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc)); + + regc->pool = pool; + regc->endpt = endpt; + regc->token = token; + regc->cb = cb; + regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE); + regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; + + pj_list_init(®c->auth_sess_list); + + return regc; +} + + +PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc) +{ + if (regc->pending_tsx) { + regc->_delete_flag = 1; + regc->cb = NULL; + } else { + pjsip_endpt_destroy_pool(regc->endpt, regc->pool); + } +} + + +PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc) +{ + return regc->pool; +} + +static void set_expires( pjsip_regc *regc, pj_uint32_t expires) +{ + if (expires != regc->expires) { + regc->expires_hdr = pjsip_expires_hdr_create(regc->pool); + regc->expires_hdr->ivalue = expires; + } else { + regc->expires_hdr = NULL; + } +} + + +static pj_status_t set_contact( pjsip_regc *regc, + int contact_cnt, + const pj_str_t contact[] ) +{ + int i; + char *s; + const pj_str_t contact_STR = { "Contact", 7}; + + /* Concatenate contacts. */ + for (i=0, s=regc->contact_buf; icontact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) { + return -1; + } + pj_memcpy(s, contact[i].ptr, contact[i].slen); + s += contact[i].slen; + + if (i != contact_cnt - 1) { + *s++ = ','; + *s++ = ' '; + } + } + + /* Set "Contact" header. */ + regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR); + regc->contact_hdr->hvalue.ptr = regc->contact_buf; + regc->contact_hdr->hvalue.slen = (s - regc->contact_buf); + + return 0; +} + + +PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, + const pj_str_t *srv_url, + const pj_str_t *from_url, + const pj_str_t *to_url, + int contact_cnt, + const pj_str_t contact[], + pj_uint32_t expires) +{ + pj_str_t tmp; + + /* Copy server URL. */ + pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); + + /* Set server URL. */ + tmp = regc->str_srv_url; + regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0); + if (regc->srv_url == NULL) { + return -1; + } + + /* Set "From" header. */ + pj_strdup_with_null(regc->pool, &tmp, from_url); + regc->from_hdr = pjsip_from_hdr_create(regc->pool); + regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (!regc->from_hdr->uri) { + PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr)); + return -1; + } + + /* Set "To" header. */ + pj_strdup_with_null(regc->pool, &tmp, to_url); + regc->to_hdr = pjsip_to_hdr_create(regc->pool); + regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (!regc->to_hdr->uri) { + PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr)); + return -1; + } + + + /* Set "Contact" header. */ + if (set_contact( regc, contact_cnt, contact) != 0) + return -1; + + /* Set "Expires" header, if required. */ + set_expires( regc, expires); + + /* Set "Call-ID" header. */ + regc->cid_hdr = pjsip_cid_hdr_create(regc->pool); + pj_create_unique_string(regc->pool, ®c->cid_hdr->id); + + /* Set "CSeq" header. */ + regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool); + regc->cseq_hdr->cseq = 0; + pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD); + + /* Create "Contact" header used in unregistration. */ + regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool); + regc->unreg_contact_hdr->star = 1; + + /* Create "Expires" header used in unregistration. */ + regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool); + regc->unreg_expires_hdr->ivalue = 0; + + /* Done. */ + return 0; +} + +PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, + int count, + const pjsip_cred_info cred[] ) +{ + if (count > 0) { + regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info)); + pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info)); + } + regc->cred_count = count; + return 0; +} + +static pjsip_tx_data *create_request(pjsip_regc *regc) +{ + pjsip_tx_data *tdata; + pjsip_msg *msg; + + /* Create transmit data. */ + tdata = pjsip_endpt_create_tdata(regc->endpt); + if (!tdata) { + return NULL; + } + + /* Create request message. */ + msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); + tdata->msg = msg; + + /* Initialize request line. */ + pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD); + msg->line.req.uri = regc->srv_url; + + /* Add headers. */ + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr); + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr); + + /* Add cached authorization headers. */ + pjsip_auth_init_req( regc->pool, tdata, ®c->auth_sess_list, + regc->cred_count, regc->cred_info ); + + /* Add reference counter to transmit data. */ + pjsip_tx_data_add_ref(tdata); + + return tdata; +} + + +PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg) +{ + pjsip_msg *msg; + pjsip_tx_data *tdata; + + tdata = create_request(regc); + if (!tdata) + return NULL; + + msg = tdata->msg; + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr); + if (regc->expires_hdr) + pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr); + + if (regc->timer.id != 0) { + pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); + regc->timer.id = 0; + } + + regc->auto_reg = autoreg; + + return tdata; +} + + +PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc) +{ + pjsip_tx_data *tdata; + pjsip_msg *msg; + + if (regc->timer.id != 0) { + pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); + regc->timer.id = 0; + } + + tdata = create_request(regc); + if (!tdata) + return NULL; + + msg = tdata->msg; + pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr); + pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); + + return tdata; +} + + +PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, + int contact_cnt, + const pj_str_t contact[] ) +{ + return set_contact( regc, contact_cnt, contact ); +} + + +PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, + pj_uint32_t expires ) +{ + set_expires( regc, expires ); + return 0; +} + + +static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason, + pjsip_rx_data *rdata, pj_int32_t expiration, + int contact_cnt, pjsip_contact_hdr *contact[]) +{ + struct pjsip_regc_cbparam cbparam; + + + cbparam.regc = regc; + cbparam.token = regc->token; + cbparam.code = status; + cbparam.reason = *reason; + cbparam.rdata = rdata; + cbparam.contact_cnt = contact_cnt; + cbparam.expiration = expiration; + if (contact_cnt) { + pj_memcpy( cbparam.contact, contact, + contact_cnt*sizeof(pjsip_contact_hdr*)); + } + + (*regc->cb)(&cbparam); +} + +static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry) +{ + pjsip_regc *regc = entry->user_data; + pjsip_tx_data *tdata; + + PJ_UNUSED_ARG(timer_heap) + + entry->id = 0; + tdata = pjsip_regc_register(regc, 1); + if (tdata) { + pjsip_regc_send(regc, tdata); + } else { + pj_str_t reason = pj_str("Unable to create txdata"); + call_callback(regc, -1, &reason, NULL, -1, 0, NULL); + } +} + +static void tsx_callback(void *token, pjsip_event *event) +{ + pjsip_regc *regc = token; + pjsip_transaction *tsx = event->obj.tsx; + + /* If registration data has been deleted by user then remove registration + * data from transaction's callback, and don't call callback. + */ + if (regc->_delete_flag) { + --regc->pending_tsx; + + } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || + tsx->status_code == PJSIP_SC_UNAUTHORIZED) + { + pjsip_rx_data *rdata = event->src.rdata; + pjsip_tx_data *tdata; + + tdata = pjsip_auth_reinit_req( regc->endpt, + regc->pool, ®c->auth_sess_list, + regc->cred_count, regc->cred_info, + tsx->last_tx, event->src.rdata ); + + if (tdata) { + --regc->pending_tsx; + pjsip_regc_send(regc, tdata); + return; + } else { + call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason, + rdata, -1, 0, NULL); + --regc->pending_tsx; + } + } else { + int contact_cnt = 0; + pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; + pjsip_rx_data *rdata; + pj_int32_t expiration = 0xFFFF; + + if (tsx->status_code/100 == 2) { + int i; + pjsip_contact_hdr *hdr; + pjsip_msg *msg; + pjsip_expires_hdr *expires; + + rdata = event->src.rdata; + msg = rdata->msg; + hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); + while (hdr) { + contact[contact_cnt++] = hdr; + hdr = hdr->next; + if (hdr == (void*)&msg->hdr) + break; + hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr); + } + + expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); + + if (expires) + expiration = expires->ivalue; + + for (i=0; iexpires >= 0 && hdr->expires < expiration) + expiration = contact[i]->expires; + } + + if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) { + pj_time_val delay = { 0, 0}; + + delay.sec = expiration - DELAY_BEFORE_REFRESH; + if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && + delay.sec > (pj_int32_t)regc->expires) + { + delay.sec = regc->expires; + } + if (delay.sec < DELAY_BEFORE_REFRESH) + delay.sec = DELAY_BEFORE_REFRESH; + regc->timer.cb = ®c_refresh_timer_cb; + regc->timer.id = REFRESH_TIMER; + regc->timer.user_data = regc; + pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay); + } + + } else { + rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL; + } + + + /* Call callback. */ + if (expiration == 0xFFFF) expiration = -1; + call_callback(regc, tsx->status_code, + (rdata ? &rdata->msg->line.status.reason + : pjsip_get_status_text(tsx->status_code)), + rdata, expiration, + contact_cnt, contact); + + --regc->pending_tsx; + } + + /* Delete the record if user destroy regc during the callback. */ + if (regc->_delete_flag && regc->pending_tsx==0) { + pjsip_regc_destroy(regc); + } +} + +PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) +{ + int status; + + /* Make sure we don't have pending transaction. */ + if (regc->pending_tsx) { + pj_str_t reason = pj_str("Transaction in progress"); + call_callback(regc, -1, &reason, NULL, -1, 0, NULL); + pjsip_tx_data_dec_ref( tdata ); + return; + } + + /* Invalidate message buffer. */ + pjsip_tx_data_invalidate_msg(tdata); + + /* Increment CSeq */ + regc->cseq_hdr->cseq++; + + /* Send. */ + status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback); + if (status==0) + ++regc->pending_tsx; + else { + pj_str_t reason = pj_str("Unable to send request."); + call_callback(regc, status, &reason, NULL, -1, 0, NULL); + } +} + + diff --git a/pjsip/src/pjsip_mod_ua/sip_reg.h b/pjsip/src/pjsip_mod_ua/sip_reg.h index f5158f710..48d56519a 100644 --- a/pjsip/src/pjsip_mod_ua/sip_reg.h +++ b/pjsip/src/pjsip_mod_ua/sip_reg.h @@ -1,193 +1,193 @@ -/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_reg.h 11 8/24/05 10:35a Bennylp $ */ -#ifndef __PJSIP_SIP_REG_H__ -#define __PJSIP_SIP_REG_H__ - -/** - * @file sip_reg.h - * @brief SIP Registration Client - */ - -#include -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSUA_REGC SIP Registration Client - * @ingroup PJSUA - * @{ - * \brief - * API for performing registration for user agent. - */ - -/** Typedef for client registration data. */ -typedef struct pjsip_regc pjsip_regc; - -/** Maximum contacts in registration. */ -#define PJSIP_REGC_MAX_CONTACT 10 - -/** Expiration not specified. */ -#define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL) - -/** Buffer to hold all contacts. */ -#define PJSIP_REGC_CONTACT_BUF_SIZE 512 - -/** Structure to hold parameters when calling application's callback. - * The application's callback is called when the client registration process - * has finished. - */ -struct pjsip_regc_cbparam -{ - pjsip_regc *regc; - void *token; - int code; - pj_str_t reason; - pjsip_rx_data *rdata; - int contact_cnt; - int expiration; - pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; -}; - - -/** Type declaration for callback to receive registration result. */ -typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param); - - -/** - * Get the module instance for client registration module. - * - * @return client registration module. - */ -PJ_DECL(pjsip_module*) pjsip_regc_get_module(void); - - -/** - * Create client registration structure. - * - * @param endpt Endpoint, used to allocate pool from. - * @param token A data to be associated with the client registration struct. - * @param cb Pointer to callback function to receive registration status. - * - * @return client registration structure. - */ -PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, - pjsip_regc_cb *cb); - - -/** - * Destroy client registration structure. If a registration transaction is - * in progress, then the structure will be deleted only after a final response - * has been received, and in this case, the callback won't be called. - * - * @param regc The client registration structure. - */ -PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc); - -/** - * Get the memory pool associated with a registration client handle. - * - * @param regc The client registration structure. - * @return pool handle. - */ -PJ_DECL(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc); - -/** - * Initialize client registration structure with various information needed to - * perform the registration. - * - * @param regc The client registration structure. - * @param from_url The person performing the registration, must be a SIP URL type. - * @param to_url The address of record for which the registration is targetd, must - * be a SIP/SIPS URL. - * @param ccnt Number of contacts in the array. - * @param contact Array of contacts. - * @param expires Default expiration interval (in seconds) to be applied for - * contact URL that doesn't have expiration settings. If the - * value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then - * no default expiration will be applied. - * @return zero on success. - */ -PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc, - const pj_str_t *srv_url, - const pj_str_t *from_url, - const pj_str_t *to_url, - int ccnt, - const pj_str_t contact[], - pj_uint32_t expires); - - -/** - * Set authentication credentials to use by this registration. - * - * @param dlg The registration structure. - * @param count Number of credentials in the array. - * @param cred Array of credentials. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, - int count, - const pjsip_cred_info cred[] ); - -/** - * Create REGISTER request for the specified client registration structure. - * - * After successfull registration, application can inspect the contacts in - * the client registration structure to list what contacts are associaciated - * with the address of record being targeted in the registration. - * - * @param regc The client registration structure. - * @param autoreg If non zero, the library will automatically refresh the - * next registration until application unregister. - * - * @return SIP REGISTER request. - */ -PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg); - - -/** - * Create REGISTER request to unregister all contacts from server records. - * - * @param regc The client registration structure. - * - * @return SIP REGISTER request. - */ -PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc); - -/** - * Update Contact details in the client registration structure. - * - * @param regc The client registration structure. - * @param ccnt Number of contacts. - * @param contact Array of contacts. - * @return zero if sucessfull. - */ -PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, - int ccnt, - const pj_str_t contact[] ); - -/** - * Update the expires value. - * - * @param regc The client registration structure. - * @param expires The new expires value. - * @return zero on successfull. - */ -PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, - pj_uint32_t expires ); - -/** - * Sends outgoing REGISTER request. - * The process will complete asynchronously, and application - * will be notified via the callback when the process completes. - * - * @param regc The client registration structure. - * @param tdata Transmit data. - */ -PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata); - - -PJ_END_DECL - -#endif /* __PJSIP_REG_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_reg.h 11 8/24/05 10:35a Bennylp $ */ +#ifndef __PJSIP_SIP_REG_H__ +#define __PJSIP_SIP_REG_H__ + +/** + * @file sip_reg.h + * @brief SIP Registration Client + */ + +#include +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSUA_REGC SIP Registration Client + * @ingroup PJSUA + * @{ + * \brief + * API for performing registration for user agent. + */ + +/** Typedef for client registration data. */ +typedef struct pjsip_regc pjsip_regc; + +/** Maximum contacts in registration. */ +#define PJSIP_REGC_MAX_CONTACT 10 + +/** Expiration not specified. */ +#define PJSIP_REGC_EXPIRATION_NOT_SPECIFIED ((pj_uint32_t)0xFFFFFFFFUL) + +/** Buffer to hold all contacts. */ +#define PJSIP_REGC_CONTACT_BUF_SIZE 512 + +/** Structure to hold parameters when calling application's callback. + * The application's callback is called when the client registration process + * has finished. + */ +struct pjsip_regc_cbparam +{ + pjsip_regc *regc; + void *token; + int code; + pj_str_t reason; + pjsip_rx_data *rdata; + int contact_cnt; + int expiration; + pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; +}; + + +/** Type declaration for callback to receive registration result. */ +typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param); + + +/** + * Get the module instance for client registration module. + * + * @return client registration module. + */ +PJ_DECL(pjsip_module*) pjsip_regc_get_module(void); + + +/** + * Create client registration structure. + * + * @param endpt Endpoint, used to allocate pool from. + * @param token A data to be associated with the client registration struct. + * @param cb Pointer to callback function to receive registration status. + * + * @return client registration structure. + */ +PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, + pjsip_regc_cb *cb); + + +/** + * Destroy client registration structure. If a registration transaction is + * in progress, then the structure will be deleted only after a final response + * has been received, and in this case, the callback won't be called. + * + * @param regc The client registration structure. + */ +PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc); + +/** + * Get the memory pool associated with a registration client handle. + * + * @param regc The client registration structure. + * @return pool handle. + */ +PJ_DECL(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc); + +/** + * Initialize client registration structure with various information needed to + * perform the registration. + * + * @param regc The client registration structure. + * @param from_url The person performing the registration, must be a SIP URL type. + * @param to_url The address of record for which the registration is targetd, must + * be a SIP/SIPS URL. + * @param ccnt Number of contacts in the array. + * @param contact Array of contacts. + * @param expires Default expiration interval (in seconds) to be applied for + * contact URL that doesn't have expiration settings. If the + * value PJSIP_REGC_EXPIRATION_NOT_SPECIFIED is given, then + * no default expiration will be applied. + * @return zero on success. + */ +PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc, + const pj_str_t *srv_url, + const pj_str_t *from_url, + const pj_str_t *to_url, + int ccnt, + const pj_str_t contact[], + pj_uint32_t expires); + + +/** + * Set authentication credentials to use by this registration. + * + * @param dlg The registration structure. + * @param count Number of credentials in the array. + * @param cred Array of credentials. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, + int count, + const pjsip_cred_info cred[] ); + +/** + * Create REGISTER request for the specified client registration structure. + * + * After successfull registration, application can inspect the contacts in + * the client registration structure to list what contacts are associaciated + * with the address of record being targeted in the registration. + * + * @param regc The client registration structure. + * @param autoreg If non zero, the library will automatically refresh the + * next registration until application unregister. + * + * @return SIP REGISTER request. + */ +PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg); + + +/** + * Create REGISTER request to unregister all contacts from server records. + * + * @param regc The client registration structure. + * + * @return SIP REGISTER request. + */ +PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc); + +/** + * Update Contact details in the client registration structure. + * + * @param regc The client registration structure. + * @param ccnt Number of contacts. + * @param contact Array of contacts. + * @return zero if sucessfull. + */ +PJ_DECL(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, + int ccnt, + const pj_str_t contact[] ); + +/** + * Update the expires value. + * + * @param regc The client registration structure. + * @param expires The new expires value. + * @return zero on successfull. + */ +PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, + pj_uint32_t expires ); + +/** + * Sends outgoing REGISTER request. + * The process will complete asynchronously, and application + * will be notified via the callback when the process completes. + * + * @param regc The client registration structure. + * @param tdata Transmit data. + */ +PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata); + + +PJ_END_DECL + +#endif /* __PJSIP_REG_H__ */ diff --git a/pjsip/src/pjsip_mod_ua/sip_ua.c b/pjsip/src/pjsip_mod_ua/sip_ua.c index ade2be364..bd7fe0152 100644 --- a/pjsip/src/pjsip_mod_ua/sip_ua.c +++ b/pjsip/src/pjsip_mod_ua/sip_ua.c @@ -1,489 +1,489 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsip_mod_ua/sip_ua.c 16 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PJSIP_POOL_LEN_USER_AGENT 1024 -#define PJSIP_POOL_INC_USER_AGENT 0 - - -#define LOG_THIS "useragent.." - -/* - * Static prototypes. - */ -static pj_status_t ua_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ); -static pj_status_t ua_start( struct pjsip_module *mod ); -static pj_status_t ua_deinit( struct pjsip_module *mod ); -static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt ); -static pjsip_dlg *find_dialog( pjsip_user_agent *ua, - pjsip_rx_data *rdata ); -static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, - pj_str_t *key ); -PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ); - -/* - * Default UA instance. - */ -static pjsip_user_agent ua_instance; - -/* - * Module interface. - */ -static struct pjsip_module mod_ua = -{ - { "User-Agent", 10 }, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - NULL, /* User agent instance, initialized by APP. */ - 0, /* Number of methods supported (will be initialized later). */ - { 0 }, /* Array of methods (will be initialized later) */ - &ua_init, /* init_module() */ - &ua_start, /* start_module() */ - &ua_deinit, /* deinit_module() */ - &ua_tsx_handler, /* tsx_handler() */ -}; - -/* - * Initialize user agent instance. - */ -static pj_status_t ua_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - static pjsip_method m_invite, m_ack, m_cancel, m_bye; - pjsip_user_agent *ua = mod->mod_data; - extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */ - - pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD ); - pjsip_method_set( &m_ack, PJSIP_ACK_METHOD ); - pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD ); - pjsip_method_set( &m_bye, PJSIP_BYE_METHOD ); - - mod->method_cnt = 4; - mod->methods[0] = &m_invite; - mod->methods[1] = &m_ack; - mod->methods[2] = &m_cancel; - mod->methods[3] = &m_bye; - - /* Initialize the user agent. */ - ua->endpt = endpt; - ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA, - PJSIP_POOL_INC_UA); - if (!ua->pool) { - return -1; - } - ua->mod_id = id; - ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0); - if (!ua->mutex) { - return -1; - } - ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT); - if (ua->dlg_table == NULL) { - return -1; - } - pj_list_init(&ua->dlg_list); - - /* Initialize dialog lock. */ - pjsip_dlg_lock_tls_id = pj_thread_local_alloc(); - if (pjsip_dlg_lock_tls_id == -1) { - return -1; - } - pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL); - - return 0; -} - -/* - * Start user agent instance. - */ -static pj_status_t ua_start( struct pjsip_module *mod ) -{ - PJ_UNUSED_ARG(mod) - return 0; -} - -/* - * Destroy user agent. - */ -static pj_status_t ua_deinit( struct pjsip_module *mod ) -{ - pjsip_user_agent *ua = mod->mod_data; - - pj_mutex_unlock(ua->mutex); - - /* Release pool */ - if (ua->pool) { - pjsip_endpt_destroy_pool( ua->endpt, ua->pool ); - } - return 0; -} - -/* - * Get the module interface for the UA module. - */ -PJ_DEF(pjsip_module*) pjsip_ua_get_module(void) -{ - mod_ua.mod_data = &ua_instance; - return &mod_ua; -} - -/* - * Register callback to receive dialog notifications. - */ -PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, - pjsip_dlg_callback *cb ) -{ - ua->dlg_cb = cb; -} - -/* - * Find dialog. - * This function is called for a new transactions, which a dialog hasn't been - * 'attached' to the transaction. - */ -static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata ) -{ - pjsip_dlg *dlg; - pj_str_t *tag; - - /* Non-CANCEL requests/response can be found by looking at the tag in the - * hash table. CANCEL requests don't have tags, so instead we'll try to - * find the UAS INVITE transaction in endpoint's hash table - */ - if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) { - - /* Create key for the rdata, but this time, use INVITE as the - * method. - */ - pj_str_t key; - pjsip_role_e role; - pjsip_method invite_method; - pjsip_transaction *invite_tsx; - - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - role = PJSIP_ROLE_UAS; - } else { - role = PJSIP_ROLE_UAC; - } - pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD); - pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata); - - /* Lookup the INVITE transaction */ - invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key); - - /* We should find the dialog attached to the INVITE transaction */ - return invite_tsx ? - (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL; - - } else { - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - tag = &rdata->to_tag; - } else { - tag = &rdata->from_tag; - } - /* Find the dialog in UA hash table */ - pj_mutex_lock(ua->mutex); - dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen ); - pj_mutex_unlock(ua->mutex); - } - - return dlg; -} - -/* - * This function receives event notification from transactions. It is called by - * endpoint. - */ -static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_user_agent *ua = mod->mod_data; - pjsip_dlg *dlg = NULL; - pjsip_transaction *tsx = event->obj.tsx; - - PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)", - (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type), - pjsip_event_str(event->src_type), event->src.data)); - - /* Special case to handle ACK which doesn't match any INVITE transactions. */ - if (event->type == PJSIP_EVENT_RX_ACK_MSG) { - /* Find the dialog based on the "tag". */ - dlg = find_dialog( ua, event->src.rdata ); - - /* We should be able to find it. */ - if (!dlg) { - PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK")); - return; - } - - /* Match CSeq with pending INVITE in dialog. */ - if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) { - /* A match found. */ - tsx = dlg->invite_tsx; - - /* Pass the event to transaction if transaction handles ACK. */ - if (tsx->handle_ack) { - PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction")); - pjsip_tsx_on_rx_msg(tsx, event->src.rdata); - return; - } - } else { - tsx = NULL; - PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK")); - return; - } - } - - /* For discard event, transaction is NULL. */ - if (tsx == NULL) { - return; - } - - /* Try to pickup the dlg from the transaction. */ - dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id]; - - if (dlg != NULL) { - - /* Nothing to do now. */ - - } else if (event->src_type == PJSIP_EVENT_RX_MSG) { - - /* This must be a new UAS transaction. */ - - /* Finds dlg that can handle this transaction. */ - dlg = find_dialog( ua, event->src.rdata); - - /* Create a new dlg if there's no existing dlg that can handle - the request, ONLY if the incoming message is an INVITE request. - */ - if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) { - - if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) { - /* Create new dialog. */ - dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS ); - - if (dlg == NULL || - pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0) - { - pjsip_tx_data *tdata; - - /* Dialog initialization has failed. Respond request with 500 */ - if (dlg) { - pjsip_ua_destroy_dialog(dlg); - } - tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, - PJSIP_SC_INTERNAL_SERVER_ERROR); - if (tdata) { - pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); - } - return; - } - - } else { - pjsip_tx_data *tdata; - - /* Check the method */ - switch (tsx->method.id) { - case PJSIP_INVITE_METHOD: - case PJSIP_ACK_METHOD: - case PJSIP_BYE_METHOD: - case PJSIP_CANCEL_METHOD: - /* Stale non-INVITE request. - * For now, respond all stale requests with 481 (?). - */ - tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, - PJSIP_SC_CALL_TSX_DOES_NOT_EXIST); - if (tdata) { - pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); - } - break; - } - - return; - } - } else { - /* Check the method */ - switch (tsx->method.id) { - case PJSIP_INVITE_METHOD: - case PJSIP_ACK_METHOD: - case PJSIP_BYE_METHOD: - case PJSIP_CANCEL_METHOD: - /* These methods belongs to dialog. - * If we receive these methods while no dialog is found, - * then it must be a stale responses. - */ - break; - default: - return; - } - - } - - if (dlg == NULL) { - PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d", - event->src.rdata, - pj_sockaddr_get_str_addr(&event->src.rdata->addr), - pj_sockaddr_get_port(&event->src.rdata->addr))); - } - - /* Set the dlg in the transaction (dlg can be NULL). */ - tsx->module_data[ua->mod_id] = dlg; - - } else { - /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG - * if UAS is responding to a transaction which does not exist. - * Just ignore. - */ - return; - } - - /* Pass the event to the dlg. */ - if (dlg) { - pjsip_dlg_on_tsx_event(dlg, tsx, event); - } -} - -/* - * Register dialog to UA. - */ -static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, - pj_str_t *key ) -{ - /* Assure that no entry with similar key exists in the hash table. */ - pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0); - - /* Insert entry to hash table. */ - pj_hash_set( dlg->pool, ua->dlg_table, - key->ptr, key->slen, dlg); - - /* Insert to the list. */ - pj_list_insert_before(&ua->dlg_list, dlg); - return PJ_SUCCESS; -} - -/* - * Create a new dialog. - */ -PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua, - pjsip_role_e role ) -{ - pj_pool_t *pool; - pjsip_dlg *dlg; - - PJ_UNUSED_ARG(ua) - - /* Create pool for the dialog. */ - pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p", - PJSIP_POOL_LEN_DIALOG, - PJSIP_POOL_INC_DIALOG); - - /* Create the dialog. */ - dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg)); - dlg->pool = pool; - dlg->ua = ua; - dlg->role = role; - sprintf(dlg->obj_name, "dlg%p", dlg); - - /* Create mutex for the dialog. */ - dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); - if (!dlg->mutex) { - pjsip_endpt_destroy_pool(ua->endpt, pool); - return NULL; - } - - /* Create unique tag for the dialog. */ - pj_create_unique_string( pool, &dlg->local.tag ); - - /* Register dialog. */ - pj_mutex_lock(ua->mutex); - if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) { - pj_mutex_unlock(ua->mutex); - pj_mutex_destroy(dlg->mutex); - pjsip_endpt_destroy_pool( ua->endpt, pool ); - return NULL; - } - pj_mutex_unlock(ua->mutex); - - PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role))); - return dlg; -} - -/* - * Destroy dialog. - */ -PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg ) -{ - PJ_LOG(5, (dlg->obj_name, "destroying..")); - - /* Lock dialog's mutex. - * Check the mutex validity first since this function can be called - * on dialog initialization failure (which might be because mutex could not - * be allocated in the first place). - */ - if (dlg->mutex) { - pj_mutex_lock(dlg->mutex); - } - - /* This must be called while holding dialog's mutex, if any. */ - pjsip_on_dialog_destroyed(dlg); - - /* Lock UA. */ - pj_mutex_lock(dlg->ua->mutex); - - /* Erase from hash table. */ - pj_hash_set( dlg->pool, dlg->ua->dlg_table, - dlg->local.tag.ptr, dlg->local.tag.slen, NULL); - - /* Erase from the list. */ - pj_list_erase(dlg); - - /* Unlock UA. */ - pj_mutex_unlock(dlg->ua->mutex); - - /* Unlock mutex. */ - if (dlg->mutex) { - pj_mutex_unlock(dlg->mutex); - } - - /* Destroy the pool. */ - pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool); -} - -/* - * Dump user agent state to log file. - */ -PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua) -{ -#if PJ_LOG_MAX_LEVEL >= 3 - PJ_LOG(3,(LOG_THIS, "Dumping user agent")); - PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u", - pj_pool_get_capacity(ua->pool), - pj_pool_get_used_size(ua->pool))); - PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table))); - - if (pj_hash_count(ua->dlg_table)) { - pjsip_dlg *dlg; - - PJ_LOG(3,(LOG_THIS, " Dumping dialog list:")); - dlg = ua->dlg_list.next; - while (dlg != (pjsip_dlg*) &ua->dlg_list) { - PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name, - pjsip_dlg_state_str(dlg->state))); - dlg = dlg->next; - } - } -#endif -} - +/* $Header: /pjproject-0.3/pjsip/src/pjsip_mod_ua/sip_ua.c 16 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PJSIP_POOL_LEN_USER_AGENT 1024 +#define PJSIP_POOL_INC_USER_AGENT 0 + + +#define LOG_THIS "useragent.." + +/* + * Static prototypes. + */ +static pj_status_t ua_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ); +static pj_status_t ua_start( struct pjsip_module *mod ); +static pj_status_t ua_deinit( struct pjsip_module *mod ); +static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt ); +static pjsip_dlg *find_dialog( pjsip_user_agent *ua, + pjsip_rx_data *rdata ); +static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, + pj_str_t *key ); +PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ); + +/* + * Default UA instance. + */ +static pjsip_user_agent ua_instance; + +/* + * Module interface. + */ +static struct pjsip_module mod_ua = +{ + { "User-Agent", 10 }, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + NULL, /* User agent instance, initialized by APP. */ + 0, /* Number of methods supported (will be initialized later). */ + { 0 }, /* Array of methods (will be initialized later) */ + &ua_init, /* init_module() */ + &ua_start, /* start_module() */ + &ua_deinit, /* deinit_module() */ + &ua_tsx_handler, /* tsx_handler() */ +}; + +/* + * Initialize user agent instance. + */ +static pj_status_t ua_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + static pjsip_method m_invite, m_ack, m_cancel, m_bye; + pjsip_user_agent *ua = mod->mod_data; + extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */ + + pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD ); + pjsip_method_set( &m_ack, PJSIP_ACK_METHOD ); + pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD ); + pjsip_method_set( &m_bye, PJSIP_BYE_METHOD ); + + mod->method_cnt = 4; + mod->methods[0] = &m_invite; + mod->methods[1] = &m_ack; + mod->methods[2] = &m_cancel; + mod->methods[3] = &m_bye; + + /* Initialize the user agent. */ + ua->endpt = endpt; + ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA, + PJSIP_POOL_INC_UA); + if (!ua->pool) { + return -1; + } + ua->mod_id = id; + ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0); + if (!ua->mutex) { + return -1; + } + ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT); + if (ua->dlg_table == NULL) { + return -1; + } + pj_list_init(&ua->dlg_list); + + /* Initialize dialog lock. */ + pjsip_dlg_lock_tls_id = pj_thread_local_alloc(); + if (pjsip_dlg_lock_tls_id == -1) { + return -1; + } + pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL); + + return 0; +} + +/* + * Start user agent instance. + */ +static pj_status_t ua_start( struct pjsip_module *mod ) +{ + PJ_UNUSED_ARG(mod) + return 0; +} + +/* + * Destroy user agent. + */ +static pj_status_t ua_deinit( struct pjsip_module *mod ) +{ + pjsip_user_agent *ua = mod->mod_data; + + pj_mutex_unlock(ua->mutex); + + /* Release pool */ + if (ua->pool) { + pjsip_endpt_destroy_pool( ua->endpt, ua->pool ); + } + return 0; +} + +/* + * Get the module interface for the UA module. + */ +PJ_DEF(pjsip_module*) pjsip_ua_get_module(void) +{ + mod_ua.mod_data = &ua_instance; + return &mod_ua; +} + +/* + * Register callback to receive dialog notifications. + */ +PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, + pjsip_dlg_callback *cb ) +{ + ua->dlg_cb = cb; +} + +/* + * Find dialog. + * This function is called for a new transactions, which a dialog hasn't been + * 'attached' to the transaction. + */ +static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata ) +{ + pjsip_dlg *dlg; + pj_str_t *tag; + + /* Non-CANCEL requests/response can be found by looking at the tag in the + * hash table. CANCEL requests don't have tags, so instead we'll try to + * find the UAS INVITE transaction in endpoint's hash table + */ + if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) { + + /* Create key for the rdata, but this time, use INVITE as the + * method. + */ + pj_str_t key; + pjsip_role_e role; + pjsip_method invite_method; + pjsip_transaction *invite_tsx; + + if (rdata->msg->type == PJSIP_REQUEST_MSG) { + role = PJSIP_ROLE_UAS; + } else { + role = PJSIP_ROLE_UAC; + } + pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD); + pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata); + + /* Lookup the INVITE transaction */ + invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key); + + /* We should find the dialog attached to the INVITE transaction */ + return invite_tsx ? + (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL; + + } else { + if (rdata->msg->type == PJSIP_REQUEST_MSG) { + tag = &rdata->to_tag; + } else { + tag = &rdata->from_tag; + } + /* Find the dialog in UA hash table */ + pj_mutex_lock(ua->mutex); + dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen ); + pj_mutex_unlock(ua->mutex); + } + + return dlg; +} + +/* + * This function receives event notification from transactions. It is called by + * endpoint. + */ +static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_user_agent *ua = mod->mod_data; + pjsip_dlg *dlg = NULL; + pjsip_transaction *tsx = event->obj.tsx; + + PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)", + (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type), + pjsip_event_str(event->src_type), event->src.data)); + + /* Special case to handle ACK which doesn't match any INVITE transactions. */ + if (event->type == PJSIP_EVENT_RX_ACK_MSG) { + /* Find the dialog based on the "tag". */ + dlg = find_dialog( ua, event->src.rdata ); + + /* We should be able to find it. */ + if (!dlg) { + PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK")); + return; + } + + /* Match CSeq with pending INVITE in dialog. */ + if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) { + /* A match found. */ + tsx = dlg->invite_tsx; + + /* Pass the event to transaction if transaction handles ACK. */ + if (tsx->handle_ack) { + PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction")); + pjsip_tsx_on_rx_msg(tsx, event->src.rdata); + return; + } + } else { + tsx = NULL; + PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK")); + return; + } + } + + /* For discard event, transaction is NULL. */ + if (tsx == NULL) { + return; + } + + /* Try to pickup the dlg from the transaction. */ + dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id]; + + if (dlg != NULL) { + + /* Nothing to do now. */ + + } else if (event->src_type == PJSIP_EVENT_RX_MSG) { + + /* This must be a new UAS transaction. */ + + /* Finds dlg that can handle this transaction. */ + dlg = find_dialog( ua, event->src.rdata); + + /* Create a new dlg if there's no existing dlg that can handle + the request, ONLY if the incoming message is an INVITE request. + */ + if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) { + + if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) { + /* Create new dialog. */ + dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS ); + + if (dlg == NULL || + pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0) + { + pjsip_tx_data *tdata; + + /* Dialog initialization has failed. Respond request with 500 */ + if (dlg) { + pjsip_ua_destroy_dialog(dlg); + } + tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, + PJSIP_SC_INTERNAL_SERVER_ERROR); + if (tdata) { + pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); + } + return; + } + + } else { + pjsip_tx_data *tdata; + + /* Check the method */ + switch (tsx->method.id) { + case PJSIP_INVITE_METHOD: + case PJSIP_ACK_METHOD: + case PJSIP_BYE_METHOD: + case PJSIP_CANCEL_METHOD: + /* Stale non-INVITE request. + * For now, respond all stale requests with 481 (?). + */ + tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST); + if (tdata) { + pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); + } + break; + } + + return; + } + } else { + /* Check the method */ + switch (tsx->method.id) { + case PJSIP_INVITE_METHOD: + case PJSIP_ACK_METHOD: + case PJSIP_BYE_METHOD: + case PJSIP_CANCEL_METHOD: + /* These methods belongs to dialog. + * If we receive these methods while no dialog is found, + * then it must be a stale responses. + */ + break; + default: + return; + } + + } + + if (dlg == NULL) { + PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d", + event->src.rdata, + pj_sockaddr_get_str_addr(&event->src.rdata->addr), + pj_sockaddr_get_port(&event->src.rdata->addr))); + } + + /* Set the dlg in the transaction (dlg can be NULL). */ + tsx->module_data[ua->mod_id] = dlg; + + } else { + /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG + * if UAS is responding to a transaction which does not exist. + * Just ignore. + */ + return; + } + + /* Pass the event to the dlg. */ + if (dlg) { + pjsip_dlg_on_tsx_event(dlg, tsx, event); + } +} + +/* + * Register dialog to UA. + */ +static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, + pj_str_t *key ) +{ + /* Assure that no entry with similar key exists in the hash table. */ + pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0); + + /* Insert entry to hash table. */ + pj_hash_set( dlg->pool, ua->dlg_table, + key->ptr, key->slen, dlg); + + /* Insert to the list. */ + pj_list_insert_before(&ua->dlg_list, dlg); + return PJ_SUCCESS; +} + +/* + * Create a new dialog. + */ +PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua, + pjsip_role_e role ) +{ + pj_pool_t *pool; + pjsip_dlg *dlg; + + PJ_UNUSED_ARG(ua) + + /* Create pool for the dialog. */ + pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p", + PJSIP_POOL_LEN_DIALOG, + PJSIP_POOL_INC_DIALOG); + + /* Create the dialog. */ + dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg)); + dlg->pool = pool; + dlg->ua = ua; + dlg->role = role; + sprintf(dlg->obj_name, "dlg%p", dlg); + + /* Create mutex for the dialog. */ + dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); + if (!dlg->mutex) { + pjsip_endpt_destroy_pool(ua->endpt, pool); + return NULL; + } + + /* Create unique tag for the dialog. */ + pj_create_unique_string( pool, &dlg->local.tag ); + + /* Register dialog. */ + pj_mutex_lock(ua->mutex); + if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) { + pj_mutex_unlock(ua->mutex); + pj_mutex_destroy(dlg->mutex); + pjsip_endpt_destroy_pool( ua->endpt, pool ); + return NULL; + } + pj_mutex_unlock(ua->mutex); + + PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role))); + return dlg; +} + +/* + * Destroy dialog. + */ +PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg ) +{ + PJ_LOG(5, (dlg->obj_name, "destroying..")); + + /* Lock dialog's mutex. + * Check the mutex validity first since this function can be called + * on dialog initialization failure (which might be because mutex could not + * be allocated in the first place). + */ + if (dlg->mutex) { + pj_mutex_lock(dlg->mutex); + } + + /* This must be called while holding dialog's mutex, if any. */ + pjsip_on_dialog_destroyed(dlg); + + /* Lock UA. */ + pj_mutex_lock(dlg->ua->mutex); + + /* Erase from hash table. */ + pj_hash_set( dlg->pool, dlg->ua->dlg_table, + dlg->local.tag.ptr, dlg->local.tag.slen, NULL); + + /* Erase from the list. */ + pj_list_erase(dlg); + + /* Unlock UA. */ + pj_mutex_unlock(dlg->ua->mutex); + + /* Unlock mutex. */ + if (dlg->mutex) { + pj_mutex_unlock(dlg->mutex); + } + + /* Destroy the pool. */ + pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool); +} + +/* + * Dump user agent state to log file. + */ +PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua) +{ +#if PJ_LOG_MAX_LEVEL >= 3 + PJ_LOG(3,(LOG_THIS, "Dumping user agent")); + PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u", + pj_pool_get_capacity(ua->pool), + pj_pool_get_used_size(ua->pool))); + PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table))); + + if (pj_hash_count(ua->dlg_table)) { + pjsip_dlg *dlg; + + PJ_LOG(3,(LOG_THIS, " Dumping dialog list:")); + dlg = ua->dlg_list.next; + while (dlg != (pjsip_dlg*) &ua->dlg_list) { + PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name, + pjsip_dlg_state_str(dlg->state))); + dlg = dlg->next; + } + } +#endif +} + diff --git a/pjsip/src/pjsip_mod_ua/sip_ua.h b/pjsip/src/pjsip_mod_ua/sip_ua.h index 51c50aea6..4ea6c2a22 100644 --- a/pjsip/src/pjsip_mod_ua/sip_ua.h +++ b/pjsip/src/pjsip_mod_ua/sip_ua.h @@ -1,80 +1,80 @@ -/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_ua.h 6 6/17/05 11:16p Bennylp $ */ -#ifndef __PJSIP_SIP_UA_H__ -#define __PJSIP_SIP_UA_H__ - -/** - * @file ua.h - * @brief SIP User Agent Library - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSUA SIP User Agent Stack - */ - -/** - * @defgroup PJSUA_UA SIP User Agent - * @ingroup PJSUA - * @{ - * \brief - * User Agent manages the interactions between application and SIP dialogs. - */ - -typedef struct pjsip_dlg_callback pjsip_dlg_callback; - -/** - * \brief This structure describes a User Agent instance. - */ -struct pjsip_user_agent -{ - pjsip_endpoint *endpt; - pj_pool_t *pool; - pj_mutex_t *mutex; - pj_uint32_t mod_id; - pj_hash_table_t *dlg_table; - pjsip_dlg_callback *dlg_cb; - pj_list dlg_list; -}; - -/** - * Create a new dialog. - */ -PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua, - pjsip_role_e role ); - - -/** - * Destroy dialog. - */ -PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg ); - - -/** - * Register callback to receive dialog notifications. - */ -PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, - pjsip_dlg_callback *cb ); - - -/** - * Get the module interface for the UA module. - */ -PJ_DECL(pjsip_module*) pjsip_ua_get_module(void); - - -/** - * Dump user agent state to log file. - */ -PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua ); - -/** - * @} - */ - -PJ_END_DECL - -#endif /* __PJSIP_UA_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_ua.h 6 6/17/05 11:16p Bennylp $ */ +#ifndef __PJSIP_SIP_UA_H__ +#define __PJSIP_SIP_UA_H__ + +/** + * @file ua.h + * @brief SIP User Agent Library + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSUA SIP User Agent Stack + */ + +/** + * @defgroup PJSUA_UA SIP User Agent + * @ingroup PJSUA + * @{ + * \brief + * User Agent manages the interactions between application and SIP dialogs. + */ + +typedef struct pjsip_dlg_callback pjsip_dlg_callback; + +/** + * \brief This structure describes a User Agent instance. + */ +struct pjsip_user_agent +{ + pjsip_endpoint *endpt; + pj_pool_t *pool; + pj_mutex_t *mutex; + pj_uint32_t mod_id; + pj_hash_table_t *dlg_table; + pjsip_dlg_callback *dlg_cb; + pj_list dlg_list; +}; + +/** + * Create a new dialog. + */ +PJ_DECL(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua, + pjsip_role_e role ); + + +/** + * Destroy dialog. + */ +PJ_DECL(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg ); + + +/** + * Register callback to receive dialog notifications. + */ +PJ_DECL(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, + pjsip_dlg_callback *cb ); + + +/** + * Get the module interface for the UA module. + */ +PJ_DECL(pjsip_module*) pjsip_ua_get_module(void); + + +/** + * Dump user agent state to log file. + */ +PJ_DECL(void) pjsip_ua_dump( pjsip_user_agent *ua ); + +/** + * @} + */ + +PJ_END_DECL + +#endif /* __PJSIP_UA_H__ */ + diff --git a/pjsip/src/pjsip_mod_ua/sip_ua_private.h b/pjsip/src/pjsip_mod_ua/sip_ua_private.h index 6b8b2deb0..f5264ba56 100644 --- a/pjsip/src/pjsip_mod_ua/sip_ua_private.h +++ b/pjsip/src/pjsip_mod_ua/sip_ua_private.h @@ -1,20 +1,20 @@ -/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_ua_private.h 3 3/25/05 12:51p Bennylp $ */ - -#ifndef __PJSIP_UA_PRIVATE_H__ -#define __PJSIP_UA_PRIVATE_H__ - - -/* - * Internal dialog functions. - */ -pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, - pjsip_rx_data *rdata ); - - -void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); - - -#endif /* __PJSIP_UA_PRIVATE_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip_mod_ua/sip_ua_private.h 3 3/25/05 12:51p Bennylp $ */ + +#ifndef __PJSIP_UA_PRIVATE_H__ +#define __PJSIP_UA_PRIVATE_H__ + + +/* + * Internal dialog functions. + */ +pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, + pjsip_rx_data *rdata ); + + +void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, + pjsip_transaction *tsx, + pjsip_event *event); + + +#endif /* __PJSIP_UA_PRIVATE_H__ */ + diff --git a/pjsip/src/pjsip_simple.h b/pjsip/src/pjsip_simple.h index 152718c97..14db78407 100644 --- a/pjsip/src/pjsip_simple.h +++ b/pjsip/src/pjsip_simple.h @@ -1,24 +1,24 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple.h 5 6/17/05 12:26a Bennylp $ */ - -/** - * @defgroup PJSIP_SIMPLE SIP Event, Instant Messaging and Presence Extension (SIMPLE) - */ - -/** - * @file pjsip_simple.h - * @brief SIP SIMPLE Extension - */ - -/* - * Include this header file to get all functionalities for SIMPLE extension - * (SIP for Instant Messaging and Presence Leveraging Extension). - */ -#ifndef __PJSIP_SIMPLE_H__ -#define __PJSIP_SIMPLE_H__ - -#include -#include -#include -#include - -#endif /* __PJSIP_SIMPLE_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip_simple.h 5 6/17/05 12:26a Bennylp $ */ + +/** + * @defgroup PJSIP_SIMPLE SIP Event, Instant Messaging and Presence Extension (SIMPLE) + */ + +/** + * @file pjsip_simple.h + * @brief SIP SIMPLE Extension + */ + +/* + * Include this header file to get all functionalities for SIMPLE extension + * (SIP for Instant Messaging and Presence Leveraging Extension). + */ +#ifndef __PJSIP_SIMPLE_H__ +#define __PJSIP_SIMPLE_H__ + +#include +#include +#include +#include + +#endif /* __PJSIP_SIMPLE_H__ */ diff --git a/pjsip/src/pjsip_simple/event_notify.c b/pjsip/src/pjsip_simple/event_notify.c index 6fd53d955..658ccde8a 100644 --- a/pjsip/src/pjsip_simple/event_notify.c +++ b/pjsip/src/pjsip_simple/event_notify.c @@ -1,1627 +1,1627 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify.c 11 8/31/05 9:05p Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define THIS_FILE "event_sub" - -/* String names for state. - * The names here should be compliant with sub_state names in RFC3265. - */ -static const pj_str_t state[] = { - { "null", 4 }, - { "active", 6 }, - { "pending", 7 }, - { "terminated", 10 }, - { "unknown", 7 } -}; - -/* Timer IDs */ -#define TIMER_ID_REFRESH 1 -#define TIMER_ID_UAS_EXPIRY 2 - -/* Static configuration. */ -#define SECONDS_BEFORE_EXPIRY 10 -#define MGR_POOL_SIZE 512 -#define MGR_POOL_INC 0 -#define SUB_POOL_SIZE 2048 -#define SUB_POOL_INC 0 -#define HASH_TABLE_SIZE 32 - -/* Static vars. */ -static int mod_id; -static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}}; -static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}}; - -typedef struct package -{ - PJ_DECL_LIST_MEMBER(struct package) - pj_str_t event; - int accept_cnt; - pj_str_t *accept; - pjsip_event_sub_pkg_cb cb; -} package; - -/* Event subscription manager singleton instance. */ -static struct pjsip_event_sub_mgr -{ - pj_pool_t *pool; - pj_hash_table_t *ht; - pjsip_endpoint *endpt; - pj_mutex_t *mutex; - pjsip_allow_events_hdr *allow_events; - package pkg_list; -} mgr; - -/* Fordward declarations for static functions. */ -static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t); -static pj_status_t mod_deinit(pjsip_module*); -static void tsx_handler(pjsip_module*, pjsip_event*); -static pjsip_event_sub *find_sub(pjsip_rx_data *); -static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*); -static void on_subscribe_response(void *, pjsip_event*); -static void on_notify_request(pjsip_transaction *, pjsip_rx_data*); -static void on_notify_response(void *, pjsip_event *); -static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*); -static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*); -static pj_status_t send_sub_refresh( pjsip_event_sub *sub ); - -/* Module descriptor. */ -static pjsip_module event_sub_module = -{ - {"EventSub", 8}, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - &mgr, /* User data. */ - 2, /* Number of methods supported . */ - { &SUBSCRIBE, &NOTIFY }, /* Array of methods */ - &mod_init, /* init_module() */ - NULL, /* start_module() */ - &mod_deinit, /* deinit_module() */ - &tsx_handler, /* tsx_handler() */ -}; - -/* - * Module initialization. - * This will be called by endpoint when it initializes all modules. - */ -static pj_status_t mod_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - pj_pool_t *pool; - - pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC); - if (!pool) - return -1; - - /* Manager initialization: create hash table and mutex. */ - mgr.pool = pool; - mgr.endpt = endpt; - mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE); - if (!mgr.ht) - return -1; - - mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE); - if (!mgr.mutex) - return -1; - - /* Attach manager to module. */ - mod->mod_data = &mgr; - - /* Init package list. */ - pj_list_init(&mgr.pkg_list); - - /* Init Allow-Events header. */ - mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool); - - /* Save the module ID. */ - mod_id = id; - - pjsip_event_notify_init_parser(); - return 0; -} - -/* - * Module deinitialization. - * Called by endpoint. - */ -static pj_status_t mod_deinit( struct pjsip_module *mod ) -{ - pj_mutex_lock(mgr.mutex); - pj_mutex_destroy(mgr.mutex); - pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool); - return 0; -} - -/* - * This public function is called by application to register callback. - * In exchange, the instance of the module is returned. - */ -PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void) -{ - return &event_sub_module; -} - -/* - * Register event package. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event, - int accept_cnt, - const pj_str_t accept[], - const pjsip_event_sub_pkg_cb *cb ) -{ - package *pkg; - int i; - - pj_mutex_lock(mgr.mutex); - - /* Create and register new package. */ - pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg)); - pj_strdup(mgr.pool, &pkg->event, event); - pj_list_insert_before(&mgr.pkg_list, pkg); - - /* Save Accept specification. */ - pkg->accept_cnt = accept_cnt; - pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t)); - for (i=0; iaccept[i], &accept[i]); - } - - /* Copy callback. */ - pj_memcpy(&pkg->cb, cb, sizeof(*cb)); - - /* Update Allow-Events header. */ - pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS); - mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event; - - pj_mutex_unlock(mgr.mutex); - return 0; -} - -/* - * Create subscription key (for hash table). - */ -static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool, - pjsip_role_e role, - const pj_str_t *call_id, const pj_str_t *from_tag) -{ - char *p; - - p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3); - *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C'); - *p++ = '$'; - pj_memcpy(p, call_id->ptr, call_id->slen); - p += call_id->slen; - *p++ = '$'; - pj_memcpy(p, from_tag->ptr, from_tag->slen); - p += from_tag->slen; - - key->slen = p - key->ptr; -} - - -/* - * Create UAC subscription. - */ -PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, - const pj_str_t *from, - const pj_str_t *to, - const pj_str_t *event, - int expires, - int accept_cnt, - const pj_str_t accept[], - void *user_data, - const pjsip_event_sub_cb *cb) -{ - pjsip_tx_data *tdata; - pj_pool_t *pool; - const pjsip_hdr *hdr; - pjsip_event_sub *sub; - PJ_USE_EXCEPTION; - - PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s", - event->slen, event->ptr, to->slen, to->ptr)); - - /* Create pool for the event subscription. */ - pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC); - if (!pool) { - return NULL; - } - - /* Init subscription. */ - sub = pj_pool_calloc(pool, 1, sizeof(*sub)); - sub->pool = pool; - sub->endpt = endpt; - sub->role = PJSIP_ROLE_UAC; - sub->state = PJSIP_EVENT_SUB_STATE_PENDING; - sub->state_str = state[sub->state]; - sub->user_data = user_data; - sub->timer.id = 0; - sub->default_interval = expires; - pj_memcpy(&sub->cb, cb, sizeof(*cb)); - pj_list_init(&sub->auth_sess); - pj_list_init(&sub->route_set); - sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); - if (!sub->mutex) { - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - - /* The easiest way to parse the parameters is to create a dummy request! */ - tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from, - NULL, -1, NULL); - if (!tdata) { - pj_mutex_destroy(sub->mutex); - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - - /* - * Duplicate headers in the request to our structure. - */ - PJ_TRY { - int i; - - /* From */ - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); - pj_assert(hdr != NULL); - sub->from = pjsip_hdr_clone(pool, hdr); - - /* To */ - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); - pj_assert(hdr != NULL); - sub->to = pjsip_hdr_clone(pool, hdr); - - /* Contact. */ - sub->contact = pjsip_contact_hdr_create(pool); - sub->contact->uri = sub->from->uri; - - /* Call-ID */ - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL); - pj_assert(hdr != NULL); - sub->call_id = pjsip_hdr_clone(pool, hdr); - - /* CSeq */ - sub->cseq = pj_rand() % 0xFFFF; - - /* Event. */ - sub->event = pjsip_event_hdr_create(sub->pool); - pj_strdup(pool, &sub->event->event_type, event); - - /* Expires. */ - sub->uac_expires = pjsip_expires_hdr_create(pool); - sub->uac_expires->ivalue = expires; - - /* Accept. */ - sub->local_accept = pjsip_accept_hdr_create(pool); - for (i=0; ilocal_accept->count++; - pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]); - } - - /* Register to hash table. */ - create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC, - &sub->call_id->id, &sub->from->tag); - pj_mutex_lock( mgr.mutex ); - pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); - pj_mutex_unlock( mgr.mutex ); - - } - PJ_DEFAULT { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init", - sub, state[sub->state].ptr, PJ_GET_EXCEPTION())); - - pjsip_tx_data_dec_ref(tdata); - pj_mutex_destroy(sub->mutex); - pjsip_endpt_destroy_pool(endpt, sub->pool); - return NULL; - } - PJ_END; - - /* All set, delete temporary transmit data as we don't need it. */ - pjsip_tx_data_dec_ref(tdata); - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s", - sub, state[sub->state].ptr, - to->slen, to->ptr, event->slen, event->ptr)); - - return sub; -} - -/* - * Set credentials. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, - int count, - const pjsip_cred_info cred[]) -{ - pj_mutex_lock(sub->mutex); - if (count > 0) { - sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info)); - pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info)); - } - sub->cred_cnt = count; - pj_mutex_unlock(sub->mutex); - return 0; -} - -/* - * Set route-set. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, - const pjsip_route_hdr *route_set ) -{ - const pjsip_route_hdr *hdr; - - pj_mutex_lock(sub->mutex); - - /* Clear existing route set. */ - pj_list_init(&sub->route_set); - - /* Duplicate route headers. */ - hdr = route_set->next; - while (hdr != route_set) { - pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr); - pj_list_insert_before(&sub->route_set, new_hdr); - hdr = hdr->next; - } - - pj_mutex_unlock(sub->mutex); - - return 0; -} - -/* - * Send subscribe request. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ) -{ - pj_status_t status; - - pj_mutex_lock(sub->mutex); - status = send_sub_refresh(sub); - pj_mutex_unlock(sub->mutex); - - return status; -} - -/* - * Destroy subscription. - * If there are pending transactions, then this will just set the flag. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub) -{ - pj_assert(sub != NULL); - if (sub == NULL) - return -1; - - /* Application must terminate the subscription first. */ - pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL || - sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed", - sub, state[sub->state].ptr)); - - pj_mutex_lock(mgr.mutex); - pj_mutex_lock(sub->mutex); - - /* Set delete flag. */ - sub->delete_flag = 1; - - /* Unregister timer, if any. */ - if (sub->timer.id != 0) { - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - sub->timer.id = 0; - } - - if (sub->pending_tsx > 0) { - pj_mutex_unlock(sub->mutex); - pj_mutex_unlock(mgr.mutex); - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later", - sub, state[sub->state].ptr, - sub->pending_tsx)); - return 1; - } - - /* Unregister from hash table. */ - pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL); - - /* Destroy. */ - pj_mutex_destroy(sub->mutex); - pjsip_endpt_destroy_pool(sub->endpt, sub->pool); - - pj_mutex_unlock(mgr.mutex); - - PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub)); - return 0; -} - -/* Change state. */ -static void sub_set_state( pjsip_event_sub *sub, int new_state) -{ - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s", - sub, state[sub->state].ptr, state[new_state].ptr)); - sub->state = new_state; - sub->state_str = state[new_state]; -} - -/* - * Refresh subscription. - */ -static pj_status_t send_sub_refresh( pjsip_event_sub *sub ) -{ - pjsip_tx_data *tdata; - pj_status_t status; - const pjsip_route_hdr *route; - - pj_assert(sub->role == PJSIP_ROLE_UAC); - pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED); - if (sub->role != PJSIP_ROLE_UAC || - sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) - { - return -1; - } - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription", - sub, state[sub->state].ptr)); - - /* Create request. */ - tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, - &SUBSCRIBE, - sub->to->uri, - sub->from, sub->to, - sub->contact, sub->call_id, - sub->cseq++, - NULL); - - if (!tdata) { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!", - sub, state[sub->state].ptr)); - return -1; - } - - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, sub->event)); - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept)); - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); - - /* Authentication */ - pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, - sub->cred_cnt, sub->cred_info); - - /* Route set. */ - route = sub->route_set.next; - while (route != &sub->route_set) { - pj_list_insert_before( &tdata->msg->hdr, - pjsip_hdr_shallow_clone(tdata->pool, route)); - route = route->next; - } - - /* Send */ - status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, - &on_subscribe_response); - if (status == 0) { - sub->pending_tsx++; - } else { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!", - sub, state[sub->state].ptr)); - } - - return status; -} - -/* - * Stop subscription. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ) -{ - pjsip_tx_data *tdata; - const pjsip_route_hdr *route; - pj_status_t status; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...", - sub, state[sub->state].ptr)); - - /* Lock subscription. */ - pj_mutex_lock(sub->mutex); - - pj_assert(sub->role == PJSIP_ROLE_UAC); - - /* Kill refresh timer, if any. */ - if (sub->timer.id != 0) { - sub->timer.id = 0; - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - } - - /* Create request. */ - tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, - &SUBSCRIBE, - sub->to->uri, - sub->from, sub->to, - sub->contact, sub->call_id, - sub->cseq++, - NULL); - - if (!tdata) { - pj_mutex_unlock(sub->mutex); - return -1; - } - - /* Add headers to request. */ - pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); - sub->uac_expires->ivalue = 0; - pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); - - /* Add authentication. */ - pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, - sub->cred_cnt, sub->cred_info); - - - /* Route set. */ - route = sub->route_set.next; - while (route != &sub->route_set) { - pj_list_insert_before( &tdata->msg->hdr, - pjsip_hdr_shallow_clone(tdata->pool, route)); - route = route->next; - } - - /* Prevent timer from refreshing itself. */ - sub->default_interval = 0; - - /* Set state. */ - sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED ); - - /* Send the request. */ - status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, - &on_subscribe_response); - if (status == 0) { - sub->pending_tsx++; - } - - pj_mutex_unlock(sub->mutex); - - if (status != 0) { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!", - sub, state[sub->state].ptr)); - } - - return status; -} - -/* - * Send notify. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub, - pjsip_event_sub_state new_state, - const pj_str_t *reason, - pjsip_msg_body *body) -{ - pjsip_tx_data *tdata; - pjsip_sub_state_hdr *ss_hdr; - const pjsip_route_hdr *route; - pj_time_val now; - pj_status_t status; - pjsip_event_sub_state old_state = sub->state; - - pj_gettimeofday(&now); - - pj_assert(sub->role == PJSIP_ROLE_UAS); - if (sub->role != PJSIP_ROLE_UAS) - return -1; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY", - sub, state[new_state].ptr)); - - /* Lock subscription. */ - pj_mutex_lock(sub->mutex); - - /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */ - if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) { - pj_assert(0); - pj_mutex_unlock(sub->mutex); - return -1; - } - - /* Update state no matter what. */ - sub_set_state(sub, new_state); - - /* Create transmit data. */ - tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, - &NOTIFY, - sub->to->uri, - sub->from, sub->to, - sub->contact, sub->call_id, - sub->cseq++, - NULL); - if (!tdata) { - pj_mutex_unlock(sub->mutex); - return -1; - } - - /* Add Event header. */ - pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); - - /* Add Subscription-State header. */ - ss_hdr = pjsip_sub_state_hdr_create(tdata->pool); - ss_hdr->sub_state = state[new_state]; - ss_hdr->expires_param = sub->expiry_time.sec - now.sec; - if (ss_hdr->expires_param < 0) - ss_hdr->expires_param = 0; - if (reason) - pj_strdup(tdata->pool, &ss_hdr->reason_param, reason); - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr); - - /* Add Allow-Events header. */ - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); - - /* Add authentication */ - pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, - sub->cred_cnt, sub->cred_info); - - /* Route set. */ - route = sub->route_set.next; - while (route != &sub->route_set) { - pj_list_insert_before( &tdata->msg->hdr, - pjsip_hdr_shallow_clone(tdata->pool, route)); - route = route->next; - } - - /* Attach body. */ - tdata->msg->body = body; - - /* That's it, send! */ - status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response); - if (status == 0) - sub->pending_tsx++; - - /* If terminated notify application. */ - if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { - if (sub->cb.on_sub_terminated) { - sub->pending_tsx++; - (*sub->cb.on_sub_terminated)(sub, reason); - sub->pending_tsx--; - } - } - - /* Unlock subscription. */ - pj_mutex_unlock(sub->mutex); - - if (status != 0) { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY", - sub, state[sub->state].ptr)); - } - - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } - return status; -} - - -/* If this timer callback is called, it means subscriber hasn't refreshed its - * subscription on-time. Set the state to terminated. This will also send - * NOTIFY with Subscription-State set to terminated. - */ -static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) -{ - pjsip_event_sub *sub = entry->user_data; - pj_str_t reason = { "timeout", 7 }; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!", - sub, state[sub->state].ptr)); - - pj_mutex_lock(sub->mutex); - sub->timer.id = 0; - - if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) { - /* Notify application, but prevent app from destroying the sub. */ - ++sub->pending_tsx; - (*sub->cb.on_sub_terminated)(sub, &reason); - --sub->pending_tsx; - } - //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED, - // &reason, NULL); - pj_mutex_unlock(sub->mutex); - -} - -/* Schedule notifier expiration. */ -static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay) -{ - pj_time_val delay = { 0, 0 }; - pj_parsed_time pt; - - if (sub->timer.id != 0) - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - - pj_gettimeofday(&sub->expiry_time); - sub->expiry_time.sec += sec_delay; - - sub->timer.id = TIMER_ID_UAS_EXPIRY; - sub->timer.user_data = sub; - sub->timer.cb = &uas_expire_timer_cb; - delay.sec = sec_delay; - pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay); - - pj_time_decode(&sub->expiry_time, &pt); - PJ_LOG(4,(THIS_FILE, - "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)", - sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay)); -} - -/* This timer is called for UAC to refresh the subscription. */ -static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) -{ - pjsip_event_sub *sub = entry->user_data; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer", - sub, state[sub->state].ptr)); - - pj_mutex_lock(sub->mutex); - sub->timer.id = 0; - send_sub_refresh(sub); - pj_mutex_unlock(sub->mutex); -} - - -/* This will update the UAC's refresh schedule. */ -static void update_next_refresh(pjsip_event_sub *sub, int interval) -{ - pj_time_val delay = {0, 0}; - pj_parsed_time pt; - - if (interval < SECONDS_BEFORE_EXPIRY) { - PJ_LOG(4,(THIS_FILE, - "event_sub%p (%s): expiration delay too short (%d sec)! updated.", - sub, state[sub->state].ptr, interval)); - interval = SECONDS_BEFORE_EXPIRY; - } - - if (sub->timer.id != 0) - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - - sub->timer.id = TIMER_ID_REFRESH; - sub->timer.user_data = sub; - sub->timer.cb = &refresh_timer_cb; - pj_gettimeofday(&sub->expiry_time); - delay.sec = interval - SECONDS_BEFORE_EXPIRY; - sub->expiry_time.sec += delay.sec; - - pj_time_decode(&sub->expiry_time, &pt); - PJ_LOG(4,(THIS_FILE, - "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)", - sub, state[sub->state].ptr, - pt.hour, pt.min, pt.sec, - delay.sec)); - - pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay ); -} - - -/* Find subscription in the hash table. - * If found, lock the subscription before returning to caller. - */ -static pjsip_event_sub *find_sub(pjsip_rx_data *rdata) -{ - pj_str_t key; - pjsip_role_e role; - pjsip_event_sub *sub; - pjsip_method *method = &rdata->msg->line.req.method; - pj_str_t *tag; - - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - if (pjsip_method_cmp(method, &SUBSCRIBE)==0) { - role = PJSIP_ROLE_UAS; - tag = &rdata->to_tag; - } else { - pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); - role = PJSIP_ROLE_UAC; - tag = &rdata->to_tag; - } - } else { - if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) { - role = PJSIP_ROLE_UAC; - tag = &rdata->from_tag; - } else { - pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); - role = PJSIP_ROLE_UAS; - tag = &rdata->from_tag; - } - } - create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag); - - pj_mutex_lock(mgr.mutex); - sub = pj_hash_get(mgr.ht, key.ptr, key.slen); - if (sub) - pj_mutex_lock(sub->mutex); - pj_mutex_unlock(mgr.mutex); - - return sub; -} - - -/* This function is called when we receive SUBSCRIBE request message - * to refresh existing subscription. - */ -static void on_received_sub_refresh( pjsip_event_sub *sub, - pjsip_transaction *tsx, pjsip_rx_data *rdata) -{ - pjsip_event_hdr *e; - pjsip_expires_hdr *expires; - pj_str_t hname; - int status = 200; - pj_str_t reason_phrase = { NULL, 0 }; - int new_state = sub->state; - int old_state = sub->state; - int new_interval = 0; - pjsip_tx_data *tdata; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", - sub, state[sub->state].ptr)); - - /* Check that the event matches. */ - hname = pj_str("Event"); - e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); - if (!e) { - status = 400; - reason_phrase = pj_str("Missing Event header"); - goto send_response; - } - if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 || - pj_stricmp(&e->id_param, &sub->event->id_param) != 0) - { - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - goto send_response; - } - - /* Check server state. */ - if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) { - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - goto send_response; - } - - /* Check expires header. */ - expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL); - if (!expires) { - /* - status = 400; - reason_phrase = pj_str("Missing Expires header"); - goto send_response; - */ - new_interval = sub->default_interval; - } else { - /* Check that interval is not too short. - * Note that expires time may be zero (for unsubscription). - */ - new_interval = expires->ivalue; - if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) { - status = PJSIP_SC_INTERVAL_TOO_BRIEF; - goto send_response; - } - } - - /* Update interval. */ - sub->default_interval = new_interval; - pj_gettimeofday(&sub->expiry_time); - sub->expiry_time.sec += new_interval; - - /* Update timer only if this is not unsubscription. */ - if (new_interval > 0) { - sub->default_interval = new_interval; - sub_schedule_uas_expire( sub, new_interval ); - - /* Call callback. */ - if (sub->cb.on_received_refresh) { - sub->pending_tsx++; - (*sub->cb.on_received_refresh)(sub, rdata); - sub->pending_tsx--; - } - } - -send_response: - tdata = pjsip_endpt_create_response( sub->endpt, rdata, status); - if (tdata) { - if (reason_phrase.slen) - tdata->msg->line.status.reason = reason_phrase; - - /* Add Expires header. */ - expires = pjsip_expires_hdr_create(tdata->pool); - expires->ivalue = sub->default_interval; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires); - - if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { - pjsip_msg_add_hdr(tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); - } - /* Send down to transaction. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - } - - if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) { - /* Notify application if sub is terminated. */ - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - sub_set_state(sub, new_state); - if (new_state!=old_state && sub->cb.on_sub_terminated) { - pj_str_t reason = {"", 0}; - if (reason_phrase.slen) reason = reason_phrase; - else reason = *pjsip_get_status_text(status); - - sub->pending_tsx++; - (*sub->cb.on_sub_terminated)(sub, &reason); - sub->pending_tsx--; - } - } - - pj_mutex_unlock(sub->mutex); - - /* Prefer to call log when we're not holding the mutex. */ - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", - sub, state[sub->state].ptr, - (tdata ? tdata->obj_name : "null"), status)); - - /* Check if application has requested deletion. */ - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } - -} - - -/* This function is called when we receive SUBSCRIBE request message for - * a new subscription. - */ -static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata ) -{ - package *pkg; - pj_pool_t *pool; - pjsip_event_sub *sub = NULL; - pj_str_t hname; - int status = 200; - pj_str_t reason = { NULL, 0 }; - pjsip_tx_data *tdata; - pjsip_expires_hdr *expires; - pjsip_accept_hdr *accept; - pjsip_event_hdr *evhdr; - - /* Get the Event header. */ - hname = pj_str("Event"); - evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); - if (!evhdr) { - status = 400; - reason = pj_str("No Event header in request"); - goto send_response; - } - - /* Find corresponding package. - * We don't lock the manager's mutex since we assume the package list - * won't change once the application is running! - */ - pkg = mgr.pkg_list.next; - while (pkg != &mgr.pkg_list) { - if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0) - break; - pkg = pkg->next; - } - - if (pkg == &mgr.pkg_list) { - /* Event type is not supported by any packages! */ - status = 489; - reason = pj_str("Bad Event"); - goto send_response; - } - - /* First check that the Accept specification matches the - * package's Accept types. - */ - accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); - if (accept) { - unsigned i; - pj_str_t *content_type = NULL; - - for (i=0; icount && !content_type; ++i) { - int j; - for (j=0; jaccept_cnt; ++j) { - if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) { - content_type = &pkg->accept[j]; - break; - } - } - } - - if (!content_type) { - status = PJSIP_SC_NOT_ACCEPTABLE_HERE; - goto send_response; - } - } - - /* Check whether the package wants to accept the subscription. */ - pj_assert(pkg->cb.on_query_subscribe != NULL); - (*pkg->cb.on_query_subscribe)(rdata, &status); - if (!PJSIP_IS_STATUS_IN_CLASS(status,200)) - goto send_response; - - /* Create new subscription record. */ - pool = pjsip_endpt_create_pool(tsx->endpt, "esub", - SUB_POOL_SIZE, SUB_POOL_INC); - if (!pool) { - status = 500; - goto send_response; - } - sub = pj_pool_calloc(pool, 1, sizeof(*sub)); - sub->pool = pool; - sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); - if (!sub->mutex) { - status = 500; - goto send_response; - } - - PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub)); - - /* Start locking mutex. */ - pj_mutex_lock(sub->mutex); - - /* Init UAS subscription */ - sub->endpt = tsx->endpt; - sub->role = PJSIP_ROLE_UAS; - sub->state = PJSIP_EVENT_SUB_STATE_PENDING; - sub->state_str = state[sub->state]; - pj_list_init(&sub->auth_sess); - pj_list_init(&sub->route_set); - sub->from = pjsip_hdr_clone(pool, rdata->to); - pjsip_fromto_set_from(sub->from); - if (sub->from->tag.slen == 0) { - pj_create_unique_string(pool, &sub->from->tag); - rdata->to->tag = sub->from->tag; - } - sub->to = pjsip_hdr_clone(pool, rdata->from); - pjsip_fromto_set_to(sub->to); - sub->contact = pjsip_contact_hdr_create(pool); - sub->contact->uri = sub->from->uri; - sub->call_id = pjsip_cid_hdr_create(pool); - pj_strdup(pool, &sub->call_id->id, &rdata->call_id); - sub->cseq = pj_rand() % 0xFFFF; - - expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL); - if (expires) { - sub->default_interval = expires->ivalue; - if (sub->default_interval > 0 && - sub->default_interval < SECONDS_BEFORE_EXPIRY) - { - status = 423; /* Interval too short. */ - goto send_response; - } - } else { - sub->default_interval = 600; - } - - /* Clone Event header. */ - sub->event = pjsip_hdr_clone(pool, evhdr); - - /* Register to hash table. */ - create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id, - &sub->from->tag); - pj_mutex_lock(mgr.mutex); - pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); - pj_mutex_unlock(mgr.mutex); - - /* Set timer where subscription will expire only when expires<>0. - * Subscriber may send new subscription with expires==0. - */ - if (sub->default_interval != 0) { - sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY); - } - - /* Notify application. */ - if (pkg->cb.on_subscribe) { - pjsip_event_sub_cb *cb = NULL; - sub->pending_tsx++; - (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval); - sub->pending_tsx--; - if (cb == NULL) - pj_memset(&sub->cb, 0, sizeof(*cb)); - else - pj_memcpy(&sub->cb, cb, sizeof(*cb)); - } - - -send_response: - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", - sub, state[sub->state].ptr, status)); - - tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status); - if (tdata) { - if (reason.slen) { - /* Customize reason text. */ - tdata->msg->line.status.reason = reason; - } - if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { - /* Add Expires header. */ - pjsip_expires_hdr *hdr; - - hdr = pjsip_expires_hdr_create(tdata->pool); - hdr->ivalue = sub->default_interval; - pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr ); - } - if (status == 423) { - /* Add Min-Expires header. */ - pjsip_min_expires_hdr *hdr; - - hdr = pjsip_min_expires_hdr_create(tdata->pool); - hdr->ivalue = SECONDS_BEFORE_EXPIRY; - pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr); - } - if (status == 489 || - status==PJSIP_SC_NOT_ACCEPTABLE_HERE || - PJSIP_IS_STATUS_IN_CLASS(status,200)) - { - /* Add Allow-Events header. */ - pjsip_hdr *hdr; - hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); - pjsip_msg_add_hdr(tdata->msg, hdr); - - /* Should add Accept header?. */ - } - - pjsip_tsx_on_tx_msg(tsx, tdata); - } - - /* If received new subscription with expires=0, terminate. */ - if (sub && sub->default_interval == 0) { - pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); - if (sub->cb.on_sub_terminated) { - pj_str_t reason = { "timeout", 7 }; - (*sub->cb.on_sub_terminated)(sub, &reason); - } - } - - if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) { - if (sub && sub->mutex) { - pjsip_event_sub_destroy(sub); - } else if (sub) { - pjsip_endpt_destroy_pool(tsx->endpt, sub->pool); - } - } else { - pj_assert(status >= 200); - pj_mutex_unlock(sub->mutex); - } -} - -/* This is the main callback when SUBSCRIBE request is received. */ -static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) -{ - pjsip_event_sub *sub = find_sub(rdata); - - if (sub) - on_received_sub_refresh(sub, tsx, rdata); - else - on_new_subscription(tsx, rdata); -} - - -/* This callback is called when response to SUBSCRIBE is received. */ -static void on_subscribe_response(void *token, pjsip_event *event) -{ - pjsip_event_sub *sub = token; - pjsip_transaction *tsx = event->obj.tsx; - int new_state, old_state = sub->state; - - pj_assert(tsx->status_code >= 200); - if (tsx->status_code < 200) - return; - - pj_assert(sub->role == PJSIP_ROLE_UAC); - - /* Lock mutex. */ - pj_mutex_lock(sub->mutex); - - /* If request failed with 401/407 error, silently retry the request. */ - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req(sub->endpt, - sub->pool, &sub->auth_sess, - sub->cred_cnt, sub->cred_info, - tsx->last_tx, event->src.rdata ); - if (tdata) { - int status; - pjsip_cseq_hdr *cseq; - cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); - cseq->cseq = sub->cseq++; - status = pjsip_endpt_send_request( sub->endpt, tdata, - -1, sub, - &on_subscribe_response); - if (status == 0) { - pj_mutex_unlock(sub->mutex); - return; - } - } - } - - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { - /* Update To tag. */ - if (sub->to->tag.slen == 0) - pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag); - - new_state = sub->state; - - } else if (tsx->status_code == 481) { - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - - } else if (tsx->status_code >= 300) { - /* RFC 3265 Section 3.1.4.2: - * If a SUBSCRIBE request to refresh a subscription fails - * with a non-481 response, the original subscription is still - * considered valid for the duration of original exires. - * - * Note: - * Since we normally send SUBSCRIBE for refreshing the subscription, - * it means the subscription already expired anyway. So we terminate - * the subscription now. - */ - if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) { - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } else { - /* Use this to be compliant with Section 3.1.4.2 - new_state = sub->state; - */ - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } - } else { - pj_assert(0); - new_state = sub->state; - } - - if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) { - sub_set_state(sub, new_state); - } - - if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE || - sub->state == PJSIP_EVENT_SUB_STATE_PENDING) - { - /* - * Register timer for next subscription refresh, but only when - * we're not unsubscribing. Also update default_interval and Expires - * header. - */ - if (sub->default_interval > 0 && !sub->delete_flag) { - pjsip_expires_hdr *exp = NULL; - - /* Could be transaction timeout. */ - if (event->src_type == PJSIP_EVENT_RX_MSG) { - exp = pjsip_msg_find_hdr(event->src.rdata->msg, - PJSIP_H_EXPIRES, NULL); - } - - if (exp) { - int delay = exp->ivalue; - if (delay > 0) { - pj_time_val new_expiry; - pj_gettimeofday(&new_expiry); - new_expiry.sec += delay; - if (sub->timer.id==0 || - new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) - { - //if (delay > 0 && delay < sub->default_interval) { - sub->default_interval = delay; - sub->uac_expires->ivalue = delay; - update_next_refresh(sub, delay); - } - } - } - } - } - - /* Call callback. */ - if (!sub->delete_flag) { - if (sub->cb.on_received_sub_response) { - (*sub->cb.on_received_sub_response)(sub, event); - } - } - - /* Notify application if we're terminated. */ - if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { - if (sub->cb.on_sub_terminated) { - pj_str_t reason; - if (event->src_type == PJSIP_EVENT_RX_MSG) - reason = event->src.rdata->msg->line.status.reason; - else - reason = *pjsip_get_status_text(tsx->status_code); - - (*sub->cb.on_sub_terminated)(sub, &reason); - } - } - - /* Decrement pending tsx count. */ - --sub->pending_tsx; - pj_assert(sub->pending_tsx >= 0); - - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } else { - pj_mutex_unlock(sub->mutex); - } - - /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */ -} - -/* - * This callback called when we receive incoming NOTIFY request. - */ -static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) -{ - pjsip_event_sub *sub; - pjsip_tx_data *tdata; - int status = 200; - int old_state; - pj_str_t reason = { NULL, 0 }; - pj_str_t reason_phrase = { NULL, 0 }; - int new_state = PJSIP_EVENT_SUB_STATE_NULL; - - /* Find subscription based on Call-ID and From tag. - * This will also automatically lock the subscription, if it's found. - */ - sub = find_sub(rdata); - if (!sub) { - /* RFC 3265: Section 3.2 Description of NOTIFY Behavior: - * Answer with 481 Subscription does not exist. - */ - PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!")); - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - - } else { - pj_assert(sub->role == PJSIP_ROLE_UAC); - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", - sub, state[sub->state].ptr)); - - } - - new_state = old_state = sub->state; - - /* RFC 3265: Section 3.2.1 - * Check that the Event header match the subscription. - */ - if (status == 200) { - pjsip_event_hdr *hdr; - pj_str_t hname = { "Event", 5 }; - - hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); - if (!hdr) { - status = PJSIP_SC_BAD_REQUEST; - reason_phrase = pj_str("No Event header found"); - } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 || - pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) - { - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - } - } - - /* Update subscription state and timer. */ - if (status == 200) { - pjsip_sub_state_hdr *hdr; - const pj_str_t hname = { "Subscription-State", 18 }; - const pj_str_t state_active = { "active", 6 }, - state_pending = { "pending", 7}, - state_terminated = { "terminated", 10 }; - - hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); - if (!hdr) { - status = PJSIP_SC_BAD_REQUEST; - reason_phrase = pj_str("No Subscription-State header found"); - goto process; - } - - /* - * Update subscription state. - */ - if (pj_stricmp(&hdr->sub_state, &state_active) == 0) { - if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) - new_state = PJSIP_EVENT_SUB_STATE_ACTIVE; - } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) { - if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) - new_state = PJSIP_EVENT_SUB_STATE_PENDING; - } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) { - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } else { - new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN; - } - - reason = hdr->reason_param; - - if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL && - sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) - { - sub_set_state(sub, new_state); - if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) { - pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state); - } else { - sub->state_str = state[new_state]; - } - } - - /* - * Update timeout timer in required, just in case notifier changed the - * expiration to shorter time. - * Section 3.2.2: the expires param can only shorten the interval. - */ - if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || - sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) - { - pj_time_val now, new_expiry; - - pj_gettimeofday(&now); - new_expiry.sec = now.sec + hdr->expires_param; - if (sub->timer.id==0 || - new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) - { - update_next_refresh(sub, hdr->expires_param); - } - } - } - -process: - /* Note: here we sub MAY BE NULL! */ - - /* Send response to NOTIFY */ - tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status ); - if (tdata) { - if (reason_phrase.slen) - tdata->msg->line.status.reason = reason_phrase; - - if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { - pjsip_hdr *hdr; - hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); - pjsip_msg_add_hdr( tdata->msg, hdr); - } - - pjsip_tsx_on_tx_msg(tsx, tdata); - } - - /* Call NOTIFY callback, if any. */ - if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) { - sub->pending_tsx++; - (*sub->cb.on_received_notify)(sub, rdata); - sub->pending_tsx--; - } - - /* Check if subscription is terminated and call callback. */ - if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { - if (sub->cb.on_sub_terminated) { - sub->pending_tsx++; - (*sub->cb.on_sub_terminated)(sub, &reason); - sub->pending_tsx--; - } - } - - /* Check if application has requested deletion. */ - if (sub && sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } else if (sub) { - pj_mutex_unlock(sub->mutex); - } -} - -/* This callback is called when we received NOTIFY response. */ -static void on_notify_response(void *token, pjsip_event *event) -{ - pjsip_event_sub *sub = token; - pjsip_event_sub_state old_state = sub->state; - pjsip_transaction *tsx = event->obj.tsx; - - /* Lock the subscription. */ - pj_mutex_lock(sub->mutex); - - pj_assert(sub->role == PJSIP_ROLE_UAS); - - /* If request failed with authorization failure, silently retry. */ - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req(sub->endpt, - sub->pool, &sub->auth_sess, - sub->cred_cnt, sub->cred_info, - tsx->last_tx, event->src.rdata ); - if (tdata) { - int status; - pjsip_cseq_hdr *cseq; - cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); - cseq->cseq = sub->cseq++; - status = pjsip_endpt_send_request( sub->endpt, tdata, - -1, sub, - &on_notify_response); - if (status == 0) { - pj_mutex_unlock(sub->mutex); - return; - } - } - } - - /* Notify application. */ - if (sub->cb.on_received_notify_response) - (*sub->cb.on_received_notify_response)(sub, event); - - /* Check for response 481. */ - if (event->obj.tsx->status_code == 481) { - /* Remote says that the subscription does not exist! - * Terminate subscription! - */ - sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED); - if (sub->timer.id) { - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - sub->timer.id = 0; - } - - PJ_LOG(4, (THIS_FILE, - "event_sub%p (%s): got 481 response to NOTIFY. Terminating...", - sub, state[sub->state].ptr)); - - /* Notify app. */ - if (sub->state!=old_state && sub->cb.on_sub_terminated) - (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason); - } - - /* Decrement pending transaction count. */ - --sub->pending_tsx; - pj_assert(sub->pending_tsx >= 0); - - /* Check that the subscription is marked for deletion. */ - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } else { - pj_mutex_unlock(sub->mutex); - } - - /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */ -} - - -/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY - * requests. - */ -static void tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_msg *msg; - pjsip_rx_data *rdata; - - /* Only want incoming message events. */ - if (event->src_type != PJSIP_EVENT_RX_MSG) - return; - - rdata = event->src.rdata; - msg = rdata->msg; - - /* Only want to process request messages. */ - if (msg->type != PJSIP_REQUEST_MSG) - return; - - /* Only want the first notification. */ - if (event->obj.tsx && event->obj.tsx->status_code >= 100) - return; - - if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) { - /* Process incoming SUBSCRIBE request. */ - on_subscribe_request( event->obj.tsx, rdata ); - } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) { - /* Process incoming NOTIFY request. */ - on_notify_request( event->obj.tsx, rdata ); - } -} - +/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify.c 11 8/31/05 9:05p Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "event_sub" + +/* String names for state. + * The names here should be compliant with sub_state names in RFC3265. + */ +static const pj_str_t state[] = { + { "null", 4 }, + { "active", 6 }, + { "pending", 7 }, + { "terminated", 10 }, + { "unknown", 7 } +}; + +/* Timer IDs */ +#define TIMER_ID_REFRESH 1 +#define TIMER_ID_UAS_EXPIRY 2 + +/* Static configuration. */ +#define SECONDS_BEFORE_EXPIRY 10 +#define MGR_POOL_SIZE 512 +#define MGR_POOL_INC 0 +#define SUB_POOL_SIZE 2048 +#define SUB_POOL_INC 0 +#define HASH_TABLE_SIZE 32 + +/* Static vars. */ +static int mod_id; +static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}}; +static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}}; + +typedef struct package +{ + PJ_DECL_LIST_MEMBER(struct package) + pj_str_t event; + int accept_cnt; + pj_str_t *accept; + pjsip_event_sub_pkg_cb cb; +} package; + +/* Event subscription manager singleton instance. */ +static struct pjsip_event_sub_mgr +{ + pj_pool_t *pool; + pj_hash_table_t *ht; + pjsip_endpoint *endpt; + pj_mutex_t *mutex; + pjsip_allow_events_hdr *allow_events; + package pkg_list; +} mgr; + +/* Fordward declarations for static functions. */ +static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t); +static pj_status_t mod_deinit(pjsip_module*); +static void tsx_handler(pjsip_module*, pjsip_event*); +static pjsip_event_sub *find_sub(pjsip_rx_data *); +static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*); +static void on_subscribe_response(void *, pjsip_event*); +static void on_notify_request(pjsip_transaction *, pjsip_rx_data*); +static void on_notify_response(void *, pjsip_event *); +static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*); +static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*); +static pj_status_t send_sub_refresh( pjsip_event_sub *sub ); + +/* Module descriptor. */ +static pjsip_module event_sub_module = +{ + {"EventSub", 8}, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + &mgr, /* User data. */ + 2, /* Number of methods supported . */ + { &SUBSCRIBE, &NOTIFY }, /* Array of methods */ + &mod_init, /* init_module() */ + NULL, /* start_module() */ + &mod_deinit, /* deinit_module() */ + &tsx_handler, /* tsx_handler() */ +}; + +/* + * Module initialization. + * This will be called by endpoint when it initializes all modules. + */ +static pj_status_t mod_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + pj_pool_t *pool; + + pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC); + if (!pool) + return -1; + + /* Manager initialization: create hash table and mutex. */ + mgr.pool = pool; + mgr.endpt = endpt; + mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE); + if (!mgr.ht) + return -1; + + mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE); + if (!mgr.mutex) + return -1; + + /* Attach manager to module. */ + mod->mod_data = &mgr; + + /* Init package list. */ + pj_list_init(&mgr.pkg_list); + + /* Init Allow-Events header. */ + mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool); + + /* Save the module ID. */ + mod_id = id; + + pjsip_event_notify_init_parser(); + return 0; +} + +/* + * Module deinitialization. + * Called by endpoint. + */ +static pj_status_t mod_deinit( struct pjsip_module *mod ) +{ + pj_mutex_lock(mgr.mutex); + pj_mutex_destroy(mgr.mutex); + pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool); + return 0; +} + +/* + * This public function is called by application to register callback. + * In exchange, the instance of the module is returned. + */ +PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void) +{ + return &event_sub_module; +} + +/* + * Register event package. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event, + int accept_cnt, + const pj_str_t accept[], + const pjsip_event_sub_pkg_cb *cb ) +{ + package *pkg; + int i; + + pj_mutex_lock(mgr.mutex); + + /* Create and register new package. */ + pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg)); + pj_strdup(mgr.pool, &pkg->event, event); + pj_list_insert_before(&mgr.pkg_list, pkg); + + /* Save Accept specification. */ + pkg->accept_cnt = accept_cnt; + pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t)); + for (i=0; iaccept[i], &accept[i]); + } + + /* Copy callback. */ + pj_memcpy(&pkg->cb, cb, sizeof(*cb)); + + /* Update Allow-Events header. */ + pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS); + mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event; + + pj_mutex_unlock(mgr.mutex); + return 0; +} + +/* + * Create subscription key (for hash table). + */ +static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool, + pjsip_role_e role, + const pj_str_t *call_id, const pj_str_t *from_tag) +{ + char *p; + + p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3); + *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C'); + *p++ = '$'; + pj_memcpy(p, call_id->ptr, call_id->slen); + p += call_id->slen; + *p++ = '$'; + pj_memcpy(p, from_tag->ptr, from_tag->slen); + p += from_tag->slen; + + key->slen = p - key->ptr; +} + + +/* + * Create UAC subscription. + */ +PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *event, + int expires, + int accept_cnt, + const pj_str_t accept[], + void *user_data, + const pjsip_event_sub_cb *cb) +{ + pjsip_tx_data *tdata; + pj_pool_t *pool; + const pjsip_hdr *hdr; + pjsip_event_sub *sub; + PJ_USE_EXCEPTION; + + PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s", + event->slen, event->ptr, to->slen, to->ptr)); + + /* Create pool for the event subscription. */ + pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC); + if (!pool) { + return NULL; + } + + /* Init subscription. */ + sub = pj_pool_calloc(pool, 1, sizeof(*sub)); + sub->pool = pool; + sub->endpt = endpt; + sub->role = PJSIP_ROLE_UAC; + sub->state = PJSIP_EVENT_SUB_STATE_PENDING; + sub->state_str = state[sub->state]; + sub->user_data = user_data; + sub->timer.id = 0; + sub->default_interval = expires; + pj_memcpy(&sub->cb, cb, sizeof(*cb)); + pj_list_init(&sub->auth_sess); + pj_list_init(&sub->route_set); + sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); + if (!sub->mutex) { + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + + /* The easiest way to parse the parameters is to create a dummy request! */ + tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from, + NULL, -1, NULL); + if (!tdata) { + pj_mutex_destroy(sub->mutex); + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + + /* + * Duplicate headers in the request to our structure. + */ + PJ_TRY { + int i; + + /* From */ + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); + pj_assert(hdr != NULL); + sub->from = pjsip_hdr_clone(pool, hdr); + + /* To */ + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); + pj_assert(hdr != NULL); + sub->to = pjsip_hdr_clone(pool, hdr); + + /* Contact. */ + sub->contact = pjsip_contact_hdr_create(pool); + sub->contact->uri = sub->from->uri; + + /* Call-ID */ + hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL); + pj_assert(hdr != NULL); + sub->call_id = pjsip_hdr_clone(pool, hdr); + + /* CSeq */ + sub->cseq = pj_rand() % 0xFFFF; + + /* Event. */ + sub->event = pjsip_event_hdr_create(sub->pool); + pj_strdup(pool, &sub->event->event_type, event); + + /* Expires. */ + sub->uac_expires = pjsip_expires_hdr_create(pool); + sub->uac_expires->ivalue = expires; + + /* Accept. */ + sub->local_accept = pjsip_accept_hdr_create(pool); + for (i=0; ilocal_accept->count++; + pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]); + } + + /* Register to hash table. */ + create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC, + &sub->call_id->id, &sub->from->tag); + pj_mutex_lock( mgr.mutex ); + pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); + pj_mutex_unlock( mgr.mutex ); + + } + PJ_DEFAULT { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init", + sub, state[sub->state].ptr, PJ_GET_EXCEPTION())); + + pjsip_tx_data_dec_ref(tdata); + pj_mutex_destroy(sub->mutex); + pjsip_endpt_destroy_pool(endpt, sub->pool); + return NULL; + } + PJ_END; + + /* All set, delete temporary transmit data as we don't need it. */ + pjsip_tx_data_dec_ref(tdata); + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s", + sub, state[sub->state].ptr, + to->slen, to->ptr, event->slen, event->ptr)); + + return sub; +} + +/* + * Set credentials. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, + int count, + const pjsip_cred_info cred[]) +{ + pj_mutex_lock(sub->mutex); + if (count > 0) { + sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info)); + pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info)); + } + sub->cred_cnt = count; + pj_mutex_unlock(sub->mutex); + return 0; +} + +/* + * Set route-set. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, + const pjsip_route_hdr *route_set ) +{ + const pjsip_route_hdr *hdr; + + pj_mutex_lock(sub->mutex); + + /* Clear existing route set. */ + pj_list_init(&sub->route_set); + + /* Duplicate route headers. */ + hdr = route_set->next; + while (hdr != route_set) { + pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr); + pj_list_insert_before(&sub->route_set, new_hdr); + hdr = hdr->next; + } + + pj_mutex_unlock(sub->mutex); + + return 0; +} + +/* + * Send subscribe request. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ) +{ + pj_status_t status; + + pj_mutex_lock(sub->mutex); + status = send_sub_refresh(sub); + pj_mutex_unlock(sub->mutex); + + return status; +} + +/* + * Destroy subscription. + * If there are pending transactions, then this will just set the flag. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub) +{ + pj_assert(sub != NULL); + if (sub == NULL) + return -1; + + /* Application must terminate the subscription first. */ + pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL || + sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed", + sub, state[sub->state].ptr)); + + pj_mutex_lock(mgr.mutex); + pj_mutex_lock(sub->mutex); + + /* Set delete flag. */ + sub->delete_flag = 1; + + /* Unregister timer, if any. */ + if (sub->timer.id != 0) { + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + sub->timer.id = 0; + } + + if (sub->pending_tsx > 0) { + pj_mutex_unlock(sub->mutex); + pj_mutex_unlock(mgr.mutex); + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later", + sub, state[sub->state].ptr, + sub->pending_tsx)); + return 1; + } + + /* Unregister from hash table. */ + pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL); + + /* Destroy. */ + pj_mutex_destroy(sub->mutex); + pjsip_endpt_destroy_pool(sub->endpt, sub->pool); + + pj_mutex_unlock(mgr.mutex); + + PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub)); + return 0; +} + +/* Change state. */ +static void sub_set_state( pjsip_event_sub *sub, int new_state) +{ + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s", + sub, state[sub->state].ptr, state[new_state].ptr)); + sub->state = new_state; + sub->state_str = state[new_state]; +} + +/* + * Refresh subscription. + */ +static pj_status_t send_sub_refresh( pjsip_event_sub *sub ) +{ + pjsip_tx_data *tdata; + pj_status_t status; + const pjsip_route_hdr *route; + + pj_assert(sub->role == PJSIP_ROLE_UAC); + pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED); + if (sub->role != PJSIP_ROLE_UAC || + sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) + { + return -1; + } + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription", + sub, state[sub->state].ptr)); + + /* Create request. */ + tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, + &SUBSCRIBE, + sub->to->uri, + sub->from, sub->to, + sub->contact, sub->call_id, + sub->cseq++, + NULL); + + if (!tdata) { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!", + sub, state[sub->state].ptr)); + return -1; + } + + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, sub->event)); + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept)); + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); + + /* Authentication */ + pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, + sub->cred_cnt, sub->cred_info); + + /* Route set. */ + route = sub->route_set.next; + while (route != &sub->route_set) { + pj_list_insert_before( &tdata->msg->hdr, + pjsip_hdr_shallow_clone(tdata->pool, route)); + route = route->next; + } + + /* Send */ + status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, + &on_subscribe_response); + if (status == 0) { + sub->pending_tsx++; + } else { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!", + sub, state[sub->state].ptr)); + } + + return status; +} + +/* + * Stop subscription. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ) +{ + pjsip_tx_data *tdata; + const pjsip_route_hdr *route; + pj_status_t status; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...", + sub, state[sub->state].ptr)); + + /* Lock subscription. */ + pj_mutex_lock(sub->mutex); + + pj_assert(sub->role == PJSIP_ROLE_UAC); + + /* Kill refresh timer, if any. */ + if (sub->timer.id != 0) { + sub->timer.id = 0; + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + } + + /* Create request. */ + tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, + &SUBSCRIBE, + sub->to->uri, + sub->from, sub->to, + sub->contact, sub->call_id, + sub->cseq++, + NULL); + + if (!tdata) { + pj_mutex_unlock(sub->mutex); + return -1; + } + + /* Add headers to request. */ + pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); + sub->uac_expires->ivalue = 0; + pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); + + /* Add authentication. */ + pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, + sub->cred_cnt, sub->cred_info); + + + /* Route set. */ + route = sub->route_set.next; + while (route != &sub->route_set) { + pj_list_insert_before( &tdata->msg->hdr, + pjsip_hdr_shallow_clone(tdata->pool, route)); + route = route->next; + } + + /* Prevent timer from refreshing itself. */ + sub->default_interval = 0; + + /* Set state. */ + sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED ); + + /* Send the request. */ + status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, + &on_subscribe_response); + if (status == 0) { + sub->pending_tsx++; + } + + pj_mutex_unlock(sub->mutex); + + if (status != 0) { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!", + sub, state[sub->state].ptr)); + } + + return status; +} + +/* + * Send notify. + */ +PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub, + pjsip_event_sub_state new_state, + const pj_str_t *reason, + pjsip_msg_body *body) +{ + pjsip_tx_data *tdata; + pjsip_sub_state_hdr *ss_hdr; + const pjsip_route_hdr *route; + pj_time_val now; + pj_status_t status; + pjsip_event_sub_state old_state = sub->state; + + pj_gettimeofday(&now); + + pj_assert(sub->role == PJSIP_ROLE_UAS); + if (sub->role != PJSIP_ROLE_UAS) + return -1; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY", + sub, state[new_state].ptr)); + + /* Lock subscription. */ + pj_mutex_lock(sub->mutex); + + /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */ + if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) { + pj_assert(0); + pj_mutex_unlock(sub->mutex); + return -1; + } + + /* Update state no matter what. */ + sub_set_state(sub, new_state); + + /* Create transmit data. */ + tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, + &NOTIFY, + sub->to->uri, + sub->from, sub->to, + sub->contact, sub->call_id, + sub->cseq++, + NULL); + if (!tdata) { + pj_mutex_unlock(sub->mutex); + return -1; + } + + /* Add Event header. */ + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); + + /* Add Subscription-State header. */ + ss_hdr = pjsip_sub_state_hdr_create(tdata->pool); + ss_hdr->sub_state = state[new_state]; + ss_hdr->expires_param = sub->expiry_time.sec - now.sec; + if (ss_hdr->expires_param < 0) + ss_hdr->expires_param = 0; + if (reason) + pj_strdup(tdata->pool, &ss_hdr->reason_param, reason); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr); + + /* Add Allow-Events header. */ + pjsip_msg_add_hdr( tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); + + /* Add authentication */ + pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, + sub->cred_cnt, sub->cred_info); + + /* Route set. */ + route = sub->route_set.next; + while (route != &sub->route_set) { + pj_list_insert_before( &tdata->msg->hdr, + pjsip_hdr_shallow_clone(tdata->pool, route)); + route = route->next; + } + + /* Attach body. */ + tdata->msg->body = body; + + /* That's it, send! */ + status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response); + if (status == 0) + sub->pending_tsx++; + + /* If terminated notify application. */ + if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { + if (sub->cb.on_sub_terminated) { + sub->pending_tsx++; + (*sub->cb.on_sub_terminated)(sub, reason); + sub->pending_tsx--; + } + } + + /* Unlock subscription. */ + pj_mutex_unlock(sub->mutex); + + if (status != 0) { + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY", + sub, state[sub->state].ptr)); + } + + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } + return status; +} + + +/* If this timer callback is called, it means subscriber hasn't refreshed its + * subscription on-time. Set the state to terminated. This will also send + * NOTIFY with Subscription-State set to terminated. + */ +static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) +{ + pjsip_event_sub *sub = entry->user_data; + pj_str_t reason = { "timeout", 7 }; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!", + sub, state[sub->state].ptr)); + + pj_mutex_lock(sub->mutex); + sub->timer.id = 0; + + if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) { + /* Notify application, but prevent app from destroying the sub. */ + ++sub->pending_tsx; + (*sub->cb.on_sub_terminated)(sub, &reason); + --sub->pending_tsx; + } + //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED, + // &reason, NULL); + pj_mutex_unlock(sub->mutex); + +} + +/* Schedule notifier expiration. */ +static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay) +{ + pj_time_val delay = { 0, 0 }; + pj_parsed_time pt; + + if (sub->timer.id != 0) + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + + pj_gettimeofday(&sub->expiry_time); + sub->expiry_time.sec += sec_delay; + + sub->timer.id = TIMER_ID_UAS_EXPIRY; + sub->timer.user_data = sub; + sub->timer.cb = &uas_expire_timer_cb; + delay.sec = sec_delay; + pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay); + + pj_time_decode(&sub->expiry_time, &pt); + PJ_LOG(4,(THIS_FILE, + "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)", + sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay)); +} + +/* This timer is called for UAC to refresh the subscription. */ +static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) +{ + pjsip_event_sub *sub = entry->user_data; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer", + sub, state[sub->state].ptr)); + + pj_mutex_lock(sub->mutex); + sub->timer.id = 0; + send_sub_refresh(sub); + pj_mutex_unlock(sub->mutex); +} + + +/* This will update the UAC's refresh schedule. */ +static void update_next_refresh(pjsip_event_sub *sub, int interval) +{ + pj_time_val delay = {0, 0}; + pj_parsed_time pt; + + if (interval < SECONDS_BEFORE_EXPIRY) { + PJ_LOG(4,(THIS_FILE, + "event_sub%p (%s): expiration delay too short (%d sec)! updated.", + sub, state[sub->state].ptr, interval)); + interval = SECONDS_BEFORE_EXPIRY; + } + + if (sub->timer.id != 0) + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + + sub->timer.id = TIMER_ID_REFRESH; + sub->timer.user_data = sub; + sub->timer.cb = &refresh_timer_cb; + pj_gettimeofday(&sub->expiry_time); + delay.sec = interval - SECONDS_BEFORE_EXPIRY; + sub->expiry_time.sec += delay.sec; + + pj_time_decode(&sub->expiry_time, &pt); + PJ_LOG(4,(THIS_FILE, + "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)", + sub, state[sub->state].ptr, + pt.hour, pt.min, pt.sec, + delay.sec)); + + pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay ); +} + + +/* Find subscription in the hash table. + * If found, lock the subscription before returning to caller. + */ +static pjsip_event_sub *find_sub(pjsip_rx_data *rdata) +{ + pj_str_t key; + pjsip_role_e role; + pjsip_event_sub *sub; + pjsip_method *method = &rdata->msg->line.req.method; + pj_str_t *tag; + + if (rdata->msg->type == PJSIP_REQUEST_MSG) { + if (pjsip_method_cmp(method, &SUBSCRIBE)==0) { + role = PJSIP_ROLE_UAS; + tag = &rdata->to_tag; + } else { + pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); + role = PJSIP_ROLE_UAC; + tag = &rdata->to_tag; + } + } else { + if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) { + role = PJSIP_ROLE_UAC; + tag = &rdata->from_tag; + } else { + pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); + role = PJSIP_ROLE_UAS; + tag = &rdata->from_tag; + } + } + create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag); + + pj_mutex_lock(mgr.mutex); + sub = pj_hash_get(mgr.ht, key.ptr, key.slen); + if (sub) + pj_mutex_lock(sub->mutex); + pj_mutex_unlock(mgr.mutex); + + return sub; +} + + +/* This function is called when we receive SUBSCRIBE request message + * to refresh existing subscription. + */ +static void on_received_sub_refresh( pjsip_event_sub *sub, + pjsip_transaction *tsx, pjsip_rx_data *rdata) +{ + pjsip_event_hdr *e; + pjsip_expires_hdr *expires; + pj_str_t hname; + int status = 200; + pj_str_t reason_phrase = { NULL, 0 }; + int new_state = sub->state; + int old_state = sub->state; + int new_interval = 0; + pjsip_tx_data *tdata; + + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", + sub, state[sub->state].ptr)); + + /* Check that the event matches. */ + hname = pj_str("Event"); + e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); + if (!e) { + status = 400; + reason_phrase = pj_str("Missing Event header"); + goto send_response; + } + if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 || + pj_stricmp(&e->id_param, &sub->event->id_param) != 0) + { + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + goto send_response; + } + + /* Check server state. */ + if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) { + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + goto send_response; + } + + /* Check expires header. */ + expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL); + if (!expires) { + /* + status = 400; + reason_phrase = pj_str("Missing Expires header"); + goto send_response; + */ + new_interval = sub->default_interval; + } else { + /* Check that interval is not too short. + * Note that expires time may be zero (for unsubscription). + */ + new_interval = expires->ivalue; + if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) { + status = PJSIP_SC_INTERVAL_TOO_BRIEF; + goto send_response; + } + } + + /* Update interval. */ + sub->default_interval = new_interval; + pj_gettimeofday(&sub->expiry_time); + sub->expiry_time.sec += new_interval; + + /* Update timer only if this is not unsubscription. */ + if (new_interval > 0) { + sub->default_interval = new_interval; + sub_schedule_uas_expire( sub, new_interval ); + + /* Call callback. */ + if (sub->cb.on_received_refresh) { + sub->pending_tsx++; + (*sub->cb.on_received_refresh)(sub, rdata); + sub->pending_tsx--; + } + } + +send_response: + tdata = pjsip_endpt_create_response( sub->endpt, rdata, status); + if (tdata) { + if (reason_phrase.slen) + tdata->msg->line.status.reason = reason_phrase; + + /* Add Expires header. */ + expires = pjsip_expires_hdr_create(tdata->pool); + expires->ivalue = sub->default_interval; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires); + + if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { + pjsip_msg_add_hdr(tdata->msg, + pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); + } + /* Send down to transaction. */ + pjsip_tsx_on_tx_msg(tsx, tdata); + } + + if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) { + /* Notify application if sub is terminated. */ + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + sub_set_state(sub, new_state); + if (new_state!=old_state && sub->cb.on_sub_terminated) { + pj_str_t reason = {"", 0}; + if (reason_phrase.slen) reason = reason_phrase; + else reason = *pjsip_get_status_text(status); + + sub->pending_tsx++; + (*sub->cb.on_sub_terminated)(sub, &reason); + sub->pending_tsx--; + } + } + + pj_mutex_unlock(sub->mutex); + + /* Prefer to call log when we're not holding the mutex. */ + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", + sub, state[sub->state].ptr, + (tdata ? tdata->obj_name : "null"), status)); + + /* Check if application has requested deletion. */ + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } + +} + + +/* This function is called when we receive SUBSCRIBE request message for + * a new subscription. + */ +static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata ) +{ + package *pkg; + pj_pool_t *pool; + pjsip_event_sub *sub = NULL; + pj_str_t hname; + int status = 200; + pj_str_t reason = { NULL, 0 }; + pjsip_tx_data *tdata; + pjsip_expires_hdr *expires; + pjsip_accept_hdr *accept; + pjsip_event_hdr *evhdr; + + /* Get the Event header. */ + hname = pj_str("Event"); + evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); + if (!evhdr) { + status = 400; + reason = pj_str("No Event header in request"); + goto send_response; + } + + /* Find corresponding package. + * We don't lock the manager's mutex since we assume the package list + * won't change once the application is running! + */ + pkg = mgr.pkg_list.next; + while (pkg != &mgr.pkg_list) { + if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0) + break; + pkg = pkg->next; + } + + if (pkg == &mgr.pkg_list) { + /* Event type is not supported by any packages! */ + status = 489; + reason = pj_str("Bad Event"); + goto send_response; + } + + /* First check that the Accept specification matches the + * package's Accept types. + */ + accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); + if (accept) { + unsigned i; + pj_str_t *content_type = NULL; + + for (i=0; icount && !content_type; ++i) { + int j; + for (j=0; jaccept_cnt; ++j) { + if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) { + content_type = &pkg->accept[j]; + break; + } + } + } + + if (!content_type) { + status = PJSIP_SC_NOT_ACCEPTABLE_HERE; + goto send_response; + } + } + + /* Check whether the package wants to accept the subscription. */ + pj_assert(pkg->cb.on_query_subscribe != NULL); + (*pkg->cb.on_query_subscribe)(rdata, &status); + if (!PJSIP_IS_STATUS_IN_CLASS(status,200)) + goto send_response; + + /* Create new subscription record. */ + pool = pjsip_endpt_create_pool(tsx->endpt, "esub", + SUB_POOL_SIZE, SUB_POOL_INC); + if (!pool) { + status = 500; + goto send_response; + } + sub = pj_pool_calloc(pool, 1, sizeof(*sub)); + sub->pool = pool; + sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); + if (!sub->mutex) { + status = 500; + goto send_response; + } + + PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub)); + + /* Start locking mutex. */ + pj_mutex_lock(sub->mutex); + + /* Init UAS subscription */ + sub->endpt = tsx->endpt; + sub->role = PJSIP_ROLE_UAS; + sub->state = PJSIP_EVENT_SUB_STATE_PENDING; + sub->state_str = state[sub->state]; + pj_list_init(&sub->auth_sess); + pj_list_init(&sub->route_set); + sub->from = pjsip_hdr_clone(pool, rdata->to); + pjsip_fromto_set_from(sub->from); + if (sub->from->tag.slen == 0) { + pj_create_unique_string(pool, &sub->from->tag); + rdata->to->tag = sub->from->tag; + } + sub->to = pjsip_hdr_clone(pool, rdata->from); + pjsip_fromto_set_to(sub->to); + sub->contact = pjsip_contact_hdr_create(pool); + sub->contact->uri = sub->from->uri; + sub->call_id = pjsip_cid_hdr_create(pool); + pj_strdup(pool, &sub->call_id->id, &rdata->call_id); + sub->cseq = pj_rand() % 0xFFFF; + + expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL); + if (expires) { + sub->default_interval = expires->ivalue; + if (sub->default_interval > 0 && + sub->default_interval < SECONDS_BEFORE_EXPIRY) + { + status = 423; /* Interval too short. */ + goto send_response; + } + } else { + sub->default_interval = 600; + } + + /* Clone Event header. */ + sub->event = pjsip_hdr_clone(pool, evhdr); + + /* Register to hash table. */ + create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id, + &sub->from->tag); + pj_mutex_lock(mgr.mutex); + pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); + pj_mutex_unlock(mgr.mutex); + + /* Set timer where subscription will expire only when expires<>0. + * Subscriber may send new subscription with expires==0. + */ + if (sub->default_interval != 0) { + sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY); + } + + /* Notify application. */ + if (pkg->cb.on_subscribe) { + pjsip_event_sub_cb *cb = NULL; + sub->pending_tsx++; + (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval); + sub->pending_tsx--; + if (cb == NULL) + pj_memset(&sub->cb, 0, sizeof(*cb)); + else + pj_memcpy(&sub->cb, cb, sizeof(*cb)); + } + + +send_response: + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", + sub, state[sub->state].ptr, status)); + + tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status); + if (tdata) { + if (reason.slen) { + /* Customize reason text. */ + tdata->msg->line.status.reason = reason; + } + if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { + /* Add Expires header. */ + pjsip_expires_hdr *hdr; + + hdr = pjsip_expires_hdr_create(tdata->pool); + hdr->ivalue = sub->default_interval; + pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr ); + } + if (status == 423) { + /* Add Min-Expires header. */ + pjsip_min_expires_hdr *hdr; + + hdr = pjsip_min_expires_hdr_create(tdata->pool); + hdr->ivalue = SECONDS_BEFORE_EXPIRY; + pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr); + } + if (status == 489 || + status==PJSIP_SC_NOT_ACCEPTABLE_HERE || + PJSIP_IS_STATUS_IN_CLASS(status,200)) + { + /* Add Allow-Events header. */ + pjsip_hdr *hdr; + hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); + pjsip_msg_add_hdr(tdata->msg, hdr); + + /* Should add Accept header?. */ + } + + pjsip_tsx_on_tx_msg(tsx, tdata); + } + + /* If received new subscription with expires=0, terminate. */ + if (sub && sub->default_interval == 0) { + pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); + if (sub->cb.on_sub_terminated) { + pj_str_t reason = { "timeout", 7 }; + (*sub->cb.on_sub_terminated)(sub, &reason); + } + } + + if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) { + if (sub && sub->mutex) { + pjsip_event_sub_destroy(sub); + } else if (sub) { + pjsip_endpt_destroy_pool(tsx->endpt, sub->pool); + } + } else { + pj_assert(status >= 200); + pj_mutex_unlock(sub->mutex); + } +} + +/* This is the main callback when SUBSCRIBE request is received. */ +static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) +{ + pjsip_event_sub *sub = find_sub(rdata); + + if (sub) + on_received_sub_refresh(sub, tsx, rdata); + else + on_new_subscription(tsx, rdata); +} + + +/* This callback is called when response to SUBSCRIBE is received. */ +static void on_subscribe_response(void *token, pjsip_event *event) +{ + pjsip_event_sub *sub = token; + pjsip_transaction *tsx = event->obj.tsx; + int new_state, old_state = sub->state; + + pj_assert(tsx->status_code >= 200); + if (tsx->status_code < 200) + return; + + pj_assert(sub->role == PJSIP_ROLE_UAC); + + /* Lock mutex. */ + pj_mutex_lock(sub->mutex); + + /* If request failed with 401/407 error, silently retry the request. */ + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req(sub->endpt, + sub->pool, &sub->auth_sess, + sub->cred_cnt, sub->cred_info, + tsx->last_tx, event->src.rdata ); + if (tdata) { + int status; + pjsip_cseq_hdr *cseq; + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + cseq->cseq = sub->cseq++; + status = pjsip_endpt_send_request( sub->endpt, tdata, + -1, sub, + &on_subscribe_response); + if (status == 0) { + pj_mutex_unlock(sub->mutex); + return; + } + } + } + + if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { + /* Update To tag. */ + if (sub->to->tag.slen == 0) + pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag); + + new_state = sub->state; + + } else if (tsx->status_code == 481) { + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + + } else if (tsx->status_code >= 300) { + /* RFC 3265 Section 3.1.4.2: + * If a SUBSCRIBE request to refresh a subscription fails + * with a non-481 response, the original subscription is still + * considered valid for the duration of original exires. + * + * Note: + * Since we normally send SUBSCRIBE for refreshing the subscription, + * it means the subscription already expired anyway. So we terminate + * the subscription now. + */ + if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) { + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } else { + /* Use this to be compliant with Section 3.1.4.2 + new_state = sub->state; + */ + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } + } else { + pj_assert(0); + new_state = sub->state; + } + + if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) { + sub_set_state(sub, new_state); + } + + if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE || + sub->state == PJSIP_EVENT_SUB_STATE_PENDING) + { + /* + * Register timer for next subscription refresh, but only when + * we're not unsubscribing. Also update default_interval and Expires + * header. + */ + if (sub->default_interval > 0 && !sub->delete_flag) { + pjsip_expires_hdr *exp = NULL; + + /* Could be transaction timeout. */ + if (event->src_type == PJSIP_EVENT_RX_MSG) { + exp = pjsip_msg_find_hdr(event->src.rdata->msg, + PJSIP_H_EXPIRES, NULL); + } + + if (exp) { + int delay = exp->ivalue; + if (delay > 0) { + pj_time_val new_expiry; + pj_gettimeofday(&new_expiry); + new_expiry.sec += delay; + if (sub->timer.id==0 || + new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) + { + //if (delay > 0 && delay < sub->default_interval) { + sub->default_interval = delay; + sub->uac_expires->ivalue = delay; + update_next_refresh(sub, delay); + } + } + } + } + } + + /* Call callback. */ + if (!sub->delete_flag) { + if (sub->cb.on_received_sub_response) { + (*sub->cb.on_received_sub_response)(sub, event); + } + } + + /* Notify application if we're terminated. */ + if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { + if (sub->cb.on_sub_terminated) { + pj_str_t reason; + if (event->src_type == PJSIP_EVENT_RX_MSG) + reason = event->src.rdata->msg->line.status.reason; + else + reason = *pjsip_get_status_text(tsx->status_code); + + (*sub->cb.on_sub_terminated)(sub, &reason); + } + } + + /* Decrement pending tsx count. */ + --sub->pending_tsx; + pj_assert(sub->pending_tsx >= 0); + + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } else { + pj_mutex_unlock(sub->mutex); + } + + /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */ +} + +/* + * This callback called when we receive incoming NOTIFY request. + */ +static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) +{ + pjsip_event_sub *sub; + pjsip_tx_data *tdata; + int status = 200; + int old_state; + pj_str_t reason = { NULL, 0 }; + pj_str_t reason_phrase = { NULL, 0 }; + int new_state = PJSIP_EVENT_SUB_STATE_NULL; + + /* Find subscription based on Call-ID and From tag. + * This will also automatically lock the subscription, if it's found. + */ + sub = find_sub(rdata); + if (!sub) { + /* RFC 3265: Section 3.2 Description of NOTIFY Behavior: + * Answer with 481 Subscription does not exist. + */ + PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!")); + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + + } else { + pj_assert(sub->role == PJSIP_ROLE_UAC); + PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", + sub, state[sub->state].ptr)); + + } + + new_state = old_state = sub->state; + + /* RFC 3265: Section 3.2.1 + * Check that the Event header match the subscription. + */ + if (status == 200) { + pjsip_event_hdr *hdr; + pj_str_t hname = { "Event", 5 }; + + hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); + if (!hdr) { + status = PJSIP_SC_BAD_REQUEST; + reason_phrase = pj_str("No Event header found"); + } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 || + pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) + { + status = 481; + reason_phrase = pj_str("Subscription does not exist"); + } + } + + /* Update subscription state and timer. */ + if (status == 200) { + pjsip_sub_state_hdr *hdr; + const pj_str_t hname = { "Subscription-State", 18 }; + const pj_str_t state_active = { "active", 6 }, + state_pending = { "pending", 7}, + state_terminated = { "terminated", 10 }; + + hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); + if (!hdr) { + status = PJSIP_SC_BAD_REQUEST; + reason_phrase = pj_str("No Subscription-State header found"); + goto process; + } + + /* + * Update subscription state. + */ + if (pj_stricmp(&hdr->sub_state, &state_active) == 0) { + if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) + new_state = PJSIP_EVENT_SUB_STATE_ACTIVE; + } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) { + if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) + new_state = PJSIP_EVENT_SUB_STATE_PENDING; + } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) { + new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } else { + new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN; + } + + reason = hdr->reason_param; + + if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL && + sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) + { + sub_set_state(sub, new_state); + if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) { + pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state); + } else { + sub->state_str = state[new_state]; + } + } + + /* + * Update timeout timer in required, just in case notifier changed the + * expiration to shorter time. + * Section 3.2.2: the expires param can only shorten the interval. + */ + if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || + sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) + { + pj_time_val now, new_expiry; + + pj_gettimeofday(&now); + new_expiry.sec = now.sec + hdr->expires_param; + if (sub->timer.id==0 || + new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) + { + update_next_refresh(sub, hdr->expires_param); + } + } + } + +process: + /* Note: here we sub MAY BE NULL! */ + + /* Send response to NOTIFY */ + tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status ); + if (tdata) { + if (reason_phrase.slen) + tdata->msg->line.status.reason = reason_phrase; + + if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { + pjsip_hdr *hdr; + hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); + pjsip_msg_add_hdr( tdata->msg, hdr); + } + + pjsip_tsx_on_tx_msg(tsx, tdata); + } + + /* Call NOTIFY callback, if any. */ + if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) { + sub->pending_tsx++; + (*sub->cb.on_received_notify)(sub, rdata); + sub->pending_tsx--; + } + + /* Check if subscription is terminated and call callback. */ + if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { + if (sub->cb.on_sub_terminated) { + sub->pending_tsx++; + (*sub->cb.on_sub_terminated)(sub, &reason); + sub->pending_tsx--; + } + } + + /* Check if application has requested deletion. */ + if (sub && sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } else if (sub) { + pj_mutex_unlock(sub->mutex); + } +} + +/* This callback is called when we received NOTIFY response. */ +static void on_notify_response(void *token, pjsip_event *event) +{ + pjsip_event_sub *sub = token; + pjsip_event_sub_state old_state = sub->state; + pjsip_transaction *tsx = event->obj.tsx; + + /* Lock the subscription. */ + pj_mutex_lock(sub->mutex); + + pj_assert(sub->role == PJSIP_ROLE_UAS); + + /* If request failed with authorization failure, silently retry. */ + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req(sub->endpt, + sub->pool, &sub->auth_sess, + sub->cred_cnt, sub->cred_info, + tsx->last_tx, event->src.rdata ); + if (tdata) { + int status; + pjsip_cseq_hdr *cseq; + cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + cseq->cseq = sub->cseq++; + status = pjsip_endpt_send_request( sub->endpt, tdata, + -1, sub, + &on_notify_response); + if (status == 0) { + pj_mutex_unlock(sub->mutex); + return; + } + } + } + + /* Notify application. */ + if (sub->cb.on_received_notify_response) + (*sub->cb.on_received_notify_response)(sub, event); + + /* Check for response 481. */ + if (event->obj.tsx->status_code == 481) { + /* Remote says that the subscription does not exist! + * Terminate subscription! + */ + sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED); + if (sub->timer.id) { + pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); + sub->timer.id = 0; + } + + PJ_LOG(4, (THIS_FILE, + "event_sub%p (%s): got 481 response to NOTIFY. Terminating...", + sub, state[sub->state].ptr)); + + /* Notify app. */ + if (sub->state!=old_state && sub->cb.on_sub_terminated) + (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason); + } + + /* Decrement pending transaction count. */ + --sub->pending_tsx; + pj_assert(sub->pending_tsx >= 0); + + /* Check that the subscription is marked for deletion. */ + if (sub->delete_flag && sub->pending_tsx <= 0) { + pjsip_event_sub_destroy(sub); + } else { + pj_mutex_unlock(sub->mutex); + } + + /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */ +} + + +/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY + * requests. + */ +static void tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_msg *msg; + pjsip_rx_data *rdata; + + /* Only want incoming message events. */ + if (event->src_type != PJSIP_EVENT_RX_MSG) + return; + + rdata = event->src.rdata; + msg = rdata->msg; + + /* Only want to process request messages. */ + if (msg->type != PJSIP_REQUEST_MSG) + return; + + /* Only want the first notification. */ + if (event->obj.tsx && event->obj.tsx->status_code >= 100) + return; + + if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) { + /* Process incoming SUBSCRIBE request. */ + on_subscribe_request( event->obj.tsx, rdata ); + } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) { + /* Process incoming NOTIFY request. */ + on_notify_request( event->obj.tsx, rdata ); + } +} + diff --git a/pjsip/src/pjsip_simple/event_notify.h b/pjsip/src/pjsip_simple/event_notify.h index bdc909dd1..a8c2c10de 100644 --- a/pjsip/src/pjsip_simple/event_notify.h +++ b/pjsip/src/pjsip_simple/event_notify.h @@ -1,313 +1,313 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify.h 7 8/31/05 9:05p Bennylp $ */ -#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__ -#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__ - -/** - * @file event_notify.h - * @brief SIP Specific Event Notification Extension (RFC 3265) - */ - -#include -#include -#include -#include - -/** - * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module - * @ingroup PJSIP_SIMPLE - * @{ - * - * This module provides the implementation of SIP Extension for SIP Specific - * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and - * NOTIFY methods. - * - * This module itself is extensible; new event packages can be registered to - * this module to handle specific extensions (such as presence). - */ - -PJ_BEGIN_DECL - -typedef struct pjsip_event_sub_cb pjsip_event_sub_cb; -typedef struct pjsip_event_sub pjsip_event_sub; - -/** - * This enumeration describes subscription state as described in the RFC 3265. - * The standard specifies that extensions may define additional states. In the - * case where the state is not known, the subscription state will be set to - * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str - * member of the susbcription structure. - */ -typedef enum pjsip_event_sub_state -{ - /** State is NULL. */ - PJSIP_EVENT_SUB_STATE_NULL, - - /** Subscription is active. */ - PJSIP_EVENT_SUB_STATE_ACTIVE, - - /** Subscription is pending. */ - PJSIP_EVENT_SUB_STATE_PENDING, - - /** Subscription is terminated. */ - PJSIP_EVENT_SUB_STATE_TERMINATED, - - /** Subscription state can not be determined. Application can query - * the state information in state_str member. - */ - PJSIP_EVENT_SUB_STATE_UNKNOWN, - -} pjsip_event_sub_state; - -/** - * This structure describes notification to be called when incoming SUBSCRIBE - * request is received. The module will call the callback registered by package - * that matches the event description in the incoming SUBSCRIBE. - */ -typedef struct pjsip_event_sub_pkg_cb -{ - /** - * This callback is called to first enquery the package whether it wants - * to accept incoming SUBSCRIBE request. If it does, then on_subscribe - * will be called. - * - * @param rdata The incoming request. - * @param status The status code to be returned back to subscriber. - */ - void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status); - - /** - * This callback is called when the module receives incoming SUBSCRIBE - * request. - * - * @param sub The subscription instance. - * @param rdata The received buffer. - * @param cb Callback to be registered to the subscription instance. - * @param expires The expiration to be set. - */ - void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata, - pjsip_event_sub_cb **cb, int *expires); - -} pjsip_event_sub_pkg_cb; - -/** - * This structure describes callback that is registered by application or - * package to receive notifications about a subscription. - */ -struct pjsip_event_sub_cb -{ - /** - * This callback is used by both subscriber and notifier. It is called - * when the subscription has been terminated. - * - * @param sub The subscription instance. - * @param reason The termination reason. - */ - void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason); - - /** - * This callback is called when we received SUBSCRIBE request to refresh - * the subscription. - * - * @param sub The subscription instance. - * @param rdata The received SUBSCRIBE request. - */ - void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata); - - /** - * This callback is called when the module receives final response on - * previously sent SUBSCRIBE request. - * - * @param sub The subscription instance. - * @param event The event. - */ - void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event); - - /** - * This callback is called when the module receives incoming NOTIFY - * request. - * - * @param sub The subscription instance. - * @param rdata The received data. - */ - void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata); - - /** - * This callback is called when the module receives final response to - * previously sent NOTIFY request. - * - * @param sub The subscription instance. - * @param event The event. - */ - void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event); - -}; - -/** - * This structure describes an event subscription record. The structure is used - * to represent both subscriber and notifier. - */ -struct pjsip_event_sub -{ - pj_pool_t *pool; /**< Pool. */ - pjsip_endpoint *endpt; /**< Endpoint. */ - pjsip_event_sub_cb cb; /**< Callback. */ - pj_mutex_t *mutex; /**< Mutex. */ - pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */ - pjsip_event_sub_state state; /**< Subscription state. */ - pj_str_t state_str; /**< String describing the state. */ - pjsip_from_hdr *from; /**< Cached local info (From) */ - pjsip_to_hdr *to; /**< Cached remote info (To) */ - pjsip_contact_hdr *contact; /**< Cached local contact. */ - pjsip_cid_hdr *call_id; /**< Cached Call-ID */ - int cseq; /**< Outgoing CSeq */ - pjsip_event_hdr *event; /**< Event description. */ - pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */ - pjsip_accept_hdr *local_accept; /**< Local Accept header. */ - pjsip_route_hdr route_set; /**< Route-set. */ - - pj_str_t key; /**< Key in the hash table. */ - void *user_data; /**< Application data. */ - int default_interval; /**< Refresh interval. */ - pj_timer_entry timer; /**< Internal timer. */ - pj_time_val expiry_time; /**< Time when subscription expires. */ - int pending_tsx; /**< Number of pending transactions. */ - pj_bool_t delete_flag; /**< Pending deletion flag. */ - - pjsip_auth_session auth_sess; /**< Authorization sessions. */ - unsigned cred_cnt; /**< Number of credentials. */ - pjsip_cred_info *cred_info; /**< Array of credentials. */ -}; - - - - -/** - * Initialize the module and get the instance of the module to be registered to - * endpoint. - * - * @return The module instance. - */ -PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void); - - -/** - * Register event package. - * - * @param event The event identification for the package. - * @param accept_cnt Number of strings in Accept array. - * @param accept Array of Accept value. - * @param cb Callback to receive incoming SUBSCRIBE for the package. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name, - int accept_cnt, - const pj_str_t accept[], - const pjsip_event_sub_pkg_cb *cb ); - - -/** - * Create initial subscription instance (client). - * - * @param endpt The endpoint. - * @param from URL to put in From header. - * @param to The target resource. - * @param event Event package. - * @param expires Expiration time. - * @param accept Accept specification. - * @param user_data Application data to attach to this subscription. - * - * @return New client subscription instance. - */ -PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, - const pj_str_t *from, - const pj_str_t *to, - const pj_str_t *event, - int expires, - int accept_cnt, - const pj_str_t accept[], - void *user_data, - const pjsip_event_sub_cb *cb); - -/** - * Set credentials to be used for outgoing request messages. - * - * @param sub Subscription instance. - * @param count Number of credentials. - * @param cred Array of credential info. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, - int count, - const pjsip_cred_info cred[]); - -/** - * Set route set for outgoing requests. - * - * @param sub Subscription instance. - * @param route_set List of route headers. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, - const pjsip_route_hdr *route_set ); - - -/** - * Send SUBSCRIBE request. - * - * @param sub Subscription instance. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ); - -/** - * Terminate subscription (client). This will send unsubscription request to - * notifier. - * - * @param sub Client subscription instance. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ); - - -/** - * For notifier, send NOTIFY request to subscriber, and set the state of - * the subscription. - * - * @param sub The server subscription (notifier) instance. - * @param state New state to set. - * @param reason Specify reason if new state is terminated, otherwise - * put NULL. - * @param type Description of content type. - * @param body Text body to send with the NOTIFY, or NULL if the - * NOTIFY request should not contain any message body. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub, - pjsip_event_sub_state state, - const pj_str_t *reason, - pjsip_msg_body *body); - -/** - * Destroy subscription instance. - * - * @param sub The client or server subscription instance. - * - * @return Zero on success, one if the subscription will be - * deleted automatically later, or -1 on error. - */ -PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub); - - -PJ_END_DECL - -/** - * @} - */ - -#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify.h 7 8/31/05 9:05p Bennylp $ */ +#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__ +#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__ + +/** + * @file event_notify.h + * @brief SIP Specific Event Notification Extension (RFC 3265) + */ + +#include +#include +#include +#include + +/** + * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module + * @ingroup PJSIP_SIMPLE + * @{ + * + * This module provides the implementation of SIP Extension for SIP Specific + * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and + * NOTIFY methods. + * + * This module itself is extensible; new event packages can be registered to + * this module to handle specific extensions (such as presence). + */ + +PJ_BEGIN_DECL + +typedef struct pjsip_event_sub_cb pjsip_event_sub_cb; +typedef struct pjsip_event_sub pjsip_event_sub; + +/** + * This enumeration describes subscription state as described in the RFC 3265. + * The standard specifies that extensions may define additional states. In the + * case where the state is not known, the subscription state will be set to + * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str + * member of the susbcription structure. + */ +typedef enum pjsip_event_sub_state +{ + /** State is NULL. */ + PJSIP_EVENT_SUB_STATE_NULL, + + /** Subscription is active. */ + PJSIP_EVENT_SUB_STATE_ACTIVE, + + /** Subscription is pending. */ + PJSIP_EVENT_SUB_STATE_PENDING, + + /** Subscription is terminated. */ + PJSIP_EVENT_SUB_STATE_TERMINATED, + + /** Subscription state can not be determined. Application can query + * the state information in state_str member. + */ + PJSIP_EVENT_SUB_STATE_UNKNOWN, + +} pjsip_event_sub_state; + +/** + * This structure describes notification to be called when incoming SUBSCRIBE + * request is received. The module will call the callback registered by package + * that matches the event description in the incoming SUBSCRIBE. + */ +typedef struct pjsip_event_sub_pkg_cb +{ + /** + * This callback is called to first enquery the package whether it wants + * to accept incoming SUBSCRIBE request. If it does, then on_subscribe + * will be called. + * + * @param rdata The incoming request. + * @param status The status code to be returned back to subscriber. + */ + void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status); + + /** + * This callback is called when the module receives incoming SUBSCRIBE + * request. + * + * @param sub The subscription instance. + * @param rdata The received buffer. + * @param cb Callback to be registered to the subscription instance. + * @param expires The expiration to be set. + */ + void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata, + pjsip_event_sub_cb **cb, int *expires); + +} pjsip_event_sub_pkg_cb; + +/** + * This structure describes callback that is registered by application or + * package to receive notifications about a subscription. + */ +struct pjsip_event_sub_cb +{ + /** + * This callback is used by both subscriber and notifier. It is called + * when the subscription has been terminated. + * + * @param sub The subscription instance. + * @param reason The termination reason. + */ + void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason); + + /** + * This callback is called when we received SUBSCRIBE request to refresh + * the subscription. + * + * @param sub The subscription instance. + * @param rdata The received SUBSCRIBE request. + */ + void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata); + + /** + * This callback is called when the module receives final response on + * previously sent SUBSCRIBE request. + * + * @param sub The subscription instance. + * @param event The event. + */ + void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event); + + /** + * This callback is called when the module receives incoming NOTIFY + * request. + * + * @param sub The subscription instance. + * @param rdata The received data. + */ + void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata); + + /** + * This callback is called when the module receives final response to + * previously sent NOTIFY request. + * + * @param sub The subscription instance. + * @param event The event. + */ + void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event); + +}; + +/** + * This structure describes an event subscription record. The structure is used + * to represent both subscriber and notifier. + */ +struct pjsip_event_sub +{ + pj_pool_t *pool; /**< Pool. */ + pjsip_endpoint *endpt; /**< Endpoint. */ + pjsip_event_sub_cb cb; /**< Callback. */ + pj_mutex_t *mutex; /**< Mutex. */ + pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */ + pjsip_event_sub_state state; /**< Subscription state. */ + pj_str_t state_str; /**< String describing the state. */ + pjsip_from_hdr *from; /**< Cached local info (From) */ + pjsip_to_hdr *to; /**< Cached remote info (To) */ + pjsip_contact_hdr *contact; /**< Cached local contact. */ + pjsip_cid_hdr *call_id; /**< Cached Call-ID */ + int cseq; /**< Outgoing CSeq */ + pjsip_event_hdr *event; /**< Event description. */ + pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */ + pjsip_accept_hdr *local_accept; /**< Local Accept header. */ + pjsip_route_hdr route_set; /**< Route-set. */ + + pj_str_t key; /**< Key in the hash table. */ + void *user_data; /**< Application data. */ + int default_interval; /**< Refresh interval. */ + pj_timer_entry timer; /**< Internal timer. */ + pj_time_val expiry_time; /**< Time when subscription expires. */ + int pending_tsx; /**< Number of pending transactions. */ + pj_bool_t delete_flag; /**< Pending deletion flag. */ + + pjsip_auth_session auth_sess; /**< Authorization sessions. */ + unsigned cred_cnt; /**< Number of credentials. */ + pjsip_cred_info *cred_info; /**< Array of credentials. */ +}; + + + + +/** + * Initialize the module and get the instance of the module to be registered to + * endpoint. + * + * @return The module instance. + */ +PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void); + + +/** + * Register event package. + * + * @param event The event identification for the package. + * @param accept_cnt Number of strings in Accept array. + * @param accept Array of Accept value. + * @param cb Callback to receive incoming SUBSCRIBE for the package. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name, + int accept_cnt, + const pj_str_t accept[], + const pjsip_event_sub_pkg_cb *cb ); + + +/** + * Create initial subscription instance (client). + * + * @param endpt The endpoint. + * @param from URL to put in From header. + * @param to The target resource. + * @param event Event package. + * @param expires Expiration time. + * @param accept Accept specification. + * @param user_data Application data to attach to this subscription. + * + * @return New client subscription instance. + */ +PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *event, + int expires, + int accept_cnt, + const pj_str_t accept[], + void *user_data, + const pjsip_event_sub_cb *cb); + +/** + * Set credentials to be used for outgoing request messages. + * + * @param sub Subscription instance. + * @param count Number of credentials. + * @param cred Array of credential info. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, + int count, + const pjsip_cred_info cred[]); + +/** + * Set route set for outgoing requests. + * + * @param sub Subscription instance. + * @param route_set List of route headers. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, + const pjsip_route_hdr *route_set ); + + +/** + * Send SUBSCRIBE request. + * + * @param sub Subscription instance. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ); + +/** + * Terminate subscription (client). This will send unsubscription request to + * notifier. + * + * @param sub Client subscription instance. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ); + + +/** + * For notifier, send NOTIFY request to subscriber, and set the state of + * the subscription. + * + * @param sub The server subscription (notifier) instance. + * @param state New state to set. + * @param reason Specify reason if new state is terminated, otherwise + * put NULL. + * @param type Description of content type. + * @param body Text body to send with the NOTIFY, or NULL if the + * NOTIFY request should not contain any message body. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub, + pjsip_event_sub_state state, + const pj_str_t *reason, + pjsip_msg_body *body); + +/** + * Destroy subscription instance. + * + * @param sub The client or server subscription instance. + * + * @return Zero on success, one if the subscription will be + * deleted automatically later, or -1 on error. + */ +PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub); + + +PJ_END_DECL + +/** + * @} + */ + +#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */ diff --git a/pjsip/src/pjsip_simple/event_notify_msg.c b/pjsip/src/pjsip_simple/event_notify_msg.c index 6e7136d6b..0451c816d 100644 --- a/pjsip/src/pjsip_simple/event_notify_msg.c +++ b/pjsip/src/pjsip_simple/event_notify_msg.c @@ -1,305 +1,305 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify_msg.c 2 6/21/05 12:37a Bennylp $ */ -#include -#include -#include -#include -#include -#include - -static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, - const pjsip_event_hdr *hdr); -static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_event_hdr*); - -static pjsip_hdr_vptr event_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_event_hdr_print, -}; - - -PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool) -{ - pj_str_t event = { "Event", 5 }; - pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - hdr->type = PJSIP_H_OTHER; - hdr->name = hdr->sname = event; - hdr->vptr = &event_hdr_vptr; - pj_list_init(hdr); - return hdr; -} - -static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - char *endbuf = buf+size; - int printed; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - copy_advance(p, hdr->event_type); - copy_advance_pair(p, ";id=", 4, hdr->id_param); - if (hdr->other_param.slen) - copy_advance(p, hdr->other_param); - return p - buf; -} - -static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, - const pjsip_event_hdr *rhs) -{ - pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); - pj_strdup(pool, &hdr->event_type, &rhs->event_type); - pj_strdup(pool, &hdr->id_param, &rhs->id_param); - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_event_hdr *rhs ) -{ - pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr *hdr); -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr*); - -static pjsip_hdr_vptr allow_event_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print, -}; - - -PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool) -{ - pj_str_t allow_events = { "Allow-Events", 12 }; - pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - hdr->type = PJSIP_H_OTHER; - hdr->name = hdr->sname = allow_events; - hdr->vptr = &allow_event_hdr_vptr; - pj_list_init(hdr); - return hdr; -} - -static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - char *endbuf = buf+size; - int printed; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - if (hdr->event_cnt > 0) { - int i; - copy_advance(p, hdr->events[0]); - for (i=1; ievent_cnt; ++i) { - copy_advance_pair(p, ",", 1, hdr->events[i]); - } - } - - return p - buf; -} - -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr *rhs) -{ - int i; - - pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); - hdr->event_cnt = rhs->event_cnt; - for (i=0; ievent_cnt; ++i) { - pj_strdup(pool, &hdr->events[i], &rhs->events[i]); - } - return hdr; -} - -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr *rhs) -{ - pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr *hdr); -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr*); - -static pjsip_hdr_vptr sub_state_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print, -}; - - -PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool) -{ - pj_str_t sub_state = { "Subscription-State", 18 }; - pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - hdr->type = PJSIP_H_OTHER; - hdr->name = hdr->sname = sub_state; - hdr->vptr = &sub_state_hdr_vptr; - hdr->expires_param = -1; - hdr->retry_after = -1; - pj_list_init(hdr); - return hdr; -} - -static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - char *endbuf = buf+size; - int printed; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - copy_advance(p, hdr->sub_state); - copy_advance_pair(p, ";reason=", 8, hdr->reason_param); - if (hdr->expires_param >= 0) { - pj_memcpy(p, ";expires=", 9); - p += 9; - printed = pj_utoa(hdr->expires_param, p); - p += printed; - } - if (hdr->retry_after >= 0) { - pj_memcpy(p, ";retry-after=", 13); - p += 9; - printed = pj_utoa(hdr->retry_after, p); - p += printed; - } - if (hdr->other_param.slen) - copy_advance(p, hdr->other_param); - - return p - buf; -} - -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr *rhs) -{ - pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); - pj_strdup(pool, &hdr->sub_state, &rhs->sub_state); - pj_strdup(pool, &hdr->reason_param, &rhs->reason_param); - hdr->retry_after = rhs->retry_after; - hdr->expires_param = rhs->expires_param; - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr *rhs) -{ - pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); - const pj_str_t id_param = { "id", 2 }; - - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type); - - while (*scanner->current == ';') { - pj_str_t pname, pvalue; - pj_scan_get_char(scanner); - pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); - if (pj_stricmp(&pname, &id_param)==0) { - hdr->id_param = pvalue; - } else { - pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); - } - } - pjsip_parse_end_hdr_imp( scanner ); - return hdr; -} - -static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); - - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]); - hdr->event_cnt = 1; - - while (*scanner->current == ',') { - pj_scan_get_char(scanner); - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]); - if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - } - - pjsip_parse_end_hdr_imp( scanner ); - return hdr; -} - -static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); - const pj_str_t reason = { "reason", 6 }, - expires = { "expires", 7 }, - retry_after = { "retry-after", 11 }; - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state); - - while (*scanner->current == ';') { - pj_str_t pname, pvalue; - - pj_scan_get_char(scanner); - pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); - if (pj_stricmp(&pname, &reason) == 0) { - hdr->reason_param = pvalue; - } else if (pj_stricmp(&pname, &expires) == 0) { - hdr->expires_param = pj_strtoul(&pvalue); - } else if (pj_stricmp(&pname, &retry_after) == 0) { - hdr->retry_after = pj_strtoul(&pvalue); - } else { - pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); - } - } - - pjsip_parse_end_hdr_imp( scanner ); - return hdr; -} - -PJ_DEF(void) pjsip_event_notify_init_parser(void) -{ - pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event); - pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events); - pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state); -} +/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify_msg.c 2 6/21/05 12:37a Bennylp $ */ +#include +#include +#include +#include +#include +#include + +static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, + const pjsip_event_hdr *hdr); +static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_event_hdr*); + +static pjsip_hdr_vptr event_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_event_hdr_print, +}; + + +PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool) +{ + pj_str_t event = { "Event", 5 }; + pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + hdr->type = PJSIP_H_OTHER; + hdr->name = hdr->sname = event; + hdr->vptr = &event_hdr_vptr; + pj_list_init(hdr); + return hdr; +} + +static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + char *endbuf = buf+size; + int printed; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + copy_advance(p, hdr->event_type); + copy_advance_pair(p, ";id=", 4, hdr->id_param); + if (hdr->other_param.slen) + copy_advance(p, hdr->other_param); + return p - buf; +} + +static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, + const pjsip_event_hdr *rhs) +{ + pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); + pj_strdup(pool, &hdr->event_type, &rhs->event_type); + pj_strdup(pool, &hdr->id_param, &rhs->id_param); + pj_strdup(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, + const pjsip_event_hdr *rhs ) +{ + pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr *hdr); +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr*); + +static pjsip_hdr_vptr allow_event_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print, +}; + + +PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool) +{ + pj_str_t allow_events = { "Allow-Events", 12 }; + pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + hdr->type = PJSIP_H_OTHER; + hdr->name = hdr->sname = allow_events; + hdr->vptr = &allow_event_hdr_vptr; + pj_list_init(hdr); + return hdr; +} + +static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + char *endbuf = buf+size; + int printed; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + if (hdr->event_cnt > 0) { + int i; + copy_advance(p, hdr->events[0]); + for (i=1; ievent_cnt; ++i) { + copy_advance_pair(p, ",", 1, hdr->events[i]); + } + } + + return p - buf; +} + +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr *rhs) +{ + int i; + + pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); + hdr->event_cnt = rhs->event_cnt; + for (i=0; ievent_cnt; ++i) { + pj_strdup(pool, &hdr->events[i], &rhs->events[i]); + } + return hdr; +} + +static pjsip_allow_events_hdr* +pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_allow_events_hdr *rhs) +{ + pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + + +static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, + char *buf, pj_size_t size); +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr *hdr); +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr*); + +static pjsip_hdr_vptr sub_state_hdr_vptr = +{ + (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone, + (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone, + (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print, +}; + + +PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool) +{ + pj_str_t sub_state = { "Subscription-State", 18 }; + pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + hdr->type = PJSIP_H_OTHER; + hdr->name = hdr->sname = sub_state; + hdr->vptr = &sub_state_hdr_vptr; + hdr->expires_param = -1; + hdr->retry_after = -1; + pj_list_init(hdr); + return hdr; +} + +static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, + char *buf, pj_size_t size) +{ + char *p = buf; + char *endbuf = buf+size; + int printed; + + copy_advance(p, hdr->name); + *p++ = ':'; + *p++ = ' '; + + copy_advance(p, hdr->sub_state); + copy_advance_pair(p, ";reason=", 8, hdr->reason_param); + if (hdr->expires_param >= 0) { + pj_memcpy(p, ";expires=", 9); + p += 9; + printed = pj_utoa(hdr->expires_param, p); + p += printed; + } + if (hdr->retry_after >= 0) { + pj_memcpy(p, ";retry-after=", 13); + p += 9; + printed = pj_utoa(hdr->retry_after, p); + p += printed; + } + if (hdr->other_param.slen) + copy_advance(p, hdr->other_param); + + return p - buf; +} + +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr *rhs) +{ + pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); + pj_strdup(pool, &hdr->sub_state, &rhs->sub_state); + pj_strdup(pool, &hdr->reason_param, &rhs->reason_param); + hdr->retry_after = rhs->retry_after; + hdr->expires_param = rhs->expires_param; + pj_strdup(pool, &hdr->other_param, &rhs->other_param); + return hdr; +} + +static pjsip_sub_state_hdr* +pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, + const pjsip_sub_state_hdr *rhs) +{ + pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pj_memcpy(hdr, rhs, sizeof(*hdr)); + return hdr; +} + +static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); + const pj_str_t id_param = { "id", 2 }; + + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type); + + while (*scanner->current == ';') { + pj_str_t pname, pvalue; + pj_scan_get_char(scanner); + pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); + if (pj_stricmp(&pname, &id_param)==0) { + hdr->id_param = pvalue; + } else { + pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); + } + } + pjsip_parse_end_hdr_imp( scanner ); + return hdr; +} + +static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); + + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]); + hdr->event_cnt = 1; + + while (*scanner->current == ',') { + pj_scan_get_char(scanner); + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]); + if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) { + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + } + } + + pjsip_parse_end_hdr_imp( scanner ); + return hdr; +} + +static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner, + pj_pool_t *pool) +{ + pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); + const pj_str_t reason = { "reason", 6 }, + expires = { "expires", 7 }, + retry_after = { "retry-after", 11 }; + pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state); + + while (*scanner->current == ';') { + pj_str_t pname, pvalue; + + pj_scan_get_char(scanner); + pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); + if (pj_stricmp(&pname, &reason) == 0) { + hdr->reason_param = pvalue; + } else if (pj_stricmp(&pname, &expires) == 0) { + hdr->expires_param = pj_strtoul(&pvalue); + } else if (pj_stricmp(&pname, &retry_after) == 0) { + hdr->retry_after = pj_strtoul(&pvalue); + } else { + pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); + } + } + + pjsip_parse_end_hdr_imp( scanner ); + return hdr; +} + +PJ_DEF(void) pjsip_event_notify_init_parser(void) +{ + pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event); + pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events); + pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state); +} diff --git a/pjsip/src/pjsip_simple/event_notify_msg.h b/pjsip/src/pjsip_simple/event_notify_msg.h index c3f735658..92e6bdd36 100644 --- a/pjsip/src/pjsip_simple/event_notify_msg.h +++ b/pjsip/src/pjsip_simple/event_notify_msg.h @@ -1,100 +1,100 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify_msg.h 4 8/24/05 10:33a Bennylp $ */ -#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ -#define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ - -/** - * @file event_notify_msg.h - * @brief SIP Event Notification Headers (RFC 3265) - */ -#include - -/** - * @ingroup PJSIP_EVENT_NOT - * @{ - */ - -PJ_BEGIN_DECL - - -/** Max events in Allow-Events header. */ -#define PJSIP_MAX_ALLOW_EVENTS 16 - -/** - * This structure describes Event header. - */ -typedef struct pjsip_event_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr) - pj_str_t event_type; /**< Event name. */ - pj_str_t id_param; /**< Optional event ID parameter. */ - pj_str_t other_param; /**< Other parameter, concatenated together. */ -} pjsip_event_hdr; - -/** - * Create an Event header. - * - * @param pool The pool. - * - * @return New Event header instance. - */ -PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool); - - -/** - * This structure describes Allow-Events header. - */ -typedef struct pjsip_allow_events_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr) - int event_cnt; /**< Number of event names. */ - pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */ -} pjsip_allow_events_hdr; - - -/** - * Create a new Allow-Events header. - * - * @param pool. The pool. - * - * @return Allow-Events header. - */ -PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool); - - -/** - * This structure describes Subscription-State header. - */ -typedef struct pjsip_sub_state_hdr -{ - PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr) - pj_str_t sub_state; /**< Subscription state. */ - pj_str_t reason_param; /**< Optional termination reason. */ - int expires_param; /**< Expires param, or -1. */ - int retry_after; /**< Retry after param, or -1. */ - pj_str_t other_param; /**< Other parameter, concatenated together. */ -} pjsip_sub_state_hdr; - -/** - * Create new Subscription-State header. - * - * @param pool The pool. - * - * @return Subscription-State header. - */ -PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool); - -/** - * Initialize parser for event notify module. - */ -PJ_DEF(void) pjsip_event_notify_init_parser(void); - - -PJ_END_DECL - - -/** - * @} - */ - -#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip_simple/event_notify_msg.h 4 8/24/05 10:33a Bennylp $ */ +#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ +#define __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ + +/** + * @file event_notify_msg.h + * @brief SIP Event Notification Headers (RFC 3265) + */ +#include + +/** + * @ingroup PJSIP_EVENT_NOT + * @{ + */ + +PJ_BEGIN_DECL + + +/** Max events in Allow-Events header. */ +#define PJSIP_MAX_ALLOW_EVENTS 16 + +/** + * This structure describes Event header. + */ +typedef struct pjsip_event_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr) + pj_str_t event_type; /**< Event name. */ + pj_str_t id_param; /**< Optional event ID parameter. */ + pj_str_t other_param; /**< Other parameter, concatenated together. */ +} pjsip_event_hdr; + +/** + * Create an Event header. + * + * @param pool The pool. + * + * @return New Event header instance. + */ +PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool); + + +/** + * This structure describes Allow-Events header. + */ +typedef struct pjsip_allow_events_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr) + int event_cnt; /**< Number of event names. */ + pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */ +} pjsip_allow_events_hdr; + + +/** + * Create a new Allow-Events header. + * + * @param pool. The pool. + * + * @return Allow-Events header. + */ +PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool); + + +/** + * This structure describes Subscription-State header. + */ +typedef struct pjsip_sub_state_hdr +{ + PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr) + pj_str_t sub_state; /**< Subscription state. */ + pj_str_t reason_param; /**< Optional termination reason. */ + int expires_param; /**< Expires param, or -1. */ + int retry_after; /**< Retry after param, or -1. */ + pj_str_t other_param; /**< Other parameter, concatenated together. */ +} pjsip_sub_state_hdr; + +/** + * Create new Subscription-State header. + * + * @param pool The pool. + * + * @return Subscription-State header. + */ +PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool); + +/** + * Initialize parser for event notify module. + */ +PJ_DEF(void) pjsip_event_notify_init_parser(void); + + +PJ_END_DECL + + +/** + * @} + */ + +#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_MSG_H__ */ + diff --git a/pjsip/src/pjsip_simple/messaging.c b/pjsip/src/pjsip_simple/messaging.c index 8b271f95c..b88912912 100644 --- a/pjsip/src/pjsip_simple/messaging.c +++ b/pjsip/src/pjsip_simple/messaging.c @@ -1,335 +1,335 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/messaging.c 7 8/31/05 9:05p Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define THIS_FILE "messaging" - -struct messaging_data -{ - void *token; - pjsip_messaging_cb cb; -}; - -struct pjsip_messaging_session -{ - pj_pool_t *pool; - pjsip_endpoint *endpt; - pjsip_from_hdr *from; - pjsip_to_hdr *to; - pjsip_cid_hdr *call_id; - pjsip_cseq_hdr *cseq; -}; - -static int module_id; -static pjsip_on_new_msg_cb incoming_cb; -static pjsip_method message_method; - - -/* - * Set global callback to receive incoming message. - */ -PJ_DEF(pjsip_on_new_msg_cb) -pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb) -{ - pjsip_on_new_msg_cb prev_cb = incoming_cb; - incoming_cb = cb; - return prev_cb; -} - - -/* - * Create an independent message (ie. not associated with a session). - */ -PJ_DEF(pjsip_tx_data*) -pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, - const pjsip_uri *target, - const pjsip_from_hdr *param_from, - const pjsip_to_hdr *param_to, - const pjsip_cid_hdr *param_call_id, - int param_cseq, - const pj_str_t *param_text) -{ - return pjsip_endpt_create_request_from_hdr( endpt, &message_method, - target, - param_from, param_to, - NULL, param_call_id, - param_cseq, param_text ); -} - -/* - * Create independent message from string (instead of from header). - */ -PJ_DEF(pjsip_tx_data*) -pjsip_messaging_create_msg( pjsip_endpoint *endpt, - const pj_str_t *target, - const pj_str_t *param_from, - const pj_str_t *param_to, - const pj_str_t *param_call_id, - int param_cseq, - const pj_str_t *param_text) -{ - return pjsip_endpt_create_request( endpt, &message_method, target, - param_from, param_to, NULL, param_call_id, - param_cseq, param_text); -} - -/* - * Initiate transaction to send outgoing message. - */ -PJ_DEF(pj_status_t) -pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata, - void *token, pjsip_messaging_cb cb ) -{ - pjsip_transaction *tsx; - struct messaging_data *msg_data; - - /* Create transaction. */ - tsx = pjsip_endpt_create_tsx(endpt); - if (!tsx) { - pjsip_tx_data_dec_ref(tdata); - return -1; - } - - /* Save parameters to messaging data and attach to tsx. */ - msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data)); - msg_data->cb = cb; - msg_data->token = token; - - /* Init transaction. */ - tsx->module_data[module_id] = msg_data; - if (pjsip_tsx_init_uac(tsx, tdata) != 0) { - pjsip_tx_data_dec_ref(tdata); - pjsip_endpt_destroy_tsx(endpt, tsx); - return -1; - } - - pjsip_endpt_register_tsx(endpt, tsx); - - /* - * Instruct transaction to send message. - * Further events will be received via transaction's event. - */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - /* Decrement reference counter. */ - pjsip_tx_data_dec_ref(tdata); - return 0; -} - - -/* - * Create 'IM session'. - */ -PJ_DEF(pjsip_messaging_session*) -pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from, - const pj_str_t *param_to ) -{ - pj_pool_t *pool; - pjsip_messaging_session *ses; - pj_str_t tmp, to; - - pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024); - if (!pool) - return NULL; - - ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session)); - ses->pool = pool; - ses->endpt = endpt; - - ses->call_id = pjsip_cid_hdr_create(pool); - pj_create_unique_string(pool, &ses->call_id->id); - - ses->cseq = pjsip_cseq_hdr_create(pool); - ses->cseq->cseq = pj_rand(); - ses->cseq->method = message_method; - - ses->from = pjsip_from_hdr_create(pool); - pj_strdup_with_null(pool, &tmp, param_from); - ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); - if (ses->from->uri == NULL) { - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - pj_create_unique_string(pool, &ses->from->tag); - - ses->to = pjsip_to_hdr_create(pool); - pj_strdup_with_null(pool, &to, param_from); - ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR); - if (ses->to->uri == NULL) { - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - - PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr)); - return ses; -} - - -/* - * Send IM message using identification from 'IM session'. - */ -PJ_DEF(pjsip_tx_data*) -pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text ) -{ - return pjsip_endpt_create_request_from_hdr( ses->endpt, - &message_method, - ses->to->uri, - ses->from, - ses->to, - NULL, - ses->call_id, - ses->cseq->cseq++, - text); -} - - -/* - * Destroy 'IM session'. - */ -PJ_DEF(pj_status_t) -pjsip_messaging_destroy_session( pjsip_messaging_session *ses ) -{ - /* - * NOTE ABOUT POSSIBLE BUG HERE... - * - * We don't check number of pending transaction before destroying IM - * session. As the result, the headers in the txdata of pending transaction - * wil be INVALID once the IM session is deleted (because we only - * shallo_clone()-ed them). - * - * This normally should be okay, because once the message is - * submitted to transaction, the transaction (or rather the transport) - * will 'print' the message to a buffer, and once it is printed, it - * won't try to access the original message again. So even when the - * original message has a dangling pointer, we should be safe. - * - * However, it will cause a problem if: - * - resolving completes asynchronously and with a substantial delay, - * and before the resolver/transport finished its job the user - * destroy the IM session. - * - if the transmit data is invalidated after the IM session is - * destroyed. - */ - - pjsip_endpt_destroy_pool(ses->endpt, ses->pool); - return 0; -} - - -static pj_status_t messaging_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - PJ_UNUSED_ARG(endpt) - PJ_UNUSED_ARG(mod) - - module_id = id; - return 0; -} - -static pj_status_t messaging_start( struct pjsip_module *mod ) -{ - PJ_UNUSED_ARG(mod) - return 0; -} - -static pj_status_t messaging_deinit( struct pjsip_module *mod ) -{ - PJ_UNUSED_ARG(mod) - return 0; -} - -static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_transaction *tsx = event->obj.tsx; - struct messaging_data *mod_data; - - PJ_UNUSED_ARG(mod) - - /* Ignore non transaction event */ - if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL) - return; - - /* If this is an incoming message, inform application. */ - if (tsx->role == PJSIP_ROLE_UAS) { - int status = 100; - pjsip_tx_data *tdata; - - /* Check if we already answered this request. */ - if (tsx->status_code >= 200) - return; - - /* Only handle MESSAGE requests!. */ - if (pjsip_method_cmp(&tsx->method, &message_method) != 0) - return; - - /* Call application callback. */ - if (incoming_cb) - status = (*incoming_cb)(event->src.rdata); - - if (status < 200 || status >= 700) - status = PJSIP_SC_INTERNAL_SERVER_ERROR; - - /* Respond request. */ - tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status ); - if (tdata) - pjsip_tsx_on_tx_msg(tsx, tdata); - - return; - } - - /* Ignore if it's not something that came from messaging module. */ - mod_data = tsx->module_data[ module_id ]; - if (mod_data == NULL) - return; - - /* Ignore non final response. */ - if (tsx->status_code < 200) - return; - - /* Don't want to call the callback more than once. */ - tsx->module_data[ module_id ] = NULL; - - /* Now call the callback. */ - if (mod_data->cb) { - (*mod_data->cb)(mod_data->token, tsx->status_code); - } -} - -static pjsip_module messaging_module = -{ - { "Messaging", 9}, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - NULL, /* User agent instance, initialized by APP. */ - 0, /* Number of methods supported (will be initialized later). */ - { 0 }, /* Array of methods (will be initialized later) */ - &messaging_init, /* init_module() */ - &messaging_start, /* start_module() */ - &messaging_deinit, /* deinit_module() */ - &messaging_tsx_handler, /* tsx_handler() */ -}; - -PJ_DEF(pjsip_module*) pjsip_messaging_get_module() -{ - static pj_str_t method_str = { "MESSAGE", 7 }; - - pjsip_method_init_np( &message_method, &method_str); - - messaging_module.method_cnt = 1; - messaging_module.methods[0] = &message_method; - - return &messaging_module; -} - +/* $Header: /pjproject/pjsip/src/pjsip_simple/messaging.c 7 8/31/05 9:05p Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "messaging" + +struct messaging_data +{ + void *token; + pjsip_messaging_cb cb; +}; + +struct pjsip_messaging_session +{ + pj_pool_t *pool; + pjsip_endpoint *endpt; + pjsip_from_hdr *from; + pjsip_to_hdr *to; + pjsip_cid_hdr *call_id; + pjsip_cseq_hdr *cseq; +}; + +static int module_id; +static pjsip_on_new_msg_cb incoming_cb; +static pjsip_method message_method; + + +/* + * Set global callback to receive incoming message. + */ +PJ_DEF(pjsip_on_new_msg_cb) +pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb) +{ + pjsip_on_new_msg_cb prev_cb = incoming_cb; + incoming_cb = cb; + return prev_cb; +} + + +/* + * Create an independent message (ie. not associated with a session). + */ +PJ_DEF(pjsip_tx_data*) +pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, + const pjsip_uri *target, + const pjsip_from_hdr *param_from, + const pjsip_to_hdr *param_to, + const pjsip_cid_hdr *param_call_id, + int param_cseq, + const pj_str_t *param_text) +{ + return pjsip_endpt_create_request_from_hdr( endpt, &message_method, + target, + param_from, param_to, + NULL, param_call_id, + param_cseq, param_text ); +} + +/* + * Create independent message from string (instead of from header). + */ +PJ_DEF(pjsip_tx_data*) +pjsip_messaging_create_msg( pjsip_endpoint *endpt, + const pj_str_t *target, + const pj_str_t *param_from, + const pj_str_t *param_to, + const pj_str_t *param_call_id, + int param_cseq, + const pj_str_t *param_text) +{ + return pjsip_endpt_create_request( endpt, &message_method, target, + param_from, param_to, NULL, param_call_id, + param_cseq, param_text); +} + +/* + * Initiate transaction to send outgoing message. + */ +PJ_DEF(pj_status_t) +pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata, + void *token, pjsip_messaging_cb cb ) +{ + pjsip_transaction *tsx; + struct messaging_data *msg_data; + + /* Create transaction. */ + tsx = pjsip_endpt_create_tsx(endpt); + if (!tsx) { + pjsip_tx_data_dec_ref(tdata); + return -1; + } + + /* Save parameters to messaging data and attach to tsx. */ + msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data)); + msg_data->cb = cb; + msg_data->token = token; + + /* Init transaction. */ + tsx->module_data[module_id] = msg_data; + if (pjsip_tsx_init_uac(tsx, tdata) != 0) { + pjsip_tx_data_dec_ref(tdata); + pjsip_endpt_destroy_tsx(endpt, tsx); + return -1; + } + + pjsip_endpt_register_tsx(endpt, tsx); + + /* + * Instruct transaction to send message. + * Further events will be received via transaction's event. + */ + pjsip_tsx_on_tx_msg(tsx, tdata); + + /* Decrement reference counter. */ + pjsip_tx_data_dec_ref(tdata); + return 0; +} + + +/* + * Create 'IM session'. + */ +PJ_DEF(pjsip_messaging_session*) +pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from, + const pj_str_t *param_to ) +{ + pj_pool_t *pool; + pjsip_messaging_session *ses; + pj_str_t tmp, to; + + pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024); + if (!pool) + return NULL; + + ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session)); + ses->pool = pool; + ses->endpt = endpt; + + ses->call_id = pjsip_cid_hdr_create(pool); + pj_create_unique_string(pool, &ses->call_id->id); + + ses->cseq = pjsip_cseq_hdr_create(pool); + ses->cseq->cseq = pj_rand(); + ses->cseq->method = message_method; + + ses->from = pjsip_from_hdr_create(pool); + pj_strdup_with_null(pool, &tmp, param_from); + ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); + if (ses->from->uri == NULL) { + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + pj_create_unique_string(pool, &ses->from->tag); + + ses->to = pjsip_to_hdr_create(pool); + pj_strdup_with_null(pool, &to, param_from); + ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR); + if (ses->to->uri == NULL) { + pjsip_endpt_destroy_pool(endpt, pool); + return NULL; + } + + PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr)); + return ses; +} + + +/* + * Send IM message using identification from 'IM session'. + */ +PJ_DEF(pjsip_tx_data*) +pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text ) +{ + return pjsip_endpt_create_request_from_hdr( ses->endpt, + &message_method, + ses->to->uri, + ses->from, + ses->to, + NULL, + ses->call_id, + ses->cseq->cseq++, + text); +} + + +/* + * Destroy 'IM session'. + */ +PJ_DEF(pj_status_t) +pjsip_messaging_destroy_session( pjsip_messaging_session *ses ) +{ + /* + * NOTE ABOUT POSSIBLE BUG HERE... + * + * We don't check number of pending transaction before destroying IM + * session. As the result, the headers in the txdata of pending transaction + * wil be INVALID once the IM session is deleted (because we only + * shallo_clone()-ed them). + * + * This normally should be okay, because once the message is + * submitted to transaction, the transaction (or rather the transport) + * will 'print' the message to a buffer, and once it is printed, it + * won't try to access the original message again. So even when the + * original message has a dangling pointer, we should be safe. + * + * However, it will cause a problem if: + * - resolving completes asynchronously and with a substantial delay, + * and before the resolver/transport finished its job the user + * destroy the IM session. + * - if the transmit data is invalidated after the IM session is + * destroyed. + */ + + pjsip_endpt_destroy_pool(ses->endpt, ses->pool); + return 0; +} + + +static pj_status_t messaging_init( pjsip_endpoint *endpt, + struct pjsip_module *mod, pj_uint32_t id ) +{ + PJ_UNUSED_ARG(endpt) + PJ_UNUSED_ARG(mod) + + module_id = id; + return 0; +} + +static pj_status_t messaging_start( struct pjsip_module *mod ) +{ + PJ_UNUSED_ARG(mod) + return 0; +} + +static pj_status_t messaging_deinit( struct pjsip_module *mod ) +{ + PJ_UNUSED_ARG(mod) + return 0; +} + +static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) +{ + pjsip_transaction *tsx = event->obj.tsx; + struct messaging_data *mod_data; + + PJ_UNUSED_ARG(mod) + + /* Ignore non transaction event */ + if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL) + return; + + /* If this is an incoming message, inform application. */ + if (tsx->role == PJSIP_ROLE_UAS) { + int status = 100; + pjsip_tx_data *tdata; + + /* Check if we already answered this request. */ + if (tsx->status_code >= 200) + return; + + /* Only handle MESSAGE requests!. */ + if (pjsip_method_cmp(&tsx->method, &message_method) != 0) + return; + + /* Call application callback. */ + if (incoming_cb) + status = (*incoming_cb)(event->src.rdata); + + if (status < 200 || status >= 700) + status = PJSIP_SC_INTERNAL_SERVER_ERROR; + + /* Respond request. */ + tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status ); + if (tdata) + pjsip_tsx_on_tx_msg(tsx, tdata); + + return; + } + + /* Ignore if it's not something that came from messaging module. */ + mod_data = tsx->module_data[ module_id ]; + if (mod_data == NULL) + return; + + /* Ignore non final response. */ + if (tsx->status_code < 200) + return; + + /* Don't want to call the callback more than once. */ + tsx->module_data[ module_id ] = NULL; + + /* Now call the callback. */ + if (mod_data->cb) { + (*mod_data->cb)(mod_data->token, tsx->status_code); + } +} + +static pjsip_module messaging_module = +{ + { "Messaging", 9}, /* Name. */ + 0, /* Flag */ + 128, /* Priority */ + NULL, /* User agent instance, initialized by APP. */ + 0, /* Number of methods supported (will be initialized later). */ + { 0 }, /* Array of methods (will be initialized later) */ + &messaging_init, /* init_module() */ + &messaging_start, /* start_module() */ + &messaging_deinit, /* deinit_module() */ + &messaging_tsx_handler, /* tsx_handler() */ +}; + +PJ_DEF(pjsip_module*) pjsip_messaging_get_module() +{ + static pj_str_t method_str = { "MESSAGE", 7 }; + + pjsip_method_init_np( &message_method, &method_str); + + messaging_module.method_cnt = 1; + messaging_module.methods[0] = &message_method; + + return &messaging_module; +} + diff --git a/pjsip/src/pjsip_simple/messaging.h b/pjsip/src/pjsip_simple/messaging.h index f1d26b81d..6b4c79d55 100644 --- a/pjsip/src/pjsip_simple/messaging.h +++ b/pjsip/src/pjsip_simple/messaging.h @@ -1,251 +1,251 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/messaging.h 6 8/31/05 9:05p Bennylp $ */ -#ifndef __PJSIP_SIMPLE_MESSAGING_H__ -#define __PJSIP_SIMPLE_MESSAGING_H__ - -/** - * @file messaging.h - * @brief Instant Messaging Extension (RFC 3428) - */ - -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module - * @ingroup PJSIP_SIMPLE - * @{ - * - * This module provides the implementation of SIP Extension for Instant - * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method. - * - * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/ - * receiving instant messaging. IM with SIP is basicly sessionless, which means - * that there is absolutely no correlation between IM messages sent or received - * by a host. Any correlation between IM messages is only perceivable by - * user, phsychologically. - * - * However, the RFC doesn't prohibit sending IM within a dialog (presumably - * using the same Call-ID and CSeq namespace), although it prohibits creating - * a dialog specificly for creating IM session. - * - * The implementation here is modeled to support both ways of sending IM msgs, - * i.e. sending IM message individually and sending IM message within a dialog. - * Although IM message can be associated with a dialog, this implementation of - * IM module is completely independent of the User Agent library in PJSIP. Yes, - * that's what is called modularity, and it demonstrates the clearness - * of PJSIP design (the last sentence is of course marketing crap :)). - * - * To send an IM message as part of dialog, application would first create the - * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq, - * From, and To header, then send the message using #pjsip_dlg_send_msg instead - * of #pjsip_messaging_send_msg. - * - * To send IM messages individually, application has two options. The first is - * to create the request with #pjsip_messaging_create_msg then send it with - * #pjsip_messaging_send_msg. But this way, application has to pre-construct - * From and To header first, which is not too convenient. - * - * The second option (to send IM messages not associated with a dialog) is to - * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as - * it doesn't have Contact header etc. An 'IM session' here is just a local - * state to cache most of IM headers, for convenience and optimization. Appl - * creates an IM session with #pjsip_messaging_create_session, and destroy - * the session with #pjsip_messaging_destroy_session. To create an outgoing - * IM message, application would call #pjsip_messaging_session_create_msg, - * and to send the message it would use #pjsip_messaging_send_msg. - * - * Message authorization is handled by application, as usual, by inserting a - * proper WWW-Authenticate or Proxy-Authenticate header before sending the - * message. - * - * And the last bit, to handle incoing IM messages. - * - * To handle incoming IM messages, application would register a global callback - * to be called when incoming messages arrive, by registering with function - * #pjsip_messaging_set_incoming_callback. This will be the global callback - * for all incoming IM messages. Although the message was sent as part of - * a dialog, it would still come here. And as long as the request has proper - * identification (Call-ID, From/To tag), the dialog will be aware of the - * request and update it's state (remote CSeq) accordingly. - */ - - - -/** - * Typedef for callback to be called when outgoing message has been sent - * and a final response has been received. - */ -typedef void (*pjsip_messaging_cb)(void *token, int status_code); - -/** - * Typedef for callback to receive incoming message. - * - * @param rdata Incoming message data. - * - * @return The status code to be returned back to original sender. - * Application must return a final status code upon returning - * from this function, or otherwise the stack will respond - * with error. - */ -typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata); - -/** - * Opaque data type for instant messaging session. - */ -typedef struct pjsip_messaging_session pjsip_messaging_session; - -/** - * Get the messaging module. - * - * @return SIP module. - */ -PJ_DECL(pjsip_module*) pjsip_messaging_get_module(); - -/** - * Set the global callback to be called when incoming message is received. - * - * @param cb The callback to be called when incoming message is received. - * - * @return The previous callback. - */ -PJ_DECL(pjsip_on_new_msg_cb) -pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb); - - -/** - * Create an instant message transmit data buffer using the specified arguments. - * The returned transmit data buffers will have it's reference counter set - * to 1, and when application send the buffer, the send function will decrement - * the reference counter. When the reference counter reach zero, the buffer - * will be deleted. As long as the function does not increment the buffer's - * reference counter between creating and sending the request, the buffer - * will always be deleted and no memory leak will occur. - * - * @param endpt Endpoint instance. - * @param target Target URL. - * @param from The "From" header, which content will be copied to request. - * If the "From" header doesn't have a tag parameter, the - * function will generate one. - * @param to The "To" header, which content will be copied to request. - * @param call_id Optionally specify Call-ID, or put NULL to make this - * function generate a unique Call-ID automatically. - * @param cseq Optionally specify CSeq, or put -1 to make this function - * generate a random CSeq. - * @param text Optionally specify "text/plain" message body, or put NULL - * if application wants to put body other than "text/plain" - * manually. - * - * @return SIP transmit data buffer, which reference count has been - * set to 1. - */ -PJ_DECL(pjsip_tx_data*) -pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, - const pjsip_uri *target, - const pjsip_from_hdr *from, - const pjsip_to_hdr *to, - const pjsip_cid_hdr *call_id, - int cseq, - const pj_str_t *text); - -/** - * Create instant message, by specifying URL string for both From and To header. - * - * @param endpt Endpoint instance. - * @param target Target URL. - * @param from URL of the sender. - * @param to URL of the recipient. - * @param call_id Optionally specify Call-ID, or put NULL to make this - * function generate a unique Call-ID automatically. - * @param cseq Optionally specify CSeq, or put -1 to make this function - * generate a random CSeq. - * @param text Optionally specify "text/plain" message body, or put NULL - * if application wants to put body other than "text/plain" - * manually. - * - * @return SIP transmit data buffer, which reference count has been - * set to 1. - */ -PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt, - const pj_str_t *target, - const pj_str_t *from, - const pj_str_t *to, - const pj_str_t *call_id, - int cseq, - const pj_str_t *text); - -/** - * Send the instant message transmit buffer and attach a callback to be called - * when the request has received a final response. This function will decrement - * the transmit buffer's reference counter, and when the reference counter - * reach zero, the buffer will be deleted. As long as the function does not - * increment the buffer's reference counter between creating the request and - * calling this function, the buffer will always be deleted regardless whether - * the sending was failed or succeeded. - * - * @param endpt Endpoint instance. - * @param tdata Transmit data buffer. - * @param token Token to be associated with the SIP transaction which sends - * this request. - * @param cb The callback to be called when the SIP request has received - * a final response from destination. - * - * @return Zero if the transaction was started successfully. Note that - * this doesn't mean the request has been received successfully - * by remote recipient. - */ -PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt, - pjsip_tx_data *tdata, - void *token, - pjsip_messaging_cb cb ); - -/** - * Create an instant messaging session, which can conveniently be used to send - * multiple instant messages to the same recipient. - * - * @param endpt Endpoint instance. - * @param from URI of sender. The function will add a unique tag parameter - * to this URI in the From header. - * @param to URI of recipient. - * - * @return Messaging session. - */ -PJ_DECL(pjsip_messaging_session*) -pjsip_messaging_create_session( pjsip_endpoint *endpt, - const pj_str_t *from, - const pj_str_t *to ); - -/** - * Create an instant message using instant messaging session, and optionally - * attach a text message. - * - * @param ses The instant messaging session. - * @param text Optional "text/plain" message to be attached as the - * message body. If this parameter is NULL, then no message - * body will be created, and application can attach any - * type of message body before the request is sent. - * - * @return SIP transmit data buffer, which reference counter has been - * set to 1. - */ -PJ_DECL(pjsip_tx_data*) -pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, - const pj_str_t *text ); - -/** - * Destroy an instant messaging session. - * - * @param ses The instant messaging session. - * - * @return Zero on successfull. - */ -PJ_DECL(pj_status_t) -pjsip_messaging_destroy_session( pjsip_messaging_session *ses ); - -/** - * @} - */ - -PJ_END_DECL - -#endif +/* $Header: /pjproject/pjsip/src/pjsip_simple/messaging.h 6 8/31/05 9:05p Bennylp $ */ +#ifndef __PJSIP_SIMPLE_MESSAGING_H__ +#define __PJSIP_SIMPLE_MESSAGING_H__ + +/** + * @file messaging.h + * @brief Instant Messaging Extension (RFC 3428) + */ + +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module + * @ingroup PJSIP_SIMPLE + * @{ + * + * This module provides the implementation of SIP Extension for Instant + * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method. + * + * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/ + * receiving instant messaging. IM with SIP is basicly sessionless, which means + * that there is absolutely no correlation between IM messages sent or received + * by a host. Any correlation between IM messages is only perceivable by + * user, phsychologically. + * + * However, the RFC doesn't prohibit sending IM within a dialog (presumably + * using the same Call-ID and CSeq namespace), although it prohibits creating + * a dialog specificly for creating IM session. + * + * The implementation here is modeled to support both ways of sending IM msgs, + * i.e. sending IM message individually and sending IM message within a dialog. + * Although IM message can be associated with a dialog, this implementation of + * IM module is completely independent of the User Agent library in PJSIP. Yes, + * that's what is called modularity, and it demonstrates the clearness + * of PJSIP design (the last sentence is of course marketing crap :)). + * + * To send an IM message as part of dialog, application would first create the + * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq, + * From, and To header, then send the message using #pjsip_dlg_send_msg instead + * of #pjsip_messaging_send_msg. + * + * To send IM messages individually, application has two options. The first is + * to create the request with #pjsip_messaging_create_msg then send it with + * #pjsip_messaging_send_msg. But this way, application has to pre-construct + * From and To header first, which is not too convenient. + * + * The second option (to send IM messages not associated with a dialog) is to + * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as + * it doesn't have Contact header etc. An 'IM session' here is just a local + * state to cache most of IM headers, for convenience and optimization. Appl + * creates an IM session with #pjsip_messaging_create_session, and destroy + * the session with #pjsip_messaging_destroy_session. To create an outgoing + * IM message, application would call #pjsip_messaging_session_create_msg, + * and to send the message it would use #pjsip_messaging_send_msg. + * + * Message authorization is handled by application, as usual, by inserting a + * proper WWW-Authenticate or Proxy-Authenticate header before sending the + * message. + * + * And the last bit, to handle incoing IM messages. + * + * To handle incoming IM messages, application would register a global callback + * to be called when incoming messages arrive, by registering with function + * #pjsip_messaging_set_incoming_callback. This will be the global callback + * for all incoming IM messages. Although the message was sent as part of + * a dialog, it would still come here. And as long as the request has proper + * identification (Call-ID, From/To tag), the dialog will be aware of the + * request and update it's state (remote CSeq) accordingly. + */ + + + +/** + * Typedef for callback to be called when outgoing message has been sent + * and a final response has been received. + */ +typedef void (*pjsip_messaging_cb)(void *token, int status_code); + +/** + * Typedef for callback to receive incoming message. + * + * @param rdata Incoming message data. + * + * @return The status code to be returned back to original sender. + * Application must return a final status code upon returning + * from this function, or otherwise the stack will respond + * with error. + */ +typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata); + +/** + * Opaque data type for instant messaging session. + */ +typedef struct pjsip_messaging_session pjsip_messaging_session; + +/** + * Get the messaging module. + * + * @return SIP module. + */ +PJ_DECL(pjsip_module*) pjsip_messaging_get_module(); + +/** + * Set the global callback to be called when incoming message is received. + * + * @param cb The callback to be called when incoming message is received. + * + * @return The previous callback. + */ +PJ_DECL(pjsip_on_new_msg_cb) +pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb); + + +/** + * Create an instant message transmit data buffer using the specified arguments. + * The returned transmit data buffers will have it's reference counter set + * to 1, and when application send the buffer, the send function will decrement + * the reference counter. When the reference counter reach zero, the buffer + * will be deleted. As long as the function does not increment the buffer's + * reference counter between creating and sending the request, the buffer + * will always be deleted and no memory leak will occur. + * + * @param endpt Endpoint instance. + * @param target Target URL. + * @param from The "From" header, which content will be copied to request. + * If the "From" header doesn't have a tag parameter, the + * function will generate one. + * @param to The "To" header, which content will be copied to request. + * @param call_id Optionally specify Call-ID, or put NULL to make this + * function generate a unique Call-ID automatically. + * @param cseq Optionally specify CSeq, or put -1 to make this function + * generate a random CSeq. + * @param text Optionally specify "text/plain" message body, or put NULL + * if application wants to put body other than "text/plain" + * manually. + * + * @return SIP transmit data buffer, which reference count has been + * set to 1. + */ +PJ_DECL(pjsip_tx_data*) +pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, + const pjsip_uri *target, + const pjsip_from_hdr *from, + const pjsip_to_hdr *to, + const pjsip_cid_hdr *call_id, + int cseq, + const pj_str_t *text); + +/** + * Create instant message, by specifying URL string for both From and To header. + * + * @param endpt Endpoint instance. + * @param target Target URL. + * @param from URL of the sender. + * @param to URL of the recipient. + * @param call_id Optionally specify Call-ID, or put NULL to make this + * function generate a unique Call-ID automatically. + * @param cseq Optionally specify CSeq, or put -1 to make this function + * generate a random CSeq. + * @param text Optionally specify "text/plain" message body, or put NULL + * if application wants to put body other than "text/plain" + * manually. + * + * @return SIP transmit data buffer, which reference count has been + * set to 1. + */ +PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt, + const pj_str_t *target, + const pj_str_t *from, + const pj_str_t *to, + const pj_str_t *call_id, + int cseq, + const pj_str_t *text); + +/** + * Send the instant message transmit buffer and attach a callback to be called + * when the request has received a final response. This function will decrement + * the transmit buffer's reference counter, and when the reference counter + * reach zero, the buffer will be deleted. As long as the function does not + * increment the buffer's reference counter between creating the request and + * calling this function, the buffer will always be deleted regardless whether + * the sending was failed or succeeded. + * + * @param endpt Endpoint instance. + * @param tdata Transmit data buffer. + * @param token Token to be associated with the SIP transaction which sends + * this request. + * @param cb The callback to be called when the SIP request has received + * a final response from destination. + * + * @return Zero if the transaction was started successfully. Note that + * this doesn't mean the request has been received successfully + * by remote recipient. + */ +PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt, + pjsip_tx_data *tdata, + void *token, + pjsip_messaging_cb cb ); + +/** + * Create an instant messaging session, which can conveniently be used to send + * multiple instant messages to the same recipient. + * + * @param endpt Endpoint instance. + * @param from URI of sender. The function will add a unique tag parameter + * to this URI in the From header. + * @param to URI of recipient. + * + * @return Messaging session. + */ +PJ_DECL(pjsip_messaging_session*) +pjsip_messaging_create_session( pjsip_endpoint *endpt, + const pj_str_t *from, + const pj_str_t *to ); + +/** + * Create an instant message using instant messaging session, and optionally + * attach a text message. + * + * @param ses The instant messaging session. + * @param text Optional "text/plain" message to be attached as the + * message body. If this parameter is NULL, then no message + * body will be created, and application can attach any + * type of message body before the request is sent. + * + * @return SIP transmit data buffer, which reference counter has been + * set to 1. + */ +PJ_DECL(pjsip_tx_data*) +pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, + const pj_str_t *text ); + +/** + * Destroy an instant messaging session. + * + * @param ses The instant messaging session. + * + * @return Zero on successfull. + */ +PJ_DECL(pj_status_t) +pjsip_messaging_destroy_session( pjsip_messaging_session *ses ); + +/** + * @} + */ + +PJ_END_DECL + +#endif diff --git a/pjsip/src/pjsip_simple/pidf.c b/pjsip/src/pjsip_simple/pidf.c index 4db1c14b8..f3505a9cc 100644 --- a/pjsip/src/pjsip_simple/pidf.c +++ b/pjsip/src/pjsip_simple/pidf.c @@ -1,333 +1,333 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/pidf.c 3 6/21/05 12:37a Bennylp $ */ -#include -#include -#include - -struct pjpidf_op_desc pjpidf_op = -{ - { - &pjpidf_pres_construct, - &pjpidf_pres_add_tuple, - &pjpidf_pres_get_first_tuple, - &pjpidf_pres_get_next_tuple, - &pjpidf_pres_find_tuple, - &pjpidf_pres_remove_tuple, - &pjpidf_pres_add_note, - &pjpidf_pres_get_first_note, - &pjpidf_pres_get_next_note - }, - { - &pjpidf_tuple_construct, - &pjpidf_tuple_get_id, - &pjpidf_tuple_set_id, - &pjpidf_tuple_get_status, - &pjpidf_tuple_get_contact, - &pjpidf_tuple_set_contact, - &pjpidf_tuple_set_contact_prio, - &pjpidf_tuple_get_contact_prio, - &pjpidf_tuple_add_note, - &pjpidf_tuple_get_first_note, - &pjpidf_tuple_get_next_note, - &pjpidf_tuple_get_timestamp, - &pjpidf_tuple_set_timestamp, - &pjpidf_tuple_set_timestamp_np - }, - { - &pjpidf_status_construct, - &pjpidf_status_is_basic_open, - &pjpidf_status_set_basic_open - } -}; - -static pj_str_t PRESENCE = { "presence", 8 }; -static pj_str_t ENTITY = { "entity", 6}; -static pj_str_t TUPLE = { "tuple", 5 }; -static pj_str_t ID = { "id", 2 }; -static pj_str_t NOTE = { "note", 4 }; -static pj_str_t STATUS = { "status", 6 }; -static pj_str_t CONTACT = { "contact", 7 }; -static pj_str_t PRIORITY = { "priority", 8 }; -static pj_str_t TIMESTAMP = { "timestamp", 9 }; -static pj_str_t BASIC = { "basic", 5 }; -static pj_str_t OPEN = { "open", 4 }; -static pj_str_t CLOSED = { "closed", 6 }; -static pj_str_t EMPTY_STRING = { NULL, 0 }; - -static void xml_init_node(pj_pool_t *pool, pj_xml_node *node, - pj_str_t *name, const pj_str_t *value) -{ - pj_list_init(&node->attr_head); - pj_list_init(&node->node_head); - node->name = *name; - if (value) pj_strdup(pool, &node->content, value); - else node->content.ptr=NULL, node->content.slen=0; -} - -static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, - const pj_str_t *value) -{ - pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); - attr->name = *name; - pj_strdup(pool, &attr->value, value); - return attr; -} - -/* Presence */ -PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *entity) -{ - pj_xml_attr *attr; - - xml_init_node(pool, pres, &PRESENCE, NULL); - attr = xml_create_attr(pool, &ENTITY, entity); - pj_xml_add_attr(pres, attr); -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *id) -{ - pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t)); - pjpidf_tuple_construct(pool, t, id); - pj_xml_add_node(pres, t); - return t; -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres) -{ - return pj_xml_find_node(pres, &TUPLE); -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, - pjpidf_tuple *tuple) -{ - return pj_xml_find_next_node(pres, tuple, &TUPLE); -} - -static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id) -{ - return pj_xml_find_attr(node, &ID, id) != NULL; -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id) -{ - return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id); -} - -PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t) -{ - PJ_UNUSED_ARG(pres) - pj_list_erase(t); -} - -PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *text) -{ - pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); - xml_init_node(pool, note, &NOTE, text); - pj_xml_add_node(pres, note); - return note; -} - -PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres) -{ - return pj_xml_find_node( pres, &NOTE); -} - -PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note) -{ - return pj_xml_find_next_node(t, note, &NOTE); -} - - -/* Tuple */ -PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *id) -{ - pj_xml_attr *attr; - pjpidf_status *st; - - xml_init_node(pool, t, &TUPLE, NULL); - attr = xml_create_attr(pool, &ID, id); - pj_xml_add_attr(t, attr); - st = pj_pool_alloc(pool, sizeof(*st)); - pjpidf_status_construct(pool, st); - pj_xml_add_node(t, st); -} - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t) -{ - const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL); - pj_assert(attr); - return &attr->value; -} - -PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id) -{ - pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL); - pj_assert(attr); - pj_strdup(pool, &attr->value, id); -} - - -PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t) -{ - pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS); - pj_assert(st); - return st; -} - - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); - if (!node) - return &EMPTY_STRING; - return &node->content; -} - -PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *contact) -{ - pj_xml_node *node = pj_xml_find_node(t, &CONTACT); - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &CONTACT, contact); - pj_xml_add_node(t, node); - } else { - pj_strdup(pool, &node->content, contact); - } -} - -PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *prio) -{ - pj_xml_node *node = pj_xml_find_node(t, &CONTACT); - pj_xml_attr *attr; - - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &CONTACT, NULL); - pj_xml_add_node(t, node); - } - attr = pj_xml_find_attr(node, &PRIORITY, NULL); - if (!attr) { - attr = xml_create_attr(pool, &PRIORITY, prio); - pj_xml_add_attr(node, attr); - } else { - pj_strdup(pool, &attr->value, prio); - } -} - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); - pj_xml_attr *attr; - - if (!node) - return &EMPTY_STRING; - attr = pj_xml_find_attr(node, &PRIORITY, NULL); - if (!attr) - return &EMPTY_STRING; - return &attr->value; -} - - -PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *text) -{ - pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); - xml_init_node(pool, note, &NOTE, text); - pj_xml_add_node(t, note); - return note; -} - -PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t) -{ - return pj_xml_find_node(t, &NOTE); -} - -PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n) -{ - return pj_xml_find_next_node(t, n, &NOTE); -} - - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP); - return node ? &node->content : &EMPTY_STRING; -} - -PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *ts) -{ - pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &TIMESTAMP, ts); - } else { - pj_strdup(pool, &node->content, ts); - } -} - - -PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t, - pj_str_t *ts) -{ - pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &TIMESTAMP, ts); - } else { - node->content = *ts; - } -} - - -/* Status */ -PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st) -{ - pj_xml_node *node; - - xml_init_node(pool, st, &STATUS, NULL); - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &BASIC, &CLOSED); - pj_xml_add_node(st, node); -} - -PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC); - pj_assert(node != NULL); - return pj_stricmp(&node->content, &OPEN)==0; -} - -PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open) -{ - pj_xml_node *node = pj_xml_find_node(st, &BASIC); - pj_assert(node != NULL); - node->content = open ? OPEN : CLOSED; -} - -PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity) -{ - pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres)); - pjpidf_pres_construct(pool, pres, entity); - return pres; -} - -PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len) -{ - pjpidf_pres *pres = pj_xml_parse(pool, text, len); - if (pres) { - if (pj_stricmp(&pres->name, &PRESENCE) != 0) - return NULL; - } - return pres; -} - -PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len) -{ - return pj_xml_print(pres, buf, len, PJ_TRUE); -} - +/* $Header: /pjproject/pjsip/src/pjsip_simple/pidf.c 3 6/21/05 12:37a Bennylp $ */ +#include +#include +#include + +struct pjpidf_op_desc pjpidf_op = +{ + { + &pjpidf_pres_construct, + &pjpidf_pres_add_tuple, + &pjpidf_pres_get_first_tuple, + &pjpidf_pres_get_next_tuple, + &pjpidf_pres_find_tuple, + &pjpidf_pres_remove_tuple, + &pjpidf_pres_add_note, + &pjpidf_pres_get_first_note, + &pjpidf_pres_get_next_note + }, + { + &pjpidf_tuple_construct, + &pjpidf_tuple_get_id, + &pjpidf_tuple_set_id, + &pjpidf_tuple_get_status, + &pjpidf_tuple_get_contact, + &pjpidf_tuple_set_contact, + &pjpidf_tuple_set_contact_prio, + &pjpidf_tuple_get_contact_prio, + &pjpidf_tuple_add_note, + &pjpidf_tuple_get_first_note, + &pjpidf_tuple_get_next_note, + &pjpidf_tuple_get_timestamp, + &pjpidf_tuple_set_timestamp, + &pjpidf_tuple_set_timestamp_np + }, + { + &pjpidf_status_construct, + &pjpidf_status_is_basic_open, + &pjpidf_status_set_basic_open + } +}; + +static pj_str_t PRESENCE = { "presence", 8 }; +static pj_str_t ENTITY = { "entity", 6}; +static pj_str_t TUPLE = { "tuple", 5 }; +static pj_str_t ID = { "id", 2 }; +static pj_str_t NOTE = { "note", 4 }; +static pj_str_t STATUS = { "status", 6 }; +static pj_str_t CONTACT = { "contact", 7 }; +static pj_str_t PRIORITY = { "priority", 8 }; +static pj_str_t TIMESTAMP = { "timestamp", 9 }; +static pj_str_t BASIC = { "basic", 5 }; +static pj_str_t OPEN = { "open", 4 }; +static pj_str_t CLOSED = { "closed", 6 }; +static pj_str_t EMPTY_STRING = { NULL, 0 }; + +static void xml_init_node(pj_pool_t *pool, pj_xml_node *node, + pj_str_t *name, const pj_str_t *value) +{ + pj_list_init(&node->attr_head); + pj_list_init(&node->node_head); + node->name = *name; + if (value) pj_strdup(pool, &node->content, value); + else node->content.ptr=NULL, node->content.slen=0; +} + +static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, + const pj_str_t *value) +{ + pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); + attr->name = *name; + pj_strdup(pool, &attr->value, value); + return attr; +} + +/* Presence */ +PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *entity) +{ + pj_xml_attr *attr; + + xml_init_node(pool, pres, &PRESENCE, NULL); + attr = xml_create_attr(pool, &ENTITY, entity); + pj_xml_add_attr(pres, attr); +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *id) +{ + pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t)); + pjpidf_tuple_construct(pool, t, id); + pj_xml_add_node(pres, t); + return t; +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres) +{ + return pj_xml_find_node(pres, &TUPLE); +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, + pjpidf_tuple *tuple) +{ + return pj_xml_find_next_node(pres, tuple, &TUPLE); +} + +static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id) +{ + return pj_xml_find_attr(node, &ID, id) != NULL; +} + +PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id) +{ + return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id); +} + +PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t) +{ + PJ_UNUSED_ARG(pres) + pj_list_erase(t); +} + +PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *text) +{ + pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); + xml_init_node(pool, note, &NOTE, text); + pj_xml_add_node(pres, note); + return note; +} + +PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres) +{ + return pj_xml_find_node( pres, &NOTE); +} + +PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note) +{ + return pj_xml_find_next_node(t, note, &NOTE); +} + + +/* Tuple */ +PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *id) +{ + pj_xml_attr *attr; + pjpidf_status *st; + + xml_init_node(pool, t, &TUPLE, NULL); + attr = xml_create_attr(pool, &ID, id); + pj_xml_add_attr(t, attr); + st = pj_pool_alloc(pool, sizeof(*st)); + pjpidf_status_construct(pool, st); + pj_xml_add_node(t, st); +} + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t) +{ + const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL); + pj_assert(attr); + return &attr->value; +} + +PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id) +{ + pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL); + pj_assert(attr); + pj_strdup(pool, &attr->value, id); +} + + +PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t) +{ + pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS); + pj_assert(st); + return st; +} + + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); + if (!node) + return &EMPTY_STRING; + return &node->content; +} + +PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *contact) +{ + pj_xml_node *node = pj_xml_find_node(t, &CONTACT); + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &CONTACT, contact); + pj_xml_add_node(t, node); + } else { + pj_strdup(pool, &node->content, contact); + } +} + +PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *prio) +{ + pj_xml_node *node = pj_xml_find_node(t, &CONTACT); + pj_xml_attr *attr; + + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &CONTACT, NULL); + pj_xml_add_node(t, node); + } + attr = pj_xml_find_attr(node, &PRIORITY, NULL); + if (!attr) { + attr = xml_create_attr(pool, &PRIORITY, prio); + pj_xml_add_attr(node, attr); + } else { + pj_strdup(pool, &attr->value, prio); + } +} + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); + pj_xml_attr *attr; + + if (!node) + return &EMPTY_STRING; + attr = pj_xml_find_attr(node, &PRIORITY, NULL); + if (!attr) + return &EMPTY_STRING; + return &attr->value; +} + + +PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *text) +{ + pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); + xml_init_node(pool, note, &NOTE, text); + pj_xml_add_node(t, note); + return note; +} + +PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t) +{ + return pj_xml_find_node(t, &NOTE); +} + +PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n) +{ + return pj_xml_find_next_node(t, n, &NOTE); +} + + +PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP); + return node ? &node->content : &EMPTY_STRING; +} + +PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *ts) +{ + pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &TIMESTAMP, ts); + } else { + pj_strdup(pool, &node->content, ts); + } +} + + +PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t, + pj_str_t *ts) +{ + pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); + if (!node) { + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &TIMESTAMP, ts); + } else { + node->content = *ts; + } +} + + +/* Status */ +PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st) +{ + pj_xml_node *node; + + xml_init_node(pool, st, &STATUS, NULL); + node = pj_pool_alloc(pool, sizeof(*node)); + xml_init_node(pool, node, &BASIC, &CLOSED); + pj_xml_add_node(st, node); +} + +PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st) +{ + pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC); + pj_assert(node != NULL); + return pj_stricmp(&node->content, &OPEN)==0; +} + +PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open) +{ + pj_xml_node *node = pj_xml_find_node(st, &BASIC); + pj_assert(node != NULL); + node->content = open ? OPEN : CLOSED; +} + +PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity) +{ + pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres)); + pjpidf_pres_construct(pool, pres, entity); + return pres; +} + +PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len) +{ + pjpidf_pres *pres = pj_xml_parse(pool, text, len); + if (pres) { + if (pj_stricmp(&pres->name, &PRESENCE) != 0) + return NULL; + } + return pres; +} + +PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len) +{ + return pj_xml_print(pres, buf, len, PJ_TRUE); +} + diff --git a/pjsip/src/pjsip_simple/pidf.h b/pjsip/src/pjsip_simple/pidf.h index bebe68e07..1a79f0c42 100644 --- a/pjsip/src/pjsip_simple/pidf.h +++ b/pjsip/src/pjsip_simple/pidf.h @@ -1,159 +1,159 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/pidf.h 3 8/24/05 10:33a Bennylp $ */ -#ifndef __PJSIP_SIMPLE_PIDF_H__ -#define __PJSIP_SIMPLE_PIDF_H__ - -/** - * @file pidf.h - * @brief PIDF/Presence Information Data Format (RFC 3863) - */ -#include -#include - -PJ_BEGIN_DECL - - -/** - * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863) - * @ingroup PJSIP_SIMPLE - * @{ - * - * This file provides tools for manipulating Presence Information Data - * Format (PIDF) as described in RFC 3863. - */ -typedef struct pj_xml_node pjpidf_pres; -typedef struct pj_xml_node pjpidf_tuple; -typedef struct pj_xml_node pjpidf_status; -typedef struct pj_xml_node pjpidf_note; - -typedef struct pjpidf_status_op -{ - void (*construct)(pj_pool_t*, pjpidf_status*); - pj_bool_t (*is_basic_open)(const pjpidf_status*); - void (*set_basic_open)(pjpidf_status*, pj_bool_t); -} pjpidf_status_op; - -typedef struct pjpidf_tuple_op -{ - void (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); - - const pj_str_t* (*get_id)(const pjpidf_tuple* ); - void (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*); - - pjpidf_status* (*get_status)(pjpidf_tuple* ); - - const pj_str_t* (*get_contact)(const pjpidf_tuple*); - void (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); - void (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); - const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*); - - pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); - pjpidf_note* (*get_first_note)(pjpidf_tuple*); - pjpidf_note* (*get_next_note)(pjpidf_tuple*, pjpidf_note*); - - const pj_str_t* (*get_timestamp)(const pjpidf_tuple*); - void (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); - void (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*); - -} pjpidf_tuple_op; - -typedef struct pjpidf_pres_op -{ - void (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); - - pjpidf_tuple* (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); - pjpidf_tuple* (*get_first_tuple)(pjpidf_pres*); - pjpidf_tuple* (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*); - pjpidf_tuple* (*find_tuple)(pjpidf_pres*, const pj_str_t*); - void (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*); - - pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); - pjpidf_note* (*get_first_note)(pjpidf_pres*); - pjpidf_note* (*get_next_note)(pjpidf_pres*, pjpidf_note*); - -} pjpidf_pres_op; - - -extern struct pjpidf_op_desc -{ - pjpidf_pres_op pres; - pjpidf_tuple_op tuple; - pjpidf_status_op status; -} pjpidf_op; - - -/****************************************************************************** - * Top level API for managing presence document. - *****************************************************************************/ -PJ_DECL(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity); -PJ_DECL(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len); -PJ_DECL(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len); - - -/****************************************************************************** - * API for managing Presence node. - *****************************************************************************/ -PJ_DECL(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *entity); -PJ_DECL(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *id); -PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres); -PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, - pjpidf_tuple *t); -PJ_DECL(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, - const pj_str_t *id); -PJ_DECL(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, - pjpidf_tuple*); - -PJ_DECL(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *text); -PJ_DECL(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres); -PJ_DECL(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*); - - -/****************************************************************************** - * API for managing Tuple node. - *****************************************************************************/ -PJ_DECL(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *id); -PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t ); -PJ_DECL(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *id); - -PJ_DECL(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t); - -PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t); -PJ_DECL(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *contact); -PJ_DECL(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *prio); -PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t); - -PJ_DECL(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *text); -PJ_DECL(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t); -PJ_DECL(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n); - -PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t); -PJ_DECL(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *ts); -PJ_DECL(void) pjpidf_tuple_set_timestamp_np( pj_pool_t*, pjpidf_tuple *t, - pj_str_t *ts); - - -/****************************************************************************** - * API for managing Status node. - *****************************************************************************/ -PJ_DECL(void) pjpidf_status_construct(pj_pool_t*, pjpidf_status*); -PJ_DECL(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status*); -PJ_DECL(void) pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t); - - -/** - * @} - */ - - -PJ_END_DECL - - -#endif /* __PJSIP_SIMPLE_PIDF_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip_simple/pidf.h 3 8/24/05 10:33a Bennylp $ */ +#ifndef __PJSIP_SIMPLE_PIDF_H__ +#define __PJSIP_SIMPLE_PIDF_H__ + +/** + * @file pidf.h + * @brief PIDF/Presence Information Data Format (RFC 3863) + */ +#include +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup PJSIP_SIMPLE_PIDF PIDF/Presence Information Data Format (RFC 3863) + * @ingroup PJSIP_SIMPLE + * @{ + * + * This file provides tools for manipulating Presence Information Data + * Format (PIDF) as described in RFC 3863. + */ +typedef struct pj_xml_node pjpidf_pres; +typedef struct pj_xml_node pjpidf_tuple; +typedef struct pj_xml_node pjpidf_status; +typedef struct pj_xml_node pjpidf_note; + +typedef struct pjpidf_status_op +{ + void (*construct)(pj_pool_t*, pjpidf_status*); + pj_bool_t (*is_basic_open)(const pjpidf_status*); + void (*set_basic_open)(pjpidf_status*, pj_bool_t); +} pjpidf_status_op; + +typedef struct pjpidf_tuple_op +{ + void (*construct)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + + const pj_str_t* (*get_id)(const pjpidf_tuple* ); + void (*set_id)(pj_pool_t*, pjpidf_tuple *, const pj_str_t*); + + pjpidf_status* (*get_status)(pjpidf_tuple* ); + + const pj_str_t* (*get_contact)(const pjpidf_tuple*); + void (*set_contact)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + void (*set_contact_prio)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + const pj_str_t* (*get_contact_prio)(const pjpidf_tuple*); + + pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + pjpidf_note* (*get_first_note)(pjpidf_tuple*); + pjpidf_note* (*get_next_note)(pjpidf_tuple*, pjpidf_note*); + + const pj_str_t* (*get_timestamp)(const pjpidf_tuple*); + void (*set_timestamp)(pj_pool_t*, pjpidf_tuple*, const pj_str_t*); + void (*set_timestamp_np)(pj_pool_t*,pjpidf_tuple*, pj_str_t*); + +} pjpidf_tuple_op; + +typedef struct pjpidf_pres_op +{ + void (*construct)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); + + pjpidf_tuple* (*add_tuple)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); + pjpidf_tuple* (*get_first_tuple)(pjpidf_pres*); + pjpidf_tuple* (*get_next_tuple)(pjpidf_pres*, pjpidf_tuple*); + pjpidf_tuple* (*find_tuple)(pjpidf_pres*, const pj_str_t*); + void (*remove_tuple)(pjpidf_pres*, pjpidf_tuple*); + + pjpidf_note* (*add_note)(pj_pool_t*, pjpidf_pres*, const pj_str_t*); + pjpidf_note* (*get_first_note)(pjpidf_pres*); + pjpidf_note* (*get_next_note)(pjpidf_pres*, pjpidf_note*); + +} pjpidf_pres_op; + + +extern struct pjpidf_op_desc +{ + pjpidf_pres_op pres; + pjpidf_tuple_op tuple; + pjpidf_status_op status; +} pjpidf_op; + + +/****************************************************************************** + * Top level API for managing presence document. + *****************************************************************************/ +PJ_DECL(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity); +PJ_DECL(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len); +PJ_DECL(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len); + + +/****************************************************************************** + * API for managing Presence node. + *****************************************************************************/ +PJ_DECL(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *entity); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *id); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, + pjpidf_tuple *t); +PJ_DECL(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, + const pj_str_t *id); +PJ_DECL(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, + pjpidf_tuple*); + +PJ_DECL(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, + const pj_str_t *text); +PJ_DECL(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres); +PJ_DECL(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres*, pjpidf_note*); + + +/****************************************************************************** + * API for managing Tuple node. + *****************************************************************************/ +PJ_DECL(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *id); +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t ); +PJ_DECL(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *id); + +PJ_DECL(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t); + +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t); +PJ_DECL(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *contact); +PJ_DECL(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *prio); +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t); + +PJ_DECL(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *text); +PJ_DECL(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t); +PJ_DECL(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n); + +PJ_DECL(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t); +PJ_DECL(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, + const pj_str_t *ts); +PJ_DECL(void) pjpidf_tuple_set_timestamp_np( pj_pool_t*, pjpidf_tuple *t, + pj_str_t *ts); + + +/****************************************************************************** + * API for managing Status node. + *****************************************************************************/ +PJ_DECL(void) pjpidf_status_construct(pj_pool_t*, pjpidf_status*); +PJ_DECL(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status*); +PJ_DECL(void) pjpidf_status_set_basic_open(pjpidf_status*, pj_bool_t); + + +/** + * @} + */ + + +PJ_END_DECL + + +#endif /* __PJSIP_SIMPLE_PIDF_H__ */ diff --git a/pjsip/src/pjsip_simple/presence.c b/pjsip/src/pjsip_simple/presence.c index 31ab5cdc0..6d46c4477 100644 --- a/pjsip/src/pjsip_simple/presence.c +++ b/pjsip/src/pjsip_simple/presence.c @@ -1,382 +1,382 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/presence.c 7 8/24/05 10:33a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include - -/* Forward declarations. */ -static void on_query_subscribe(pjsip_rx_data *rdata, int *status); -static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, - pjsip_event_sub_cb **cb, int *expires); -static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason); -static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata); -static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata); - -/* Some string constants. */ -static pj_str_t PRESENCE_EVENT = { "presence", 8 }; - -/* Accept types. */ -static pj_str_t accept_names[] = { - { "application/pidf+xml", 20 }, - { "application/xpidf+xml", 21 } -}; -static pjsip_media_type accept_types[] = { - { - { "application", 11 }, - { "pidf+xml", 8 } - }, - { - { "application", 11 }, - { "xpidf+xml", 9 } - } -}; - -/* Callback that is registered by application. */ -static pjsip_presence_cb cb; - -/* Package callback to be register to event_notify */ -static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe, - &on_subscribe }; - -/* Global/static callback to be registered to event_notify */ -static pjsip_event_sub_cb sub_cb = { &on_sub_terminated, - &on_sub_received_refresh, - NULL, - &on_received_notify, - NULL }; - -/* - * Initialize presence module. - * This will register event package "presence" to event framework. - */ -PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb) -{ - pj_memcpy(&cb, pcb, sizeof(*pcb)); - pjsip_event_sub_register_pkg( &PRESENCE_EVENT, - sizeof(accept_names)/sizeof(accept_names[0]), - accept_names, - &pkg_cb); -} - -/* - * Create presence subscription. - */ -PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, - const pj_str_t *local_url, - const pj_str_t *remote_url, - int expires, - void *user_data ) -{ - pjsip_event_sub *sub; - pjsip_presentity *pres; - - if (expires < 0) - expires = 300; - - /* Create event subscription */ - sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT, - expires, - sizeof(accept_names)/sizeof(accept_names[0]), - accept_names, - NULL, &sub_cb); - if (!sub) - return NULL; - - /* Allocate presence descriptor. */ - pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); - pres->sub = sub; - pres->user_data = user_data; - sub->user_data = pres; - - return pres; -} - -/* - * Send SUBSCRIBE. - */ -PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ) -{ - return pjsip_event_sub_subscribe( pres->sub ); -} - -/* - * Set credentials to be used for outgoing requests. - */ -PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, - int count, - const pjsip_cred_info cred[]) -{ - return pjsip_event_sub_set_credentials(pres->sub, count, cred); -} - -/* - * Set route-set. - */ -PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, - const pjsip_route_hdr *hdr ) -{ - return pjsip_event_sub_set_route_set( pres->sub, hdr ); -} - -/* - * Unsubscribe. - */ -PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ) -{ - return pjsip_event_sub_unsubscribe(pres->sub); -} - -/* - * This is the pjsip_msg_body callback to print XML body. - */ -static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size) -{ - return pj_xml_print( body->data, buf, size, PJ_TRUE ); -} - -/* - * Create and initialize PIDF document and msg body (notifier only). - */ -static pj_status_t init_presence_info( pjsip_presentity *pres ) -{ - pj_str_t uri; - pj_pool_t *pool = pres->sub->pool; - char tmp[PJSIP_MAX_URL_SIZE]; - pjpidf_tuple *tuple; - const pjsip_media_type *content_type = NULL; - - pj_assert(pres->uas_body == NULL); - - /* Make entity_id */ - uri.ptr = tmp; - uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri, - tmp, sizeof(tmp)); - if (uri.slen < 0) - return -1; - - if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { - pj_str_t s; - - /* Create . */ - pres->uas_data.pidf = pjpidf_create(pool, &s); - - /* Create */ - pj_create_unique_string(pool, &s); - tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s); - - /* Set */ - s.ptr = tmp; - s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp)); - if (s.slen < 0) - return -1; - pjpidf_tuple_set_contact(pool, tuple, &s); - - /* Content-Type */ - content_type = &accept_types[PJSIP_PRES_TYPE_PIDF]; - - } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { - - /* Create XPIDF */ - pres->uas_data.xpidf = pjxpidf_create(pool, &uri); - - /* Content-Type. */ - content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF]; - } - - /* Create message body */ - pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); - pres->uas_body->content_type = *content_type; - pres->uas_body->data = pres->uas_data.pidf; - pres->uas_body->len = 0; - pres->uas_body->print_body = &print_xml; - - return 0; -} - -/* - * Send NOTIFY and set subscription state. - */ -PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, - pjsip_event_sub_state state, - pj_bool_t is_online ) -{ - pj_str_t reason = { "", 0 }; - - if (pres->uas_data.pidf == NULL) { - if (init_presence_info(pres) != 0) - return -1; - } - - /* Update basic status in PIDF/XPIDF document. */ - if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { - pjpidf_tuple *first; - pjpidf_status *status; - pj_time_val now; - pj_parsed_time pnow; - - first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf); - pj_assert(first); - status = pjpidf_op.tuple.get_status(first); - pj_assert(status); - pjpidf_op.status.set_basic_open(status, is_online); - - /* Update timestamp. */ - if (pres->timestamp.ptr == 0) { - pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24); - } - pj_gettimeofday(&now); - pj_time_decode(&now, &pnow); - pres->timestamp.slen = sprintf(pres->timestamp.ptr, - "%04d-%02d-%02dT%02d:%02d:%02dZ", - pnow.year, pnow.mon, pnow.day, - pnow.hour, pnow.min, pnow.sec); - pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp); - - } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { - pjxpidf_set_status( pres->uas_data.xpidf, is_online ); - - } else { - pj_assert(0); - } - - /* Send notify. */ - return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body); -} - -/* - * Destroy subscription (can be called for both subscriber and notifier). - */ -PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ) -{ - return pjsip_event_sub_destroy(pres->sub); -} - -/* - * This callback is called by event framework to query whether we want to - * accept an incoming subscription. - */ -static void on_query_subscribe(pjsip_rx_data *rdata, int *status) -{ - if (cb.accept_presence) { - (*cb.accept_presence)(rdata, status); - } -} - -/* - * This callback is called by event framework after we accept the incoming - * subscription, to notify about the new subscription instance. - */ -static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, - pjsip_event_sub_cb **set_sub_cb, int *expires) -{ - pjsip_presentity *pres; - pjsip_accept_hdr *accept; - - pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); - pres->sub = sub; - pres->pres_type = PJSIP_PRES_TYPE_PIDF; - sub->user_data = pres; - *set_sub_cb = &sub_cb; - - accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); - if (accept) { - unsigned i; - int found = 0; - for (i=0; icount && !found; ++i) { - int j; - for (j=0; jvalues[i], &accept_names[j])) { - pres->pres_type = j; - found = 1; - break; - } - } - } - pj_assert(found ); - } - - (*cb.on_received_request)(pres, rdata, expires); -} - -/* - * This callback is called by event framework when the subscription is - * terminated. - */ -static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason) -{ - pjsip_presentity *pres = sub->user_data; - if (cb.on_terminated) - (*cb.on_terminated)(pres, reason); -} - -/* - * This callback is called by event framework when it receives incoming - * SUBSCRIBE request to refresh the subscription. - */ -static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata) -{ - pjsip_presentity *pres = sub->user_data; - if (cb.on_received_refresh) - (*cb.on_received_refresh)(pres, rdata); -} - -/* - * This callback is called by event framework when it receives incoming - * NOTIFY request. - */ -static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata) -{ - pjsip_presentity *pres = sub->user_data; - - if (cb.on_received_update) { - pj_status_t is_open; - pjsip_msg_body *body; - int i; - - body = rdata->msg->body; - if (!body) - return; - - for (i=0; icontent_type.type, &accept_types[i].type) && - !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype)) - { - break; - } - } - - if (i==PJSIP_PRES_TYPE_PIDF) { - pjpidf_pres *pres; - pjpidf_tuple *tuple; - pjpidf_status *status; - - pres = pjpidf_parse(rdata->pool, body->data, body->len); - if (!pres) - return; - tuple = pjpidf_pres_get_first_tuple(pres); - if (!tuple) - return; - status = pjpidf_tuple_get_status(tuple); - if (!status) - return; - is_open = pjpidf_status_is_basic_open(status); - - } else if (i==PJSIP_PRES_TYPE_XPIDF) { - pjxpidf_pres *pres; - - pres = pjxpidf_parse(rdata->pool, body->data, body->len); - if (!pres) - return; - is_open = pjxpidf_get_status(pres); - - } else { - return; - } - - (*cb.on_received_update)(pres, is_open); - } -} - +/* $Header: /pjproject/pjsip/src/pjsip_simple/presence.c 7 8/24/05 10:33a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include + +/* Forward declarations. */ +static void on_query_subscribe(pjsip_rx_data *rdata, int *status); +static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, + pjsip_event_sub_cb **cb, int *expires); +static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason); +static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata); +static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata); + +/* Some string constants. */ +static pj_str_t PRESENCE_EVENT = { "presence", 8 }; + +/* Accept types. */ +static pj_str_t accept_names[] = { + { "application/pidf+xml", 20 }, + { "application/xpidf+xml", 21 } +}; +static pjsip_media_type accept_types[] = { + { + { "application", 11 }, + { "pidf+xml", 8 } + }, + { + { "application", 11 }, + { "xpidf+xml", 9 } + } +}; + +/* Callback that is registered by application. */ +static pjsip_presence_cb cb; + +/* Package callback to be register to event_notify */ +static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe, + &on_subscribe }; + +/* Global/static callback to be registered to event_notify */ +static pjsip_event_sub_cb sub_cb = { &on_sub_terminated, + &on_sub_received_refresh, + NULL, + &on_received_notify, + NULL }; + +/* + * Initialize presence module. + * This will register event package "presence" to event framework. + */ +PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb) +{ + pj_memcpy(&cb, pcb, sizeof(*pcb)); + pjsip_event_sub_register_pkg( &PRESENCE_EVENT, + sizeof(accept_names)/sizeof(accept_names[0]), + accept_names, + &pkg_cb); +} + +/* + * Create presence subscription. + */ +PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, + const pj_str_t *local_url, + const pj_str_t *remote_url, + int expires, + void *user_data ) +{ + pjsip_event_sub *sub; + pjsip_presentity *pres; + + if (expires < 0) + expires = 300; + + /* Create event subscription */ + sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT, + expires, + sizeof(accept_names)/sizeof(accept_names[0]), + accept_names, + NULL, &sub_cb); + if (!sub) + return NULL; + + /* Allocate presence descriptor. */ + pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); + pres->sub = sub; + pres->user_data = user_data; + sub->user_data = pres; + + return pres; +} + +/* + * Send SUBSCRIBE. + */ +PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ) +{ + return pjsip_event_sub_subscribe( pres->sub ); +} + +/* + * Set credentials to be used for outgoing requests. + */ +PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, + int count, + const pjsip_cred_info cred[]) +{ + return pjsip_event_sub_set_credentials(pres->sub, count, cred); +} + +/* + * Set route-set. + */ +PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, + const pjsip_route_hdr *hdr ) +{ + return pjsip_event_sub_set_route_set( pres->sub, hdr ); +} + +/* + * Unsubscribe. + */ +PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ) +{ + return pjsip_event_sub_unsubscribe(pres->sub); +} + +/* + * This is the pjsip_msg_body callback to print XML body. + */ +static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size) +{ + return pj_xml_print( body->data, buf, size, PJ_TRUE ); +} + +/* + * Create and initialize PIDF document and msg body (notifier only). + */ +static pj_status_t init_presence_info( pjsip_presentity *pres ) +{ + pj_str_t uri; + pj_pool_t *pool = pres->sub->pool; + char tmp[PJSIP_MAX_URL_SIZE]; + pjpidf_tuple *tuple; + const pjsip_media_type *content_type = NULL; + + pj_assert(pres->uas_body == NULL); + + /* Make entity_id */ + uri.ptr = tmp; + uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri, + tmp, sizeof(tmp)); + if (uri.slen < 0) + return -1; + + if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { + pj_str_t s; + + /* Create . */ + pres->uas_data.pidf = pjpidf_create(pool, &s); + + /* Create */ + pj_create_unique_string(pool, &s); + tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s); + + /* Set */ + s.ptr = tmp; + s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp)); + if (s.slen < 0) + return -1; + pjpidf_tuple_set_contact(pool, tuple, &s); + + /* Content-Type */ + content_type = &accept_types[PJSIP_PRES_TYPE_PIDF]; + + } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { + + /* Create XPIDF */ + pres->uas_data.xpidf = pjxpidf_create(pool, &uri); + + /* Content-Type. */ + content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF]; + } + + /* Create message body */ + pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); + pres->uas_body->content_type = *content_type; + pres->uas_body->data = pres->uas_data.pidf; + pres->uas_body->len = 0; + pres->uas_body->print_body = &print_xml; + + return 0; +} + +/* + * Send NOTIFY and set subscription state. + */ +PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, + pjsip_event_sub_state state, + pj_bool_t is_online ) +{ + pj_str_t reason = { "", 0 }; + + if (pres->uas_data.pidf == NULL) { + if (init_presence_info(pres) != 0) + return -1; + } + + /* Update basic status in PIDF/XPIDF document. */ + if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { + pjpidf_tuple *first; + pjpidf_status *status; + pj_time_val now; + pj_parsed_time pnow; + + first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf); + pj_assert(first); + status = pjpidf_op.tuple.get_status(first); + pj_assert(status); + pjpidf_op.status.set_basic_open(status, is_online); + + /* Update timestamp. */ + if (pres->timestamp.ptr == 0) { + pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24); + } + pj_gettimeofday(&now); + pj_time_decode(&now, &pnow); + pres->timestamp.slen = sprintf(pres->timestamp.ptr, + "%04d-%02d-%02dT%02d:%02d:%02dZ", + pnow.year, pnow.mon, pnow.day, + pnow.hour, pnow.min, pnow.sec); + pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp); + + } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { + pjxpidf_set_status( pres->uas_data.xpidf, is_online ); + + } else { + pj_assert(0); + } + + /* Send notify. */ + return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body); +} + +/* + * Destroy subscription (can be called for both subscriber and notifier). + */ +PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ) +{ + return pjsip_event_sub_destroy(pres->sub); +} + +/* + * This callback is called by event framework to query whether we want to + * accept an incoming subscription. + */ +static void on_query_subscribe(pjsip_rx_data *rdata, int *status) +{ + if (cb.accept_presence) { + (*cb.accept_presence)(rdata, status); + } +} + +/* + * This callback is called by event framework after we accept the incoming + * subscription, to notify about the new subscription instance. + */ +static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, + pjsip_event_sub_cb **set_sub_cb, int *expires) +{ + pjsip_presentity *pres; + pjsip_accept_hdr *accept; + + pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); + pres->sub = sub; + pres->pres_type = PJSIP_PRES_TYPE_PIDF; + sub->user_data = pres; + *set_sub_cb = &sub_cb; + + accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); + if (accept) { + unsigned i; + int found = 0; + for (i=0; icount && !found; ++i) { + int j; + for (j=0; jvalues[i], &accept_names[j])) { + pres->pres_type = j; + found = 1; + break; + } + } + } + pj_assert(found ); + } + + (*cb.on_received_request)(pres, rdata, expires); +} + +/* + * This callback is called by event framework when the subscription is + * terminated. + */ +static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason) +{ + pjsip_presentity *pres = sub->user_data; + if (cb.on_terminated) + (*cb.on_terminated)(pres, reason); +} + +/* + * This callback is called by event framework when it receives incoming + * SUBSCRIBE request to refresh the subscription. + */ +static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata) +{ + pjsip_presentity *pres = sub->user_data; + if (cb.on_received_refresh) + (*cb.on_received_refresh)(pres, rdata); +} + +/* + * This callback is called by event framework when it receives incoming + * NOTIFY request. + */ +static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata) +{ + pjsip_presentity *pres = sub->user_data; + + if (cb.on_received_update) { + pj_status_t is_open; + pjsip_msg_body *body; + int i; + + body = rdata->msg->body; + if (!body) + return; + + for (i=0; icontent_type.type, &accept_types[i].type) && + !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype)) + { + break; + } + } + + if (i==PJSIP_PRES_TYPE_PIDF) { + pjpidf_pres *pres; + pjpidf_tuple *tuple; + pjpidf_status *status; + + pres = pjpidf_parse(rdata->pool, body->data, body->len); + if (!pres) + return; + tuple = pjpidf_pres_get_first_tuple(pres); + if (!tuple) + return; + status = pjpidf_tuple_get_status(tuple); + if (!status) + return; + is_open = pjpidf_status_is_basic_open(status); + + } else if (i==PJSIP_PRES_TYPE_XPIDF) { + pjxpidf_pres *pres; + + pres = pjxpidf_parse(rdata->pool, body->data, body->len); + if (!pres) + return; + is_open = pjxpidf_get_status(pres); + + } else { + return; + } + + (*cb.on_received_update)(pres, is_open); + } +} + diff --git a/pjsip/src/pjsip_simple/presence.h b/pjsip/src/pjsip_simple/presence.h index f9cc838ca..3c69d356c 100644 --- a/pjsip/src/pjsip_simple/presence.h +++ b/pjsip/src/pjsip_simple/presence.h @@ -1,212 +1,212 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/presence.h 6 8/24/05 10:33a Bennylp $ */ -#ifndef __PJSIP_SIMPLE_PRESENCE_H__ -#define __PJSIP_SIMPLE_PRESENCE_H__ - -/** - * @file presence.h - * @brief SIP Extension for Presence (RFC 3856) - */ -#include -#include -#include - - -PJ_BEGIN_DECL - - -/** - * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856) - * @ingroup PJSIP_SIMPLE - * @{ - * - * This module contains the implementation of SIP Presence Extension as - * described in RFC 3856. It uses the SIP Event Notification framework - * (event_notify.h) and extends the framework by implementing "presence" - * event package. - */ - -/** - * Presence message body type. - */ -typedef enum pjsip_pres_type -{ - PJSIP_PRES_TYPE_PIDF, - PJSIP_PRES_TYPE_XPIDF, -} pjsip_pres_type; - -/** - * This structure describe a presentity, for both subscriber and notifier. - */ -typedef struct pjsip_presentity -{ - pjsip_event_sub *sub; /**< Event subscribtion record. */ - pjsip_pres_type pres_type; /**< Presentity type. */ - pjsip_msg_body *uas_body; /**< Message body (UAS only). */ - union { - pjpidf_pres *pidf; - pjxpidf_pres *xpidf; - } uas_data; /**< UAS data. */ - pj_str_t timestamp; /**< Time of last update. */ - void *user_data; /**< Application data. */ -} pjsip_presentity; - - -/** - * This structure describe callback that is registered to receive notification - * from the presence module. - */ -typedef struct pjsip_presence_cb -{ - /** - * This callback is first called when the module receives incoming - * SUBSCRIBE request to determine whether application wants to accept - * the request. If it does, then on_presence_request will be called. - * - * @param rdata The received message. - * @return Application should return 2xx to accept the request, - * or failure status (>=300) to reject the request. - */ - void (*accept_presence)(pjsip_rx_data *rdata, int *status); - - /** - * This callback is called when the module receive the first presence - * subscription request. - * - * @param pres The presence descriptor. - * @param rdata The incoming request. - * @param timeout Timeout to be set for incoming request. Otherwise - * app can just leave this and accept the default. - */ - void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata, - int *timeout); - - /** - * This callback is called when the module received subscription refresh - * request. - * - * @param pres The presence descriptor. - * @param rdata The incoming request. - */ - void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata); - - /** - * This callback is called when the module receives incoming NOTIFY - * request. - * - * @param pres The presence descriptor. - * @param open The latest status of the presentity. - */ - void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open); - - /** - * This callback is called when the subscription has terminated. - * - * @param sub The subscription instance. - * @param reason The termination reason. - */ - void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason); - -} pjsip_presence_cb; - - -/** - * Initialize the presence module and register callback. - * - * @param cb Callback structure. - */ -PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb); - - -/** - * Create to presence subscription of a presentity URL. - * - * @param endpt Endpoint instance. - * @param local_url Local URL. - * @param remote_url Remote URL which the presence is being subscribed. - * @param expires The expiration. - * @param user_data User data to attach to presence subscription. - * - * @return The presence structure if successfull, or NULL if - * failed. - */ -PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, - const pj_str_t *local_url, - const pj_str_t *remote_url, - int expires, - void *user_data ); - -/** - * Set credentials to be used by this presentity for outgoing requests. - * - * @param pres Presentity instance. - * @param count Number of credentials in the array. - * @param cred Array of credentials. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, - int count, - const pjsip_cred_info cred[]); - -/** - * Set route set for outgoing requests. - * - * @param pres Presentity instance. - * @param route_set List of route headers. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, - const pjsip_route_hdr *hdr ); - -/** - * Send SUBSCRIBE request for the specified presentity. - * - * @param pres The presentity instance. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ); - -/** - * Ceased the presence subscription. - * - * @param pres The presence structure. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ); - -/** - * Notify subscriber about change in local status. - * - * @param pres The presence structure. - * @param state Set the state of the subscription. - * @param open Set the presence status (open or closed). - * - * @return Zero if a NOTIFY request can be sent. - */ -PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, - pjsip_event_sub_state state, - pj_bool_t open ); - -/** - * Destroy presence structure and the underlying subscription. - * - * @param pres The presence structure. - * - * @return Zero if the subscription was destroyed, or one if - * the subscription can not be destroyed immediately - * and will be destroyed later, or -1 if failed. - */ -PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ); - - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJSIP_SIMPLE_PRESENCE_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip_simple/presence.h 6 8/24/05 10:33a Bennylp $ */ +#ifndef __PJSIP_SIMPLE_PRESENCE_H__ +#define __PJSIP_SIMPLE_PRESENCE_H__ + +/** + * @file presence.h + * @brief SIP Extension for Presence (RFC 3856) + */ +#include +#include +#include + + +PJ_BEGIN_DECL + + +/** + * @defgroup PJSIP_SIMPLE_PRES SIP Extension for Presence (RFC 3856) + * @ingroup PJSIP_SIMPLE + * @{ + * + * This module contains the implementation of SIP Presence Extension as + * described in RFC 3856. It uses the SIP Event Notification framework + * (event_notify.h) and extends the framework by implementing "presence" + * event package. + */ + +/** + * Presence message body type. + */ +typedef enum pjsip_pres_type +{ + PJSIP_PRES_TYPE_PIDF, + PJSIP_PRES_TYPE_XPIDF, +} pjsip_pres_type; + +/** + * This structure describe a presentity, for both subscriber and notifier. + */ +typedef struct pjsip_presentity +{ + pjsip_event_sub *sub; /**< Event subscribtion record. */ + pjsip_pres_type pres_type; /**< Presentity type. */ + pjsip_msg_body *uas_body; /**< Message body (UAS only). */ + union { + pjpidf_pres *pidf; + pjxpidf_pres *xpidf; + } uas_data; /**< UAS data. */ + pj_str_t timestamp; /**< Time of last update. */ + void *user_data; /**< Application data. */ +} pjsip_presentity; + + +/** + * This structure describe callback that is registered to receive notification + * from the presence module. + */ +typedef struct pjsip_presence_cb +{ + /** + * This callback is first called when the module receives incoming + * SUBSCRIBE request to determine whether application wants to accept + * the request. If it does, then on_presence_request will be called. + * + * @param rdata The received message. + * @return Application should return 2xx to accept the request, + * or failure status (>=300) to reject the request. + */ + void (*accept_presence)(pjsip_rx_data *rdata, int *status); + + /** + * This callback is called when the module receive the first presence + * subscription request. + * + * @param pres The presence descriptor. + * @param rdata The incoming request. + * @param timeout Timeout to be set for incoming request. Otherwise + * app can just leave this and accept the default. + */ + void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata, + int *timeout); + + /** + * This callback is called when the module received subscription refresh + * request. + * + * @param pres The presence descriptor. + * @param rdata The incoming request. + */ + void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata); + + /** + * This callback is called when the module receives incoming NOTIFY + * request. + * + * @param pres The presence descriptor. + * @param open The latest status of the presentity. + */ + void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open); + + /** + * This callback is called when the subscription has terminated. + * + * @param sub The subscription instance. + * @param reason The termination reason. + */ + void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason); + +} pjsip_presence_cb; + + +/** + * Initialize the presence module and register callback. + * + * @param cb Callback structure. + */ +PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb); + + +/** + * Create to presence subscription of a presentity URL. + * + * @param endpt Endpoint instance. + * @param local_url Local URL. + * @param remote_url Remote URL which the presence is being subscribed. + * @param expires The expiration. + * @param user_data User data to attach to presence subscription. + * + * @return The presence structure if successfull, or NULL if + * failed. + */ +PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, + const pj_str_t *local_url, + const pj_str_t *remote_url, + int expires, + void *user_data ); + +/** + * Set credentials to be used by this presentity for outgoing requests. + * + * @param pres Presentity instance. + * @param count Number of credentials in the array. + * @param cred Array of credentials. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, + int count, + const pjsip_cred_info cred[]); + +/** + * Set route set for outgoing requests. + * + * @param pres Presentity instance. + * @param route_set List of route headers. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, + const pjsip_route_hdr *hdr ); + +/** + * Send SUBSCRIBE request for the specified presentity. + * + * @param pres The presentity instance. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ); + +/** + * Ceased the presence subscription. + * + * @param pres The presence structure. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ); + +/** + * Notify subscriber about change in local status. + * + * @param pres The presence structure. + * @param state Set the state of the subscription. + * @param open Set the presence status (open or closed). + * + * @return Zero if a NOTIFY request can be sent. + */ +PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, + pjsip_event_sub_state state, + pj_bool_t open ); + +/** + * Destroy presence structure and the underlying subscription. + * + * @param pres The presence structure. + * + * @return Zero if the subscription was destroyed, or one if + * the subscription can not be destroyed immediately + * and will be destroyed later, or -1 if failed. + */ +PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ); + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJSIP_SIMPLE_PRESENCE_H__ */ diff --git a/pjsip/src/pjsip_simple/xpidf.c b/pjsip/src/pjsip_simple/xpidf.c index 5241d9a3a..971a44fde 100644 --- a/pjsip/src/pjsip_simple/xpidf.c +++ b/pjsip/src/pjsip_simple/xpidf.c @@ -1,277 +1,277 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/xpidf.c 3 6/22/05 11:42p Bennylp $ */ -#include -#include -#include -#include - -static pj_str_t PRESENCE = { "presence", 8 }; -static pj_str_t STATUS = { "status", 6 }; -static pj_str_t OPEN = { "open", 4 }; -static pj_str_t CLOSED = { "closed", 6 }; -static pj_str_t URI = { "uri", 3 }; -static pj_str_t ATOM = { "atom", 4 }; -static pj_str_t ATOMID = { "atomid", 6 }; -static pj_str_t ADDRESS = { "address", 7 }; -static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 }; -static pj_str_t PRESENTITY = { "presentity", 10 }; -static pj_str_t EMPTY_STRING = { NULL, 0 }; - -static pj_xml_node* xml_create_node(pj_pool_t *pool, - pj_str_t *name, const pj_str_t *value) -{ - pj_xml_node *node; - - node = pj_pool_alloc(pool, sizeof(pj_xml_node)); - pj_list_init(&node->attr_head); - pj_list_init(&node->node_head); - node->name = *name; - if (value) pj_strdup(pool, &node->content, value); - else node->content.ptr=NULL, node->content.slen=0; - - return node; -} - -static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, - const pj_str_t *value) -{ - pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); - attr->name = *name; - pj_strdup(pool, &attr->value, value); - return attr; -} - - -PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr) -{ - pjxpidf_pres *pres; - pj_xml_node *presentity; - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_node *status; - pj_xml_attr *attr; - pj_str_t uri; - pj_str_t tmp; - - /* */ - pres = xml_create_node(pool, &PRESENCE, NULL); - - /* */ - presentity = xml_create_node(pool, &PRESENTITY, NULL); - pj_xml_add_node(pres, presentity); - - /* uri attribute */ - uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen); - pj_strcpy( &uri, uri_cstr); - pj_strcat( &uri, &SUBSCRIBE_PARAM); - attr = xml_create_attr(pool, &URI, &uri); - pj_xml_add_attr(presentity, attr); - - /* */ - atom = xml_create_node(pool, &ATOM, NULL); - pj_xml_add_node(pres, atom); - - /* atom id */ - pj_create_unique_string(pool, &tmp); - attr = xml_create_attr(pool, &ATOMID, &tmp); - pj_xml_add_attr(atom, attr); - - /* address */ - addr = xml_create_node(pool, &ADDRESS, NULL); - pj_xml_add_node(atom, addr); - - /* address'es uri */ - attr = xml_create_attr(pool, &URI, uri_cstr); - pj_xml_add_attr(addr, attr); - - /* status */ - status = xml_create_node(pool, &STATUS, NULL); - pj_xml_add_node(addr, status); - - /* status attr */ - attr = xml_create_attr(pool, &STATUS, &OPEN); - pj_xml_add_attr(status, attr); - - return pres; -} - - - -PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len) -{ - pjxpidf_pres *pres; - pj_xml_node *node; - - pres = pj_xml_parse(pool, text, len); - if (!pres) - return NULL; - - /* Validate */ - if (pj_stricmp(&pres->name, &PRESENCE) != 0) - return NULL; - if (pj_xml_find_attr(pres, &URI, NULL) == NULL) - return NULL; - - /* Validate */ - node = pj_xml_find_node(pres, &PRESENTITY); - if (node == NULL) - return NULL; - - /* Validate */ - node = pj_xml_find_node(pres, &ATOM); - if (node == NULL) - return NULL; - if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL) - return NULL; - - /* Address */ - node = pj_xml_find_node(node, &ADDRESS); - if (node == NULL) - return NULL; - if (pj_xml_find_attr(node, &URI, NULL) == NULL) - return NULL; - - - /* Status */ - node = pj_xml_find_node(node, &STATUS); - if (node == NULL) - return NULL; - if (pj_xml_find_attr(node, &STATUS, NULL) == NULL) - return NULL; - - return pres; -} - - -PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len) -{ - return pj_xml_print(pres, text, len, PJ_TRUE); -} - - -PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres) -{ - pj_xml_node *presentity; - pj_xml_attr *attr; - - presentity = pj_xml_find_node(pres, &PRESENTITY); - if (!presentity) - return &EMPTY_STRING; - - attr = pj_xml_find_attr(presentity, &URI, NULL); - if (!attr) - return &EMPTY_STRING; - - return &attr->value; -} - - -PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, - const pj_str_t *uri) -{ - pj_xml_node *presentity; - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_attr *attr; - pj_str_t dup_uri; - - presentity = pj_xml_find_node(pres, &PRESENTITY); - if (!presentity) { - pj_assert(0); - return -1; - } - atom = pj_xml_find_node(pres, &ATOM); - if (!atom) { - pj_assert(0); - return -1; - } - addr = pj_xml_find_node(atom, &ADDRESS); - if (!addr) { - pj_assert(0); - return -1; - } - - /* Set uri in presentity */ - attr = pj_xml_find_attr(presentity, &URI, NULL); - if (!attr) { - pj_assert(0); - return -1; - } - pj_strdup(pool, &dup_uri, uri); - attr->value = dup_uri; - - /* Set uri in address. */ - attr = pj_xml_find_attr(addr, &URI, NULL); - if (!attr) { - pj_assert(0); - return -1; - } - attr->value = dup_uri; - - return 0; -} - - -PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres) -{ - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_node *status; - pj_xml_attr *attr; - - atom = pj_xml_find_node(pres, &ATOM); - if (!atom) { - pj_assert(0); - return PJ_FALSE; - } - addr = pj_xml_find_node(atom, &ADDRESS); - if (!addr) { - pj_assert(0); - return PJ_FALSE; - } - status = pj_xml_find_node(atom, &STATUS); - if (!status) { - pj_assert(0); - return PJ_FALSE; - } - attr = pj_xml_find_attr(status, &STATUS, NULL); - if (!attr) { - pj_assert(0); - return PJ_FALSE; - } - - return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE; -} - - -PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status) -{ - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_node *status; - pj_xml_attr *attr; - - atom = pj_xml_find_node(pres, &ATOM); - if (!atom) { - pj_assert(0); - return -1; - } - addr = pj_xml_find_node(atom, &ADDRESS); - if (!addr) { - pj_assert(0); - return -1; - } - status = pj_xml_find_node(addr, &STATUS); - if (!status) { - pj_assert(0); - return -1; - } - attr = pj_xml_find_attr(status, &STATUS, NULL); - if (!attr) { - pj_assert(0); - return -1; - } - - attr->value = ( online_status ? OPEN : CLOSED ); - return 0; -} - +/* $Header: /pjproject/pjsip/src/pjsip_simple/xpidf.c 3 6/22/05 11:42p Bennylp $ */ +#include +#include +#include +#include + +static pj_str_t PRESENCE = { "presence", 8 }; +static pj_str_t STATUS = { "status", 6 }; +static pj_str_t OPEN = { "open", 4 }; +static pj_str_t CLOSED = { "closed", 6 }; +static pj_str_t URI = { "uri", 3 }; +static pj_str_t ATOM = { "atom", 4 }; +static pj_str_t ATOMID = { "atomid", 6 }; +static pj_str_t ADDRESS = { "address", 7 }; +static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 }; +static pj_str_t PRESENTITY = { "presentity", 10 }; +static pj_str_t EMPTY_STRING = { NULL, 0 }; + +static pj_xml_node* xml_create_node(pj_pool_t *pool, + pj_str_t *name, const pj_str_t *value) +{ + pj_xml_node *node; + + node = pj_pool_alloc(pool, sizeof(pj_xml_node)); + pj_list_init(&node->attr_head); + pj_list_init(&node->node_head); + node->name = *name; + if (value) pj_strdup(pool, &node->content, value); + else node->content.ptr=NULL, node->content.slen=0; + + return node; +} + +static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, + const pj_str_t *value) +{ + pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); + attr->name = *name; + pj_strdup(pool, &attr->value, value); + return attr; +} + + +PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr) +{ + pjxpidf_pres *pres; + pj_xml_node *presentity; + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_node *status; + pj_xml_attr *attr; + pj_str_t uri; + pj_str_t tmp; + + /* */ + pres = xml_create_node(pool, &PRESENCE, NULL); + + /* */ + presentity = xml_create_node(pool, &PRESENTITY, NULL); + pj_xml_add_node(pres, presentity); + + /* uri attribute */ + uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen); + pj_strcpy( &uri, uri_cstr); + pj_strcat( &uri, &SUBSCRIBE_PARAM); + attr = xml_create_attr(pool, &URI, &uri); + pj_xml_add_attr(presentity, attr); + + /* */ + atom = xml_create_node(pool, &ATOM, NULL); + pj_xml_add_node(pres, atom); + + /* atom id */ + pj_create_unique_string(pool, &tmp); + attr = xml_create_attr(pool, &ATOMID, &tmp); + pj_xml_add_attr(atom, attr); + + /* address */ + addr = xml_create_node(pool, &ADDRESS, NULL); + pj_xml_add_node(atom, addr); + + /* address'es uri */ + attr = xml_create_attr(pool, &URI, uri_cstr); + pj_xml_add_attr(addr, attr); + + /* status */ + status = xml_create_node(pool, &STATUS, NULL); + pj_xml_add_node(addr, status); + + /* status attr */ + attr = xml_create_attr(pool, &STATUS, &OPEN); + pj_xml_add_attr(status, attr); + + return pres; +} + + + +PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len) +{ + pjxpidf_pres *pres; + pj_xml_node *node; + + pres = pj_xml_parse(pool, text, len); + if (!pres) + return NULL; + + /* Validate */ + if (pj_stricmp(&pres->name, &PRESENCE) != 0) + return NULL; + if (pj_xml_find_attr(pres, &URI, NULL) == NULL) + return NULL; + + /* Validate */ + node = pj_xml_find_node(pres, &PRESENTITY); + if (node == NULL) + return NULL; + + /* Validate */ + node = pj_xml_find_node(pres, &ATOM); + if (node == NULL) + return NULL; + if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL) + return NULL; + + /* Address */ + node = pj_xml_find_node(node, &ADDRESS); + if (node == NULL) + return NULL; + if (pj_xml_find_attr(node, &URI, NULL) == NULL) + return NULL; + + + /* Status */ + node = pj_xml_find_node(node, &STATUS); + if (node == NULL) + return NULL; + if (pj_xml_find_attr(node, &STATUS, NULL) == NULL) + return NULL; + + return pres; +} + + +PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len) +{ + return pj_xml_print(pres, text, len, PJ_TRUE); +} + + +PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres) +{ + pj_xml_node *presentity; + pj_xml_attr *attr; + + presentity = pj_xml_find_node(pres, &PRESENTITY); + if (!presentity) + return &EMPTY_STRING; + + attr = pj_xml_find_attr(presentity, &URI, NULL); + if (!attr) + return &EMPTY_STRING; + + return &attr->value; +} + + +PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, + const pj_str_t *uri) +{ + pj_xml_node *presentity; + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_attr *attr; + pj_str_t dup_uri; + + presentity = pj_xml_find_node(pres, &PRESENTITY); + if (!presentity) { + pj_assert(0); + return -1; + } + atom = pj_xml_find_node(pres, &ATOM); + if (!atom) { + pj_assert(0); + return -1; + } + addr = pj_xml_find_node(atom, &ADDRESS); + if (!addr) { + pj_assert(0); + return -1; + } + + /* Set uri in presentity */ + attr = pj_xml_find_attr(presentity, &URI, NULL); + if (!attr) { + pj_assert(0); + return -1; + } + pj_strdup(pool, &dup_uri, uri); + attr->value = dup_uri; + + /* Set uri in address. */ + attr = pj_xml_find_attr(addr, &URI, NULL); + if (!attr) { + pj_assert(0); + return -1; + } + attr->value = dup_uri; + + return 0; +} + + +PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres) +{ + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_node *status; + pj_xml_attr *attr; + + atom = pj_xml_find_node(pres, &ATOM); + if (!atom) { + pj_assert(0); + return PJ_FALSE; + } + addr = pj_xml_find_node(atom, &ADDRESS); + if (!addr) { + pj_assert(0); + return PJ_FALSE; + } + status = pj_xml_find_node(atom, &STATUS); + if (!status) { + pj_assert(0); + return PJ_FALSE; + } + attr = pj_xml_find_attr(status, &STATUS, NULL); + if (!attr) { + pj_assert(0); + return PJ_FALSE; + } + + return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE; +} + + +PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status) +{ + pj_xml_node *atom; + pj_xml_node *addr; + pj_xml_node *status; + pj_xml_attr *attr; + + atom = pj_xml_find_node(pres, &ATOM); + if (!atom) { + pj_assert(0); + return -1; + } + addr = pj_xml_find_node(atom, &ADDRESS); + if (!addr) { + pj_assert(0); + return -1; + } + status = pj_xml_find_node(addr, &STATUS); + if (!status) { + pj_assert(0); + return -1; + } + attr = pj_xml_find_attr(status, &STATUS, NULL); + if (!attr) { + pj_assert(0); + return -1; + } + + attr->value = ( online_status ? OPEN : CLOSED ); + return 0; +} + diff --git a/pjsip/src/pjsip_simple/xpidf.h b/pjsip/src/pjsip_simple/xpidf.h index a0a56aa32..1ae85f3aa 100644 --- a/pjsip/src/pjsip_simple/xpidf.h +++ b/pjsip/src/pjsip_simple/xpidf.h @@ -1,116 +1,116 @@ -/* $Header: /pjproject/pjsip/src/pjsip_simple/xpidf.h 3 8/24/05 10:33a Bennylp $ */ -#ifndef __PJSIP_SIMPLE_XPIDF_H__ -#define __PJSIP_SIMPLE_XPIDF_H__ - -/** - * @file xpidf.h - * @brief XPIDF/Presence Information Data Format - */ -#include -#include - -PJ_BEGIN_DECL - -/** - * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format - * @ingroup PJSIP_SIMPLE - * @{ - * - * This is an old presence data format as described in: - * draft-rosenberg-impp-pidf-00.txt. - * - * We won't support this format extensively here, as it seems there's not - * too many implementations support this anymore, as it shouldn't. - */ - -/** Type definitions for XPIDF root document. */ -typedef pj_xml_node pjxpidf_pres; - - -/** - * Create a new XPIDF document. - * - * @param pool Pool. - * @param uri URI to set in the XPIDF document. - * - * @return XPIDF document. - */ -PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri); - - -/** - * Parse XPIDF document. - * - * @param pool Pool. - * @param text Input text. - * @param len Length of input text. - * - * @return XPIDF document. - */ -PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len); - - -/** - * Print XPIDF document. - * - * @param pres The XPIDF document to print. - * @param text Buffer to place the output. - * @param len Length of the buffer. - * - * @return The length printed. - */ -PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len); - - -/** - * Get URI in the XPIDF document - * - * @param pres XPIDF document - * - * @return The URI, or an empty string. - */ -PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres); - - -/** - * Set the URI of the XPIDF document. - * - * @param pool Pool. - * @param pres The XPIDF document. - * @param uri URI to set in the XPIDF document. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, - const pj_str_t *uri); - - -/** - * Get presence status in the XPIDF document. - * - * @param pres XPIDF document. - * - * @return True to indicate the contact is online. - */ -PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres); - - -/** - * Set presence status in the XPIDF document. - * - * @param pres XPIDF document. - * @param status Status to set, True for online, False for offline. - * - * @return Zero on success. - */ -PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status); - - -/** - * @} - */ - -PJ_END_DECL - - -#endif /* __PJSIP_SIMPLE_XPIDF_H__ */ +/* $Header: /pjproject/pjsip/src/pjsip_simple/xpidf.h 3 8/24/05 10:33a Bennylp $ */ +#ifndef __PJSIP_SIMPLE_XPIDF_H__ +#define __PJSIP_SIMPLE_XPIDF_H__ + +/** + * @file xpidf.h + * @brief XPIDF/Presence Information Data Format + */ +#include +#include + +PJ_BEGIN_DECL + +/** + * @defgroup PJSIP_SIMPLE_XPIDF XPIDF/Presence Information Data Format + * @ingroup PJSIP_SIMPLE + * @{ + * + * This is an old presence data format as described in: + * draft-rosenberg-impp-pidf-00.txt. + * + * We won't support this format extensively here, as it seems there's not + * too many implementations support this anymore, as it shouldn't. + */ + +/** Type definitions for XPIDF root document. */ +typedef pj_xml_node pjxpidf_pres; + + +/** + * Create a new XPIDF document. + * + * @param pool Pool. + * @param uri URI to set in the XPIDF document. + * + * @return XPIDF document. + */ +PJ_DECL(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri); + + +/** + * Parse XPIDF document. + * + * @param pool Pool. + * @param text Input text. + * @param len Length of input text. + * + * @return XPIDF document. + */ +PJ_DECL(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len); + + +/** + * Print XPIDF document. + * + * @param pres The XPIDF document to print. + * @param text Buffer to place the output. + * @param len Length of the buffer. + * + * @return The length printed. + */ +PJ_DECL(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len); + + +/** + * Get URI in the XPIDF document + * + * @param pres XPIDF document + * + * @return The URI, or an empty string. + */ +PJ_DECL(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres); + + +/** + * Set the URI of the XPIDF document. + * + * @param pool Pool. + * @param pres The XPIDF document. + * @param uri URI to set in the XPIDF document. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, + const pj_str_t *uri); + + +/** + * Get presence status in the XPIDF document. + * + * @param pres XPIDF document. + * + * @return True to indicate the contact is online. + */ +PJ_DECL(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres); + + +/** + * Set presence status in the XPIDF document. + * + * @param pres XPIDF document. + * @param status Status to set, True for online, False for offline. + * + * @return Zero on success. + */ +PJ_DECL(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t status); + + +/** + * @} + */ + +PJ_END_DECL + + +#endif /* __PJSIP_SIMPLE_XPIDF_H__ */ diff --git a/pjsip/src/pjsip_ua.h b/pjsip/src/pjsip_ua.h index c93bb0c3d..24b2e984c 100644 --- a/pjsip/src/pjsip_ua.h +++ b/pjsip/src/pjsip_ua.h @@ -1,11 +1,11 @@ -/* $Header: /pjproject/pjsip/src/pjsip_ua.h 1 4/17/05 11:59a Bennylp $ */ - -#ifndef __PJSIP_UA_H__ -#define __PJSIP_UA_H__ - -#include -#include -#include - -#endif /* __PJSIP_UA_H__ */ - +/* $Header: /pjproject/pjsip/src/pjsip_ua.h 1 4/17/05 11:59a Bennylp $ */ + +#ifndef __PJSIP_UA_H__ +#define __PJSIP_UA_H__ + +#include +#include +#include + +#endif /* __PJSIP_UA_H__ */ + diff --git a/pjsip/src/pjsua/getopt.c b/pjsip/src/pjsua/getopt.c index e4f023732..512d057ec 100644 --- a/pjsip/src/pjsua/getopt.c +++ b/pjsip/src/pjsua/getopt.c @@ -1,1044 +1,1044 @@ -/* $Header: /pjproject/pjsip/src/pjsua/getopt.c 4 5/14/05 12:24a Bennylp $ */ - -#ifdef _MSC_VER -/* in VC this file will generate a lot of warning about old style function - * declarations. - */ -# pragma warning(push, 3) -#endif - -/* - * getopt entry points - * - * modified by Mike Borella - * - * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $ - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef HAVE_GETOPT_LONG - -/* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#include "getopt.h" - - -#include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 -#include -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#include -#endif - -#ifndef NULL -#define NULL 0 -#endif - -int -getopt_long (int argc, char *const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 0); -} - -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char *const *argv; - const char *options; - const struct option *long_options; - int *opt_index; -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 1); -} - -int -getopt (int argc, char * const * argv, const char * optstring) -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0); -} - -#endif /* Not ELIDE_CODE. */ - - -#ifndef _NO_PROTO -#define _NO_PROTO -#endif - - -//#include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 -#include -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -#include -#include -#endif /* GNU C library. */ - -#ifdef VMS -#include -#if HAVE_STRING_H - 0 -#include -#endif -#endif - -#if defined (WIN32) && !defined (__CYGWIN32__) -/* It's not Unix, really. See? Capital letters. */ -#include -#define getpid() GetCurrentProcessId() -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. - When compiling libc, the _ macro is predefined. */ -#ifdef HAVE_LIBINTL_H -# include -# define _(msgid) gettext (msgid) -#else -# define _(msgid) (msgid) -#endif -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = NULL; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Formerly, initialization of getopt depended on optind==0, which - causes problems with re-calling getopt as programs generally don't - know that. */ - -int __getopt_initialized = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; - -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -#include -#define my_index strchr -#else - -static char * -my_index (const char *str, int chr) -{ - while (*str) - { - if (*str == chr) - return (char *) str; - str++; - } - return 0; -} - -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ -#ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -#if !defined (__STDC__) || !__STDC__ -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ -extern int strlen (const char *); -#endif /* not __STDC__ */ -#endif /* __GNUC__ */ - -#endif /* not __GNU_LIBRARY__ */ - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -#ifdef _LIBC -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; - -static int nonoption_flags_max_len; -static int nonoption_flags_len; - -static int original_argc; -static char *const *original_argv; - -extern pid_t __libc_pid; - -/* Make sure the environment variable bash 2.0 puts in the environment - is valid for the getopt call we must make sure that the ARGV passed - to getopt is that one passed to the process. */ -static void -__attribute__ ((unused)) -store_args_and_env (int argc, char *const *argv) -{ - /* XXX This is no good solution. We should rather copy the args so - that we can compare them later. But we must not use malloc(3). */ - original_argc = argc; - original_argv = argv; -} -text_set_element (__libc_subinit, store_args_and_env); - -# define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -#if defined (__STDC__) && __STDC__ -static void exchange (char **); -#endif - -static void -exchange (argv) - char **argv; -{ - int bottom = first_nonopt; - int middle = last_nonopt; - int top = optind; - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - -#ifdef _LIBC - /* First make sure the handling of the `__getopt_nonoption_flags' - string can work normally. Our top argument must be in the range - of the string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) - { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc (top + 1); - if (new_str == NULL) - nonoption_flags_len = nonoption_flags_max_len = 0; - else - { - memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); - memset (&new_str[nonoption_flags_max_len], '\0', - top + 1 - nonoption_flags_max_len); - nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Initialize the internal data when the first call is made. */ - -#if defined (__STDC__) && __STDC__ -static const char *_getopt_initialize (int, char *const *, const char *); -#endif -static const char * -_getopt_initialize (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - first_nonopt = last_nonopt = optind; - - nextchar = NULL; - - posixly_correct = getenv ("POSIXLY_CORRECT"); - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - -#ifdef _LIBC - if (posixly_correct == NULL - && argc == original_argc && argv == original_argv) - { - if (nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = nonoption_flags_max_len = strlen (orig_str); - if (nonoption_flags_max_len < argc) - nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - nonoption_flags_max_len = -1; - else - { - memcpy (__getopt_nonoption_flags, orig_str, len); - memset (&__getopt_nonoption_flags[len], '\0', - nonoption_flags_max_len - len); - } - } - } - nonoption_flags_len = nonoption_flags_max_len; - } - else - nonoption_flags_len = 0; -#endif - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int -_getopt_internal (argc, argv, optstring, longopts, longind, long_only) - int argc; - char *const *argv; - const char *optstring; - const struct option *longopts; - int *longind; - int long_only; -{ - optarg = NULL; - - if (optind == 0 || !__getopt_initialized) - { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring); - __getopt_initialized = 1; - } - - /* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#ifdef _LIBC -#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ - || (optind < nonoption_flags_len \ - && __getopt_nonoption_flags[optind] == '1')) -#else -#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') -#endif - - if (nextchar == NULL || *nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) - first_nonopt = optind; - - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp (argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if (NONOPTION_P) - { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[optind][1] == '-' - || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - if (opterr) - fprintf (stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - optopt = 0; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (opterr) - { - if (argv[optind - 1][1] == '-') - /* --option */ - fprintf (stderr, - _("%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); - else - { - /* +option or -option */ - fprintf (stderr, - _("%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], pfound->name); - } - } - nextchar += strlen (nextchar); - - optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (opterr) - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' - || my_index (optstring, *nextchar) == NULL) - { - if (opterr) - { - if (argv[optind][1] == '-') - /* --option */ - fprintf (stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); - else - /* +option or -option */ - fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); - } - nextchar = (char *) ""; - optind++; - optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *nextchar++; - char *temp = my_index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') - { - if (opterr) - { - if (posixly_correct) - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, _("%s: illegal option -- %c\n"), - argv[0], c); - else - fprintf (stderr, _("%s: invalid option -- %c\n"), - argv[0], c); - } - optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr) - { - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - if (opterr) - fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (opterr) - fprintf (stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); - - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (opterr) - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - nextchar = NULL; - return 'W'; /* Let the application handle it. */ - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - optarg = nextchar; - optind++; - } - else - optarg = NULL; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr) - { - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - -#endif /* Not ELIDE_CODE. */ - -#endif - -#ifdef _MSC_VER -# pragma warning(pop) -#endif - +/* $Header: /pjproject/pjsip/src/pjsua/getopt.c 4 5/14/05 12:24a Bennylp $ */ + +#ifdef _MSC_VER +/* in VC this file will generate a lot of warning about old style function + * declarations. + */ +# pragma warning(push, 3) +#endif + +/* + * getopt entry points + * + * modified by Mike Borella + * + * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef HAVE_GETOPT_LONG + +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include "getopt.h" + + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (int argc, char *const *argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + +int +getopt (int argc, char * const * argv, const char * optstring) +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + + +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + + +//#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#include +#endif /* GNU C library. */ + +#ifdef VMS +#include +#if HAVE_STRING_H - 0 +#include +#endif +#endif + +#if defined (WIN32) && !defined (__CYGWIN32__) +/* It's not Unix, really. See? Capital letters. */ +#include +#define getpid() GetCurrentProcessId() +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ +#ifdef HAVE_LIBINTL_H +# include +# define _(msgid) gettext (msgid) +#else +# define _(msgid) (msgid) +#endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = NULL; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include +#define my_index strchr +#else + +static char * +my_index (const char *str, int chr) +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +#if !defined (__STDC__) || !__STDC__ +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +#endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; + +static int original_argc; +static char *const *original_argv; + +extern pid_t __libc_pid; + +/* Make sure the environment variable bash 2.0 puts in the environment + is valid for the getopt call we must make sure that the ARGV passed + to getopt is that one passed to the process. */ +static void +__attribute__ ((unused)) +store_args_and_env (int argc, char *const *argv) +{ + /* XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ + original_argc = argc; + original_argv = argv; +} +text_set_element (__libc_subinit, store_args_and_env); + +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined (__STDC__) && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#ifdef _LIBC + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = malloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); + memset (&new_str[nonoption_flags_max_len], '\0', + top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined (__STDC__) && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#ifdef _LIBC + if (posixly_correct == NULL + && argc == original_argc && argv == original_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) malloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + { + memcpy (__getopt_nonoption_flags, orig_str, len); + memset (&__getopt_nonoption_flags[len], '\0', + nonoption_flags_max_len - len); + } + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#ifdef _LIBC +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + { + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + } + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (opterr) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +#endif /* Not ELIDE_CODE. */ + +#endif + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + diff --git a/pjsip/src/pjsua/getopt.h b/pjsip/src/pjsua/getopt.h index aa2ba1b6e..3783c6ab1 100644 --- a/pjsip/src/pjsua/getopt.h +++ b/pjsip/src/pjsua/getopt.h @@ -1,140 +1,140 @@ -/* $Header: /pjproject/pjsip/src/pjsua/getopt.h 3 5/05/05 11:43p Bennylp $ */ -/* Declarations for getopt. - Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _GETOPT_H -#define _GETOPT_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; - -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 - - -/* Get definitions and prototypes for functions to process the - arguments in ARGV (ARGC of them, minus the program name) for - options given in OPTS. - - Return the option character from OPTS just read. Return -1 when - there are no more options. For unrecognized options, or options - missing arguments, `optopt' is set to the option letter, and '?' is - returned. - - The OPTS string is a list of characters which are recognized option - letters, optionally followed by colons, specifying that that letter - takes an argument, to be placed in `optarg'. - - If a letter in OPTS is followed by two colons, its argument is - optional. This behavior is specific to the GNU `getopt'. - - The argument `--' causes premature termination of argument - scanning, explicitly telling `getopt' that there are no more - options. - - If OPTS begins with `--', then non-option arguments are treated as - arguments to the option '\0'. This behavior is specific to the GNU - `getopt'. */ - -int getopt (int argc, char *const *argv, const char *shortopts); - -int getopt_long (int argc, char *const *argv, const char *options, - const struct option *longopts, int *longind); -int getopt_long_only (int argc, char *const *argv, - const char *shortopts, - const struct option *longopts, int *longind); - -/* Internal only. Users should not call this directly. */ -int _getopt_internal (int argc, char *const *argv, - const char *shortopts, - const struct option *longopts, int *longind, - int long_only); - -#ifdef __cplusplus -} -#endif - -#endif /* getopt.h */ - +/* $Header: /pjproject/pjsip/src/pjsua/getopt.h 3 5/05/05 11:43p Bennylp $ */ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +int getopt (int argc, char *const *argv, const char *shortopts); + +int getopt_long (int argc, char *const *argv, const char *options, + const struct option *longopts, int *longind); +int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); + +#ifdef __cplusplus +} +#endif + +#endif /* getopt.h */ + diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index c0b3ff8d3..48603d574 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -1,1811 +1,1811 @@ -/* $Header: /pjproject-0.3/pjsip/src/pjsua/main.c 40 10/14/05 12:23a Bennylp $ */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define START_PORT 5060 -#define MAX_BUDDIES 32 -#define THIS_FILE "main.c" -#define MAX_PRESENTITY 32 -#define PRESENCE_TIMEOUT 60 - -/* By default we'll have one worker thread, except when threading - * is disabled. - */ -#if PJ_HAS_THREADS -# define WORKER_COUNT 1 -#else -# define WORKER_COUNT 0 -#endif - -/* Global variable. */ -static struct -{ - /* Control. */ - pj_pool_factory *pf; - pjsip_endpoint *endpt; - pj_pool_t *pool; - pjsip_user_agent *user_agent; - int worker_cnt; - int worker_quit_flag; - - /* User info. */ - char user_id[64]; - pj_str_t local_uri; - pj_str_t contact; - pj_str_t real_contact; - - /* Dialog. */ - pjsip_dlg *cur_dlg; - - /* Authentication. */ - int cred_count; - pjsip_cred_info cred_info[4]; - - /* Media stack. */ - pj_bool_t null_audio; - pj_med_mgr_t *mmgr; - - /* Misc. */ - int app_log_level; - char *log_filename; - FILE *log_file; - - /* Proxy URLs */ - pj_str_t proxy; - pj_str_t outbound_proxy; - - /* UA auto options. */ - int auto_answer; /* -1 to disable. */ - int auto_hangup; /* -1 to disable */ - - /* Registration. */ - pj_str_t registrar_uri; - pjsip_regc *regc; - pj_int32_t reg_timeout; - pj_timer_entry regc_timer; - - /* STUN */ - pj_str_t stun_srv1; - int stun_port1; - pj_str_t stun_srv2; - int stun_port2; - - /* UDP sockets and their public address. */ - int sip_port; - pj_sock_t sip_sock; - pj_sockaddr_in sip_sock_name; - pj_sock_t rtp_sock; - pj_sockaddr_in rtp_sock_name; - pj_sock_t rtcp_sock; - pj_sockaddr_in rtcp_sock_name; - - /* SIMPLE */ - pj_bool_t hide_status; - pj_bool_t offer_x_ms_msg; - int im_counter; - int buddy_cnt; - pj_str_t buddy[MAX_BUDDIES]; - pj_bool_t buddy_status[MAX_BUDDIES]; - pj_bool_t no_presence; - pjsip_presentity *buddy_pres[MAX_BUDDIES]; - - int pres_cnt; - pjsip_presentity *pres[MAX_PRESENTITY]; - -} global; - -enum { AUTO_ANSWER, AUTO_HANGUP }; - -/* This is the data that will be 'attached' on per dialog basis. */ -struct dialog_data -{ - /* Media session. */ - pj_media_session_t *msession; - - /* x-ms-chat session. */ - pj_bool_t x_ms_msg_session; - - /* Cached SDP body, updated when media session changed. */ - pjsip_msg_body *body; - - /* Timer. */ - pj_bool_t has_auto_timer; - pj_timer_entry auto_timer; -}; - -/* - * These are the callbacks to be registered to dialog to receive notifications - * about various events in the dialog. - */ -static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, - pjsip_event *event ); -static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata, int retransmission); -static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata); -static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata); -static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata); -static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata); -static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_event *event); -static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event); -static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event); -static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event); -static void dlg_on_terminated (pjsip_dlg *dlg); -static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event); - -/* The callback structure that will be registered to UA layer. */ -struct pjsip_dlg_callback dlg_callback = { - &dlg_on_all_events, - &dlg_on_before_tx, - &dlg_on_tx_msg, - &dlg_on_rx_msg, - &dlg_on_incoming, - &dlg_on_calling, - &dlg_on_provisional, - &dlg_on_connecting, - &dlg_on_established, - &dlg_on_disconnected, - &dlg_on_terminated, - &dlg_on_mid_call_evt -}; - - -/* - * Auxiliary things are put in misc.c, so that this main.c file is more - * readable. - */ -#include "misc.c" - -static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap, - struct pj_timer_entry *entry) -{ - pjsip_dlg *dlg = entry->user_data; - struct dialog_data *dlg_data = dlg->user_data; - - PJ_UNUSED_ARG(timer_heap) - - dlg_data->has_auto_timer = 0; - - if (entry->id == AUTO_ANSWER) { - pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200); - if (tdata) { - struct dialog_data *dlg_data = global.cur_dlg->user_data; - tdata->msg->body = dlg_data->body; - pjsip_dlg_send_msg(dlg, tdata); - } - } else { - pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500); - if (tdata) - pjsip_dlg_send_msg(dlg, tdata); - } -} - -static void update_registration(pjsip_regc *regc, int renew) -{ - pjsip_tx_data *tdata; - - PJ_LOG(3,(THIS_FILE, "Performing SIP registration...")); - - if (renew) { - tdata = pjsip_regc_register(regc, 1); - } else { - tdata = pjsip_regc_unregister(regc); - } - - pjsip_regc_send( regc, tdata ); -} - -static void regc_cb(struct pjsip_regc_cbparam *param) -{ - /* - * Print registration status. - */ - if (param->code < 0 || param->code >= 300) { - PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)", - param->code, pjsip_get_status_text(param->code)->ptr)); - global.regc = NULL; - - } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) { - PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), " - "will re-register in %d seconds", - param->code, - pjsip_get_status_text(param->code)->ptr, - param->expiration)); - - } else { - PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code)); - } -} - -static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata, - int *timeout) -{ - int state; - int i; - char url[PJSIP_MAX_URL_SIZE]; - int urllen; - - PJ_UNUSED_ARG(rdata) - - if (*timeout > 0) { - state = PJSIP_EVENT_SUB_STATE_ACTIVE; - if (*timeout > 300) - *timeout = 300; - } else { - state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } - - urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1); - if (urllen < 1) { - strcpy(url, ""); - } else { - url[urllen] = '\0'; - } - PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s", - url, - (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated"))); - - for (i=0; isub->default_interval); -} - -/* This is called by presence framework when we receives presence update - * of a resource (buddy). - */ -static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open) -{ - int buddy_index = (int)pres->user_data; - - global.buddy_status[buddy_index] = is_open; - PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s", - global.buddy[buddy_index].ptr, - (is_open ? "Online" : "Offline"))); -} - -/* This is called when the subscription is terminated. */ -static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason) -{ - if (pres->sub->role == PJSIP_ROLE_UAC) { - int buddy_index = (int)pres->user_data; - PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)", - global.buddy[buddy_index].ptr, - reason->slen, reason->ptr)); - global.buddy_pres[buddy_index] = NULL; - global.buddy_status[buddy_index] = 0; - } else { - int i; - PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)", - reason->slen, reason->ptr)); - pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1); - for (i=0; idata, buf, size); -} - -/* When media session has changed, call this function to update the cached body - * information in the dialog. - */ -static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg) -{ - struct dialog_data *dlg_data = dlg->user_data; - pjsdp_session_desc *sdp; - - sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg); - if (!sdp) { - dlg_data->body = NULL; - return NULL; - } - - /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new - * "m=" line in the SDP. - */ - if (dlg_data->x_ms_msg_session >= 0 && - dlg_data->x_ms_msg_session >= (int)sdp->media_count) - { - pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m)); - sdp->media[sdp->media_count] = m; - dlg_data->x_ms_msg_session = sdp->media_count++; - } - - /* - * For "x-ms-message" line, remove all attributes and connection line etc. - */ - if (dlg_data->x_ms_msg_session >= 0) { - pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session]; - if (m) { - m->desc.media = pj_str("x-ms-message"); - m->desc.port = 5060; - m->desc.transport = pj_str("sip"); - m->desc.fmt_count = 1; - m->desc.fmt[0] = pj_str("null"); - m->attr_count = 0; - m->conn = NULL; - } - } - - dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body)); - dlg_data->body->content_type.type = pj_str("application"); - dlg_data->body->content_type.subtype = pj_str("sdp"); - dlg_data->body->len = 0; /* ignored */ - dlg_data->body->print_body = &print_msg_body; - - dlg_data->body->data = sdp; - return dlg_data->body; -} - -/* This callback will be called on every occurence of events in dialogs */ -static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, - pjsip_event *event ) -{ - PJ_UNUSED_ARG(dlg_evt) - PJ_UNUSED_ARG(event) - - PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg)); -} - -/* This callback is called before each outgoing msg is sent (including - * retransmission). Application can override this notification if it wants - * to modify the message before transmission or if it wants to do something - * else for each transmission. - */ -static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata, int ret_cnt) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(tdata) - - if (ret_cnt > 0) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)", - dlg->obj_name, ret_cnt)); - } -} - -/* This callback is called after a message is sent. */ -static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(tdata) - - PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); -} - -/* This callback is called on receipt of incoming message. */ -static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(rdata) - PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); -} - -/* This callback is called after dialog has sent INVITE */ -static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(tdata) - - pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG && - tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && - tsx->method.id == PJSIP_INVITE_METHOD); - - PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name)); -} - -static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp) -{ - struct dialog_data *dlg_data = dlg->user_data; - pj_bool_t sdp_x_ms_msg_index = -1; - int i; - int mcnt; - const pj_media_stream_info *mi[PJSDP_MAX_MEDIA]; - int has_active; - pj_media_sock_info sock_info; - - /* Find "m=x-ms-message" line in the SDP. */ - for (i=0; i<(int)sdp->media_count; ++i) { - if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0) - sdp_x_ms_msg_index = i; - } - - /* - * Create media session. - */ - pj_memset(&sock_info, 0, sizeof(sock_info)); - sock_info.rtp_sock = global.rtp_sock; - sock_info.rtcp_sock = global.rtcp_sock; - pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); - - dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info); - - /* A session will always be created, unless there is memory - * alloc problem. - */ - pj_assert(dlg_data->msession); - - /* See if we can take the offer by checking that we have at least - * one media stream active. - */ - mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi); - for (i=0, has_active=0; ifmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) { - has_active = 1; - break; - } - } - - if (!has_active && sdp_x_ms_msg_index==-1) { - pjsip_tx_data *tdata; - - /* Unable to accept remote's SDP. - * Answer with 488 (Not Acceptable Here) - */ - /* Create 488 response. */ - tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE); - - /* Send response. */ - if (tdata) - pjsip_dlg_send_msg(dlg, tdata); - return; - } - - dlg_data->x_ms_msg_session = sdp_x_ms_msg_index; - - /* Create msg body to be used later in 2xx/response */ - create_msg_body(dlg, 0); - -} - -/* This callback is called after an INVITE is received. */ -static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - struct dialog_data *dlg_data; - pjsip_msg *msg; - pjsip_tx_data *tdata; - char buf[128]; - int len; - - PJ_UNUSED_ARG(tsx) - - pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG && - rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && - tsx->method.id == PJSIP_INVITE_METHOD); - - /* - * Notify user! - */ - PJ_LOG(3, (THIS_FILE, "")); - PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name)); - PJ_LOG(3, (THIS_FILE, "")); - len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, - (pjsip_name_addr*)dlg->remote.info->uri, - buf, sizeof(buf)-1); - if (len > 0) { - buf[len] = '\0'; - PJ_LOG(3,(THIS_FILE, "From:\t%s", buf)); - } - len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, - (pjsip_name_addr*)dlg->local.info->uri, - buf, sizeof(buf)-1); - if (len > 0) { - buf[len] = '\0'; - PJ_LOG(3,(THIS_FILE, "To:\t%s", buf)); - } - PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name)); - PJ_LOG(3, (THIS_FILE, "")); - - /* - * Process incoming dialog. - */ - - dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); - dlg->user_data = dlg_data; - - /* Update contact. */ - pjsip_dlg_set_contact(dlg, &global.contact); - - /* Initialize credentials. */ - pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); - - /* Create media session if the request has "application/sdp" body. */ - msg = rdata->msg; - if (msg->body && - pj_stricmp2(&msg->body->content_type.type, "application")==0 && - pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0) - { - pjsdp_session_desc *sdp; - - /* Parse SDP body, and instantiate media session based on remote's SDP. - * Then create our SDP body from the session. - */ - sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool); - if (!sdp) - goto send_answer; - - create_session_from_sdp(dlg, sdp); - - } else if (msg->body) { - /* The request has a message body other than "application/sdp" */ - pjsip_accept_hdr *accept; - - /* Create response. */ - tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); - - /* Add "Accept" header. */ - accept = pjsip_accept_hdr_create(tdata->pool); - accept->values[0] = pj_str("application/sdp"); - accept->count = 1; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept); - - /* Send response. */ - pjsip_dlg_send_msg(dlg, tdata); - return; - - } else { - /* The request has no message body. We can take this request, but - * no media session will be activated. - */ - /* Nothing to do here. */ - } - -send_answer: - /* Immediately answer with 100 (or 180? */ - tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING ); - pjsip_dlg_send_msg(dlg, tdata); - - /* Set current dialog to this dialog if we don't currently have - * current dialog. - */ - if (global.cur_dlg == NULL) { - global.cur_dlg = dlg; - } - - /* Auto-answer if option is specified. */ - if (global.auto_answer >= 0) { - pj_time_val delay = { 0, 0}; - struct dialog_data *dlg_data = dlg->user_data; - - PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds", - global.auto_answer)); - - delay.sec = global.auto_answer; - dlg_data->auto_timer.user_data = dlg; - dlg_data->auto_timer.id = AUTO_ANSWER; - dlg_data->auto_timer.cb = &dlg_auto_timer_callback; - dlg_data->has_auto_timer = 1; - pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); - } -} - -/* This callback is called when dialog has sent/received a provisional response - * to INVITE. - */ -static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_event *event) -{ - const char *action; - - pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.tdata->msg->line.status.code/100 == 1 && - tsx->method.id == PJSIP_INVITE_METHOD) - || - (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.rdata->msg->line.status.code/100 == 1 && - tsx->method.id == PJSIP_INVITE_METHOD)); - - if (event->src_type == PJSIP_EVENT_TX_MSG) - action = "Sending"; - else - action = "Received"; - - PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)", - dlg->obj_name, action, tsx->status_code, - pjsip_get_status_text(tsx->status_code)->ptr)); -} - -/* This callback is called when 200 response to INVITE is sent/received. */ -static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event) -{ - struct dialog_data *dlg_data = dlg->user_data; - const char *action; - - pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.tdata->msg->line.status.code/100 == 2) - || - (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.rdata->msg->line.status.code/100 == 2)); - - if (event->src_type == PJSIP_EVENT_RX_MSG) - action = "Received"; - else - action = "Sending"; - - PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action)); - - if (event->src_type == PJSIP_EVENT_RX_MSG) { - /* On receipt of 2xx response, negotiate our media capability - * and start media. - */ - pjsip_msg *msg = event->src.rdata->msg; - pjsip_msg_body *body; - pjsdp_session_desc *sdp; - - /* Get SDP from message. */ - - /* Ignore if no SDP body is present. */ - body = msg->body; - if (!body) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!", - dlg->obj_name)); - return; - } - - if (pj_stricmp2(&body->content_type.type, "application") != 0 && - pj_stricmp2(&body->content_type.subtype, "sdp") != 0) - { - PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!", - dlg->obj_name)); - return; - } - - /* Got what seems to be a SDP content. Parse it. */ - sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool); - if (!sdp) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!", - dlg->obj_name)); - return; - } - - /* Negotiate media session with remote's media capability. */ - if (pj_media_session_update (dlg_data->msession, sdp) != 0) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!", - dlg->obj_name)); - return; - } - - /* Update the saved SDP body because media session has changed. - * Also set ack flag to '1', because we only want to send one format/ - * codec for each media streams. - */ - create_msg_body(dlg, 1); - - /* Activate media. */ - pj_media_session_activate (dlg_data->msession); - - } else { - pjsip_msg *msg = event->src.tdata->msg; - - if (msg->body) { - /* On transmission of 2xx response, start media session. */ - pj_media_session_activate (dlg_data->msession); - } - } - -} - -/* This callback is called when ACK to initial INVITE is sent/received. */ -static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event) -{ - const char *action; - - pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->type == PJSIP_REQUEST_MSG && - event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) - || - (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_REQUEST_MSG && - event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD)); - - if (event->src_type == PJSIP_EVENT_RX_MSG) - action = "Received"; - else - action = "Sending"; - - PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED", - dlg->obj_name, action)); - - /* Attach SDP body for outgoing ACK. */ - if (event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) - { - struct dialog_data *dlg_data = dlg->user_data; - event->src.tdata->msg->body = dlg_data->body; - } - - /* Auto-hangup if option is specified. */ - if (global.auto_hangup >= 0) { - pj_time_val delay = { 0, 0}; - struct dialog_data *dlg_data = dlg->user_data; - - PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds", - global.auto_hangup)); - - delay.sec = global.auto_hangup; - dlg_data->auto_timer.user_data = dlg; - dlg_data->auto_timer.id = AUTO_HANGUP; - dlg_data->auto_timer.cb = &dlg_auto_timer_callback; - dlg_data->has_auto_timer = 1; - pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); - } -} - - -/* This callback is called when dialog is disconnected (because of final - * response, BYE, or timer). - */ -static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event) -{ - struct dialog_data *dlg_data = dlg->user_data; - int status_code; - const pj_str_t *reason; - - PJ_UNUSED_ARG(event) - - /* Cancel auto-answer/auto-hangup timer. */ - if (dlg_data->has_auto_timer) { - pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer); - dlg_data->has_auto_timer = 0; - } - - if (dlg->invite_tsx) - status_code = dlg->invite_tsx->status_code; - else - status_code = 200; - - if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) { - if (event->src_type == PJSIP_EVENT_RX_MSG) - reason = &event->src.rdata->msg->line.status.reason; - else if (event->src_type == PJSIP_EVENT_TX_MSG) - reason = &event->src.tdata->msg->line.status.reason; - else - reason = pjsip_get_status_text(event->obj.tsx->status_code); - } else { - reason = &event->obj.tsx->method.name; - } - - PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)", - dlg->obj_name, status_code, - reason->slen, reason->ptr)); - - if (dlg_data->msession) { - pj_media_session_destroy (dlg_data->msession); - dlg_data->msession = NULL; - } -} - -/* This callback is called when dialog is about to be destroyed. */ -static void dlg_on_terminated(pjsip_dlg *dlg) -{ - PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name)); - - /* If current dialog is equal to this dialog, update it. */ - if (global.cur_dlg == dlg) { - global.cur_dlg = global.cur_dlg->next; - if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { - global.cur_dlg = NULL; - } - } -} - -/* This callback is called for any requests when dialog is established. */ -static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event) -{ - pjsip_transaction *tsx = event->obj.tsx; - - if (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_REQUEST_MSG) - { - if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) { - /* Re-invitation. */ - pjsip_tx_data *tdata; - - PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)", - dlg->obj_name)); - tdata = pjsip_dlg_answer(dlg, 200); - if (tdata) { - struct dialog_data *dlg_data = dlg->user_data; - tdata->msg->body = dlg_data->body; - pjsip_dlg_send_msg(dlg, tdata); - } - } else { - /* Don't worry, endpoint will answer with 500 or whetever. */ - } - - } else if (tsx->status_code/100 == 2) { - PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)", - dlg->obj_name, - tsx->method.name.slen, tsx->method.name.ptr, - tsx->status_code, - pjsip_get_status_text(tsx->status_code)->ptr)); - - - } else if (tsx->status_code >= 300) { - pj_bool_t report_failure = PJ_TRUE; - - /* Check for authentication failures. */ - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( global.endpt, - dlg->pool, &dlg->auth_sess, - dlg->cred_count, dlg->cred_info, - tsx->last_tx, event->src.rdata ); - if (tdata) { - int rc; - rc = pjsip_dlg_send_msg( dlg, tdata); - report_failure = (rc != 0); - } - } - if (report_failure) { - const pj_str_t *reason; - if (event->src_type == PJSIP_EVENT_RX_MSG) { - reason = &event->src.rdata->msg->line.status.reason; - } else { - reason = pjsip_get_status_text(tsx->status_code); - } - PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)", - dlg->obj_name, tsx->status_code, - reason->slen, reason->ptr)); - } - } -} - -/* Initialize sockets and optionally get the public address via STUN. */ -static pj_status_t init_sockets() -{ - enum { - RTP_START_PORT = 4000, - RTP_RANDOM_START = 2, - RTP_RETRY = 10 - }; - enum { - SIP_SOCK, - RTP_SOCK, - RTCP_SOCK, - }; - int i; - int rtp_port; - pj_sock_t sock[3]; - pj_sockaddr_in mapped_addr[3]; - - for (i=0; i<3; ++i) - sock[i] = PJ_INVALID_SOCKET; - - /* Create and bind SIP UDP socket. */ - sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); - if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) { - PJ_LOG(2,(THIS_FILE, "Unable to create socket")); - goto on_error; - } - if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) { - PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port")); - goto on_error; - } - - /* Initialize start of RTP port to try. */ - rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2; - - /* Loop retry to bind RTP and RTCP sockets. */ - for (i=0; iuser.slen) - strncpy(global.user_id, url->user.ptr, url->user.slen); - } - } - pj_pool_release(pool); - } - - if (global.user_id[0]=='\0') { - strcpy(global.user_id, "user"); - } - - /* build contact */ - global.real_contact.ptr = local_uri; - global.real_contact.slen = - sprintf(local_uri, "", global.user_id, local_addr, global.sip_port); - - if (global.contact.slen == 0) - global.contact = global.real_contact; - - /* initialize local_uri with contact if it's not specified in cmdline */ - if (global.local_uri.slen == 0) - global.local_uri = global.contact; - - /* Init proxy. */ - if (global.proxy.slen || global.outbound_proxy.slen) { - int count = 0; - pj_str_t proxy_url[2]; - - if (global.outbound_proxy.slen) { - proxy_url[count++] = global.outbound_proxy; - } - if (global.proxy.slen) { - proxy_url[count++] = global.proxy; - } - - if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) { - PJ_LOG(2,(THIS_FILE, "Error setting proxy address!")); - return -1; - } - } - - /* initialize SIP registration if registrar is configured */ - if (global.registrar_uri.slen) { - global.regc = pjsip_regc_create( global.endpt, NULL, ®c_cb); - pjsip_regc_init( global.regc, &global.registrar_uri, - &global.local_uri, - &global.local_uri, - 1, &global.contact, - global.reg_timeout); - pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info ); - } - - return PJ_SUCCESS; -} - -/* Worker thread function, only used when threading is enabled. */ -static void *PJ_THREAD_FUNC worker_thread(void *unused) -{ - PJ_UNUSED_ARG(unused) - - while (!global.worker_quit_flag) { - pj_time_val timeout = { 0, 10 }; - pjsip_endpt_handle_events (global.endpt, &timeout); - } - return NULL; -} - - -/* Make call to the specified URI. */ -static pjsip_dlg *make_call(pj_str_t *remote_uri) -{ - pjsip_dlg *dlg; - pj_str_t local = global.contact; - pj_str_t remote = *remote_uri; - struct dialog_data *dlg_data; - pjsip_tx_data *tdata; - pj_media_sock_info sock_info; - - /* Create new dialog instance. */ - dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC); - - /* Attach our own user data. */ - dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); - dlg->user_data = dlg_data; - - /* Create media session. */ - pj_memset(&sock_info, 0, sizeof(sock_info)); - sock_info.rtp_sock = global.rtp_sock; - sock_info.rtcp_sock = global.rtcp_sock; - pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); - - dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info); - dlg_data->x_ms_msg_session = -1; - - if (global.offer_x_ms_msg) { - const pj_media_stream_info *minfo[32]; - unsigned cnt; - - cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo); - if (cnt > 0) - dlg_data->x_ms_msg_session = cnt; - } - - /* Initialize dialog with local and remote URI. */ - if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) { - pjsip_ua_destroy_dialog(dlg); - return NULL; - } - - /* Initialize credentials. */ - pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); - - /* Send INVITE! */ - tdata = pjsip_dlg_invite(dlg); - tdata->msg->body = create_msg_body (dlg, 0); - - if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) { - pjsip_ua_destroy_dialog(dlg); - return NULL; - } - - return dlg; -} - -/* - * Callback to receive incoming IM message. - */ -static int on_incoming_im_msg(pjsip_rx_data *rdata) -{ - pjsip_msg *msg = rdata->msg; - pjsip_msg_body *body = msg->body; - int len; - char to[128], from[128]; - - - len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, - rdata->from->uri, from, sizeof(from)); - if (len > 0) from[len] = '\0'; - else strcpy(from, ""); - - len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, - rdata->to->uri, to, sizeof(to)); - if (len > 0) to[len] = '\0'; - else strcpy(to, ""); - - PJ_LOG(3,(THIS_FILE, "Incoming instant message:")); - - printf("----- BEGIN INSTANT MESSAGE ----->\n"); - printf("From:\t%s\n", from); - printf("To:\t%s\n", to); - printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : "")); - printf("<------ END INSTANT MESSAGE ------\n"); - - fflush(stdout); - - /* Must answer with final response. */ - return 200; -} - -/* - * Input URL. - */ -static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection) -{ - int i; - - *selection = -1; - - printf("\nBuddy list:\n"); - printf("---------------------------------------\n"); - for (i=0; i\n", i+1, global.buddy[i].ptr, - (global.buddy_status[i]?"Online":"Offline")); - } - printf("-------------------------------------\n"); - - printf("Choices\n" - "\t0 For current dialog.\n" - "\t[1-%02d] Select from buddy list\n" - "\tURL An URL\n" - , global.buddy_cnt); - printf("Input: "); - - fflush(stdout); - fgets(buf, len, stdin); - buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */ - - while (isspace(*buf)) ++buf; - - if (!*buf || *buf=='\n' || *buf=='\r') - return NULL; - - i = atoi(buf); - - if (i == 0) { - if (isdigit(*buf)) { - *selection = 0; - *out = pj_str("0"); - return out; - } else { - if (verify_sip_url(buf) != 0) { - puts("Invalid URL specified!"); - return NULL; - } - *out = pj_str(buf); - return out; - } - } else if (i > global.buddy_cnt || i < 0) { - printf("Error: invalid selection!\n"); - return NULL; - } else { - *out = global.buddy[i-1]; - *selection = i; - return out; - } -} - - -static void generic_request_callback( void *token, pjsip_event *event ) -{ - pjsip_transaction *tsx = event->obj.tsx; - - PJ_UNUSED_ARG(token) - - if (tsx->status_code/100 == 2) { - PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)", - event->obj.tsx->method.name.slen, - event->obj.tsx->method.name.ptr, - tsx->status_code, - pjsip_get_status_text(tsx->status_code)->ptr)); - } else if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( global.endpt, - global.pool, NULL, global.cred_count, global.cred_info, - tsx->last_tx, event->src.rdata); - if (tdata) { - int rc; - pjsip_cseq_hdr *cseq; - cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); - cseq->cseq++; - rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, - &generic_request_callback); - if (rc == 0) - return; - } - PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)", - event->obj.tsx->method.name.slen, - event->obj.tsx->method.name.ptr, - event->obj.tsx->status_code, - pjsip_get_status_text(event->obj.tsx->status_code)->ptr)); - } else { - const pj_str_t *reason; - if (event->src_type == PJSIP_EVENT_RX_MSG) - reason = &event->src.rdata->msg->line.status.reason; - else - reason = pjsip_get_status_text(tsx->status_code); - PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)", - event->obj.tsx->method.name.slen, - event->obj.tsx->method.name.ptr, - event->obj.tsx->status_code, - reason->slen, reason->ptr)); - } -} - - -static void ui_send_im_message() -{ - char line[100]; - char text_buf[100]; - pj_str_t str; - pj_str_t text_msg; - int selection, rc; - pjsip_tx_data *tdata; - - if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) - return; - - - printf("Enter text to send (empty to cancel): "); fflush(stdout); - fgets(text_buf, sizeof(text_buf), stdin); - text_buf[strlen(text_buf)-1] = '\0'; - if (!*text_buf) - return; - - text_msg = pj_str(text_buf); - - if (selection==0) { - pjsip_method message_method; - pj_str_t str_MESSAGE = { "MESSAGE", 7 }; - - /* Send IM to current dialog. */ - if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { - printf("No current dialog or dialog state is not ESTABLISHED!\n"); - return; - } - - pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE); - tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 ); - - if (tdata) { - /* Create message body for the text. */ - pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body)); - body->content_type.type = pj_str("text"); - body->content_type.subtype = pj_str("plain"); - body->data = pj_pool_alloc(tdata->pool, text_msg.slen); - pj_memcpy(body->data, text_msg.ptr, text_msg.slen); - body->len = text_msg.slen; - body->print_body = &pjsip_print_text_body; - - /* Assign body to message, and send the message! */ - tdata->msg->body = body; - pjsip_dlg_send_msg( global.cur_dlg, tdata ); - } - - } else { - /* Send IM to buddy list. */ - pjsip_method message; - static pj_str_t MESSAGE = { "MESSAGE", 7 }; - pjsip_method_init_np(&message, &MESSAGE); - tdata = pjsip_endpt_create_request(global.endpt, &message, - &str, - &global.real_contact, - &str, &global.real_contact, NULL, -1, - &text_msg); - if (!tdata) { - puts("Error creating request"); - return; - } - rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback); - if (rc == 0) { - printf("Sending IM message %d\n", global.im_counter); - ++global.im_counter; - } else { - printf("Error: unable to send IM message!\n"); - } - } -} - -static void ui_send_options() -{ - char line[100]; - pj_str_t str; - int selection, rc; - pjsip_tx_data *tdata; - pjsip_method options; - - if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) - return; - - pjsip_method_set( &options, PJSIP_OPTIONS_METHOD ); - - if (selection == 0) { - /* Send OPTIONS to current dialog. */ - tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1); - if (tdata) - pjsip_dlg_send_msg( global.cur_dlg, tdata ); - } else { - /* Send OPTIONS to arbitrary party. */ - tdata = pjsip_endpt_create_request( global.endpt, &options, - &str, - &global.local_uri, &str, - &global.real_contact, - NULL, -1, NULL); - if (tdata) { - rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, - &generic_request_callback); - if (rc != 0) - PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!")); - } - } -} - -static void init_presence() -{ - const pjsip_presence_cb pres_cb = { - NULL, - &pres_on_received_request, - &pres_on_received_refresh, - &pres_on_received_update, - &pres_on_terminated - }; - - pjsip_presence_init(&pres_cb); -} - -/* Subscribe presence information for all buddies. */ -static void subscribe_buddies_presence() -{ - int i; - for (i=0; i=0; --i) { - pj_thread_join(thread[i]); - pj_thread_destroy(thread[i]); - } - pj_caching_pool_destroy(&cp); - return 1; - } - } - - printf("Worker thread count: %d\n", global.worker_cnt); - - /* Perform registration, if required. */ - if (global.regc) { - update_registration(global.regc, 1); - } - - /* Initialize media manager. */ - global.mmgr = pj_med_mgr_create(global.pf); - - /* Init presence. */ - init_presence(); - - /* Subscribe presence information of all buddies. */ - if (!global.no_presence) - subscribe_buddies_presence(); - - /* Initializatio completes, loop waiting for commands. */ - for (;!global.worker_quit_flag;) { - pj_str_t str; - char line[128]; - -#if WORKER_COUNT==0 - /* If worker thread does not exist, main thread must poll for evetns. - * But this won't work very well since main thread is blocked by - * fgets(). So keep pressing the ENTER key to get the events! - */ - pj_time_val timeout = { 0, 100 }; - pjsip_endpt_handle_events(global.endpt, &timeout); - puts("Keep pressing ENTER key to get the events!"); -#endif - - printf("\nCurrent dialog: "); - print_dialog(global.cur_dlg); - puts(""); - - keystroke_help(); - - fgets(line, sizeof(line), stdin); - - switch (*line) { - case 'm': - puts("Make outgoing call"); - if (ui_input_url(&str, line, sizeof(line), &i) != NULL) { - pjsip_dlg *dlg = make_call(&str); - if (global.cur_dlg == NULL) { - global.cur_dlg = dlg; - } - } - break; - case 'i': - puts("Send Instant Messaging"); - ui_send_im_message(); - break; - case 'o': - puts("Send OPTIONS"); - ui_send_options(); - break; - case 'a': - if (global.cur_dlg) { - unsigned code; - pjsip_tx_data *tdata; - struct dialog_data *dlg_data = global.cur_dlg->user_data; - - printf("Answer with status code (1xx-6xx): "); - fflush(stdout); - fgets(line, sizeof(line), stdin); - str = pj_str(line); - str.slen -= 1; - - code = pj_strtoul(&str); - tdata = pjsip_dlg_answer(global.cur_dlg, code); - if (tdata) { - if (code/100 == 2) { - tdata->msg->body = dlg_data->body; - } - pjsip_dlg_send_msg(global.cur_dlg, tdata); - - } - } else { - puts("No current dialog"); - } - break; - case 'h': - if (global.cur_dlg) { - pjsip_tx_data *tdata; - tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE); - if (tdata) { - pjsip_dlg_send_msg(global.cur_dlg, tdata); - } - } else { - puts("No current dialog"); - } - break; - case ']': - if (global.cur_dlg) { - global.cur_dlg = global.cur_dlg->next; - if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { - global.cur_dlg = global.cur_dlg->next; - } - } else { - puts("No current dialog"); - } - break; - case '[': - if (global.cur_dlg) { - global.cur_dlg = global.cur_dlg->prev; - if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { - global.cur_dlg = global.cur_dlg->prev; - } - } else { - puts("No current dialog"); - } - break; - case 'd': - pjsip_endpt_dump(global.endpt, *(line+1)=='1'); - pjsip_ua_dump(global.user_agent); - break; - case 's': - if (*(line+1) == 'u') - subscribe_buddies_presence(); - break; - case 'u': - if (*(line+1) == 's') - unsubscribe_presence(); - break; - case 't': - global.hide_status = !global.hide_status; - update_im_status(); - break; - case 'q': - goto on_exit; - case 'l': - print_all_dialogs(); - break; - } - } - -on_exit: - /* Unregister, if required. */ - if (global.regc) { - update_registration(global.regc, 0); - } - - /* Unsubscribe presence. */ - unsubscribe_presence(); - - /* Allow one second to get all events. */ - if (1) { - pj_time_val end_time; - - pj_gettimeofday(&end_time); - end_time.sec++; - - PJ_LOG(3,(THIS_FILE, "Shutting down..")); - for (;;) { - pj_time_val timeout = { 0, 20 }, now; - pjsip_endpt_handle_events (global.endpt, &timeout); - pj_gettimeofday(&now); - PJ_TIME_VAL_SUB(now, end_time); - if (now.sec >= 1) - break; - } - } - - global.worker_quit_flag = 1; - - pj_med_mgr_destroy(global.mmgr); - - /* Wait all threads to quit. */ - for (i=0; imod_data; - modules[(*count)++] = pjsip_messaging_get_module(); - modules[(*count)++] = pjsip_event_sub_get_module(); - - return PJ_SUCCESS; -} +/* $Header: /pjproject-0.3/pjsip/src/pjsua/main.c 40 10/14/05 12:23a Bennylp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define START_PORT 5060 +#define MAX_BUDDIES 32 +#define THIS_FILE "main.c" +#define MAX_PRESENTITY 32 +#define PRESENCE_TIMEOUT 60 + +/* By default we'll have one worker thread, except when threading + * is disabled. + */ +#if PJ_HAS_THREADS +# define WORKER_COUNT 1 +#else +# define WORKER_COUNT 0 +#endif + +/* Global variable. */ +static struct +{ + /* Control. */ + pj_pool_factory *pf; + pjsip_endpoint *endpt; + pj_pool_t *pool; + pjsip_user_agent *user_agent; + int worker_cnt; + int worker_quit_flag; + + /* User info. */ + char user_id[64]; + pj_str_t local_uri; + pj_str_t contact; + pj_str_t real_contact; + + /* Dialog. */ + pjsip_dlg *cur_dlg; + + /* Authentication. */ + int cred_count; + pjsip_cred_info cred_info[4]; + + /* Media stack. */ + pj_bool_t null_audio; + pj_med_mgr_t *mmgr; + + /* Misc. */ + int app_log_level; + char *log_filename; + FILE *log_file; + + /* Proxy URLs */ + pj_str_t proxy; + pj_str_t outbound_proxy; + + /* UA auto options. */ + int auto_answer; /* -1 to disable. */ + int auto_hangup; /* -1 to disable */ + + /* Registration. */ + pj_str_t registrar_uri; + pjsip_regc *regc; + pj_int32_t reg_timeout; + pj_timer_entry regc_timer; + + /* STUN */ + pj_str_t stun_srv1; + int stun_port1; + pj_str_t stun_srv2; + int stun_port2; + + /* UDP sockets and their public address. */ + int sip_port; + pj_sock_t sip_sock; + pj_sockaddr_in sip_sock_name; + pj_sock_t rtp_sock; + pj_sockaddr_in rtp_sock_name; + pj_sock_t rtcp_sock; + pj_sockaddr_in rtcp_sock_name; + + /* SIMPLE */ + pj_bool_t hide_status; + pj_bool_t offer_x_ms_msg; + int im_counter; + int buddy_cnt; + pj_str_t buddy[MAX_BUDDIES]; + pj_bool_t buddy_status[MAX_BUDDIES]; + pj_bool_t no_presence; + pjsip_presentity *buddy_pres[MAX_BUDDIES]; + + int pres_cnt; + pjsip_presentity *pres[MAX_PRESENTITY]; + +} global; + +enum { AUTO_ANSWER, AUTO_HANGUP }; + +/* This is the data that will be 'attached' on per dialog basis. */ +struct dialog_data +{ + /* Media session. */ + pj_media_session_t *msession; + + /* x-ms-chat session. */ + pj_bool_t x_ms_msg_session; + + /* Cached SDP body, updated when media session changed. */ + pjsip_msg_body *body; + + /* Timer. */ + pj_bool_t has_auto_timer; + pj_timer_entry auto_timer; +}; + +/* + * These are the callbacks to be registered to dialog to receive notifications + * about various events in the dialog. + */ +static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, + pjsip_event *event ); +static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata, int retransmission); +static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); +static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); +static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata); +static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata); +static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_event *event); +static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event); +static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event); +static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event); +static void dlg_on_terminated (pjsip_dlg *dlg); +static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event); + +/* The callback structure that will be registered to UA layer. */ +struct pjsip_dlg_callback dlg_callback = { + &dlg_on_all_events, + &dlg_on_before_tx, + &dlg_on_tx_msg, + &dlg_on_rx_msg, + &dlg_on_incoming, + &dlg_on_calling, + &dlg_on_provisional, + &dlg_on_connecting, + &dlg_on_established, + &dlg_on_disconnected, + &dlg_on_terminated, + &dlg_on_mid_call_evt +}; + + +/* + * Auxiliary things are put in misc.c, so that this main.c file is more + * readable. + */ +#include "misc.c" + +static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap, + struct pj_timer_entry *entry) +{ + pjsip_dlg *dlg = entry->user_data; + struct dialog_data *dlg_data = dlg->user_data; + + PJ_UNUSED_ARG(timer_heap) + + dlg_data->has_auto_timer = 0; + + if (entry->id == AUTO_ANSWER) { + pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200); + if (tdata) { + struct dialog_data *dlg_data = global.cur_dlg->user_data; + tdata->msg->body = dlg_data->body; + pjsip_dlg_send_msg(dlg, tdata); + } + } else { + pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500); + if (tdata) + pjsip_dlg_send_msg(dlg, tdata); + } +} + +static void update_registration(pjsip_regc *regc, int renew) +{ + pjsip_tx_data *tdata; + + PJ_LOG(3,(THIS_FILE, "Performing SIP registration...")); + + if (renew) { + tdata = pjsip_regc_register(regc, 1); + } else { + tdata = pjsip_regc_unregister(regc); + } + + pjsip_regc_send( regc, tdata ); +} + +static void regc_cb(struct pjsip_regc_cbparam *param) +{ + /* + * Print registration status. + */ + if (param->code < 0 || param->code >= 300) { + PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)", + param->code, pjsip_get_status_text(param->code)->ptr)); + global.regc = NULL; + + } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) { + PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), " + "will re-register in %d seconds", + param->code, + pjsip_get_status_text(param->code)->ptr, + param->expiration)); + + } else { + PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code)); + } +} + +static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata, + int *timeout) +{ + int state; + int i; + char url[PJSIP_MAX_URL_SIZE]; + int urllen; + + PJ_UNUSED_ARG(rdata) + + if (*timeout > 0) { + state = PJSIP_EVENT_SUB_STATE_ACTIVE; + if (*timeout > 300) + *timeout = 300; + } else { + state = PJSIP_EVENT_SUB_STATE_TERMINATED; + } + + urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1); + if (urllen < 1) { + strcpy(url, ""); + } else { + url[urllen] = '\0'; + } + PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s", + url, + (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated"))); + + for (i=0; isub->default_interval); +} + +/* This is called by presence framework when we receives presence update + * of a resource (buddy). + */ +static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open) +{ + int buddy_index = (int)pres->user_data; + + global.buddy_status[buddy_index] = is_open; + PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s", + global.buddy[buddy_index].ptr, + (is_open ? "Online" : "Offline"))); +} + +/* This is called when the subscription is terminated. */ +static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason) +{ + if (pres->sub->role == PJSIP_ROLE_UAC) { + int buddy_index = (int)pres->user_data; + PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)", + global.buddy[buddy_index].ptr, + reason->slen, reason->ptr)); + global.buddy_pres[buddy_index] = NULL; + global.buddy_status[buddy_index] = 0; + } else { + int i; + PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)", + reason->slen, reason->ptr)); + pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1); + for (i=0; idata, buf, size); +} + +/* When media session has changed, call this function to update the cached body + * information in the dialog. + */ +static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg) +{ + struct dialog_data *dlg_data = dlg->user_data; + pjsdp_session_desc *sdp; + + sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg); + if (!sdp) { + dlg_data->body = NULL; + return NULL; + } + + /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new + * "m=" line in the SDP. + */ + if (dlg_data->x_ms_msg_session >= 0 && + dlg_data->x_ms_msg_session >= (int)sdp->media_count) + { + pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m)); + sdp->media[sdp->media_count] = m; + dlg_data->x_ms_msg_session = sdp->media_count++; + } + + /* + * For "x-ms-message" line, remove all attributes and connection line etc. + */ + if (dlg_data->x_ms_msg_session >= 0) { + pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session]; + if (m) { + m->desc.media = pj_str("x-ms-message"); + m->desc.port = 5060; + m->desc.transport = pj_str("sip"); + m->desc.fmt_count = 1; + m->desc.fmt[0] = pj_str("null"); + m->attr_count = 0; + m->conn = NULL; + } + } + + dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body)); + dlg_data->body->content_type.type = pj_str("application"); + dlg_data->body->content_type.subtype = pj_str("sdp"); + dlg_data->body->len = 0; /* ignored */ + dlg_data->body->print_body = &print_msg_body; + + dlg_data->body->data = sdp; + return dlg_data->body; +} + +/* This callback will be called on every occurence of events in dialogs */ +static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, + pjsip_event *event ) +{ + PJ_UNUSED_ARG(dlg_evt) + PJ_UNUSED_ARG(event) + + PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg)); +} + +/* This callback is called before each outgoing msg is sent (including + * retransmission). Application can override this notification if it wants + * to modify the message before transmission or if it wants to do something + * else for each transmission. + */ +static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata, int ret_cnt) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(tdata) + + if (ret_cnt > 0) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)", + dlg->obj_name, ret_cnt)); + } +} + +/* This callback is called after a message is sent. */ +static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(tdata) + + PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); +} + +/* This callback is called on receipt of incoming message. */ +static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(rdata) + PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); +} + +/* This callback is called after dialog has sent INVITE */ +static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + PJ_UNUSED_ARG(tsx) + PJ_UNUSED_ARG(tdata) + + pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG && + tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && + tsx->method.id == PJSIP_INVITE_METHOD); + + PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name)); +} + +static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp) +{ + struct dialog_data *dlg_data = dlg->user_data; + pj_bool_t sdp_x_ms_msg_index = -1; + int i; + int mcnt; + const pj_media_stream_info *mi[PJSDP_MAX_MEDIA]; + int has_active; + pj_media_sock_info sock_info; + + /* Find "m=x-ms-message" line in the SDP. */ + for (i=0; i<(int)sdp->media_count; ++i) { + if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0) + sdp_x_ms_msg_index = i; + } + + /* + * Create media session. + */ + pj_memset(&sock_info, 0, sizeof(sock_info)); + sock_info.rtp_sock = global.rtp_sock; + sock_info.rtcp_sock = global.rtcp_sock; + pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); + + dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info); + + /* A session will always be created, unless there is memory + * alloc problem. + */ + pj_assert(dlg_data->msession); + + /* See if we can take the offer by checking that we have at least + * one media stream active. + */ + mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi); + for (i=0, has_active=0; ifmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) { + has_active = 1; + break; + } + } + + if (!has_active && sdp_x_ms_msg_index==-1) { + pjsip_tx_data *tdata; + + /* Unable to accept remote's SDP. + * Answer with 488 (Not Acceptable Here) + */ + /* Create 488 response. */ + tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE); + + /* Send response. */ + if (tdata) + pjsip_dlg_send_msg(dlg, tdata); + return; + } + + dlg_data->x_ms_msg_session = sdp_x_ms_msg_index; + + /* Create msg body to be used later in 2xx/response */ + create_msg_body(dlg, 0); + +} + +/* This callback is called after an INVITE is received. */ +static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_rx_data *rdata) +{ + struct dialog_data *dlg_data; + pjsip_msg *msg; + pjsip_tx_data *tdata; + char buf[128]; + int len; + + PJ_UNUSED_ARG(tsx) + + pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG && + rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && + tsx->method.id == PJSIP_INVITE_METHOD); + + /* + * Notify user! + */ + PJ_LOG(3, (THIS_FILE, "")); + PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name)); + PJ_LOG(3, (THIS_FILE, "")); + len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, + (pjsip_name_addr*)dlg->remote.info->uri, + buf, sizeof(buf)-1); + if (len > 0) { + buf[len] = '\0'; + PJ_LOG(3,(THIS_FILE, "From:\t%s", buf)); + } + len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, + (pjsip_name_addr*)dlg->local.info->uri, + buf, sizeof(buf)-1); + if (len > 0) { + buf[len] = '\0'; + PJ_LOG(3,(THIS_FILE, "To:\t%s", buf)); + } + PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name)); + PJ_LOG(3, (THIS_FILE, "")); + + /* + * Process incoming dialog. + */ + + dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); + dlg->user_data = dlg_data; + + /* Update contact. */ + pjsip_dlg_set_contact(dlg, &global.contact); + + /* Initialize credentials. */ + pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); + + /* Create media session if the request has "application/sdp" body. */ + msg = rdata->msg; + if (msg->body && + pj_stricmp2(&msg->body->content_type.type, "application")==0 && + pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0) + { + pjsdp_session_desc *sdp; + + /* Parse SDP body, and instantiate media session based on remote's SDP. + * Then create our SDP body from the session. + */ + sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool); + if (!sdp) + goto send_answer; + + create_session_from_sdp(dlg, sdp); + + } else if (msg->body) { + /* The request has a message body other than "application/sdp" */ + pjsip_accept_hdr *accept; + + /* Create response. */ + tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); + + /* Add "Accept" header. */ + accept = pjsip_accept_hdr_create(tdata->pool); + accept->values[0] = pj_str("application/sdp"); + accept->count = 1; + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept); + + /* Send response. */ + pjsip_dlg_send_msg(dlg, tdata); + return; + + } else { + /* The request has no message body. We can take this request, but + * no media session will be activated. + */ + /* Nothing to do here. */ + } + +send_answer: + /* Immediately answer with 100 (or 180? */ + tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING ); + pjsip_dlg_send_msg(dlg, tdata); + + /* Set current dialog to this dialog if we don't currently have + * current dialog. + */ + if (global.cur_dlg == NULL) { + global.cur_dlg = dlg; + } + + /* Auto-answer if option is specified. */ + if (global.auto_answer >= 0) { + pj_time_val delay = { 0, 0}; + struct dialog_data *dlg_data = dlg->user_data; + + PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds", + global.auto_answer)); + + delay.sec = global.auto_answer; + dlg_data->auto_timer.user_data = dlg; + dlg_data->auto_timer.id = AUTO_ANSWER; + dlg_data->auto_timer.cb = &dlg_auto_timer_callback; + dlg_data->has_auto_timer = 1; + pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); + } +} + +/* This callback is called when dialog has sent/received a provisional response + * to INVITE. + */ +static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx, + pjsip_event *event) +{ + const char *action; + + pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.tdata->msg->line.status.code/100 == 1 && + tsx->method.id == PJSIP_INVITE_METHOD) + || + (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.rdata->msg->line.status.code/100 == 1 && + tsx->method.id == PJSIP_INVITE_METHOD)); + + if (event->src_type == PJSIP_EVENT_TX_MSG) + action = "Sending"; + else + action = "Received"; + + PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)", + dlg->obj_name, action, tsx->status_code, + pjsip_get_status_text(tsx->status_code)->ptr)); +} + +/* This callback is called when 200 response to INVITE is sent/received. */ +static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event) +{ + struct dialog_data *dlg_data = dlg->user_data; + const char *action; + + pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.tdata->msg->line.status.code/100 == 2) + || + (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && + event->src.rdata->msg->line.status.code/100 == 2)); + + if (event->src_type == PJSIP_EVENT_RX_MSG) + action = "Received"; + else + action = "Sending"; + + PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action)); + + if (event->src_type == PJSIP_EVENT_RX_MSG) { + /* On receipt of 2xx response, negotiate our media capability + * and start media. + */ + pjsip_msg *msg = event->src.rdata->msg; + pjsip_msg_body *body; + pjsdp_session_desc *sdp; + + /* Get SDP from message. */ + + /* Ignore if no SDP body is present. */ + body = msg->body; + if (!body) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!", + dlg->obj_name)); + return; + } + + if (pj_stricmp2(&body->content_type.type, "application") != 0 && + pj_stricmp2(&body->content_type.subtype, "sdp") != 0) + { + PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!", + dlg->obj_name)); + return; + } + + /* Got what seems to be a SDP content. Parse it. */ + sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool); + if (!sdp) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!", + dlg->obj_name)); + return; + } + + /* Negotiate media session with remote's media capability. */ + if (pj_media_session_update (dlg_data->msession, sdp) != 0) { + PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!", + dlg->obj_name)); + return; + } + + /* Update the saved SDP body because media session has changed. + * Also set ack flag to '1', because we only want to send one format/ + * codec for each media streams. + */ + create_msg_body(dlg, 1); + + /* Activate media. */ + pj_media_session_activate (dlg_data->msession); + + } else { + pjsip_msg *msg = event->src.tdata->msg; + + if (msg->body) { + /* On transmission of 2xx response, start media session. */ + pj_media_session_activate (dlg_data->msession); + } + } + +} + +/* This callback is called when ACK to initial INVITE is sent/received. */ +static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event) +{ + const char *action; + + pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->type == PJSIP_REQUEST_MSG && + event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) + || + (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_REQUEST_MSG && + event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD)); + + if (event->src_type == PJSIP_EVENT_RX_MSG) + action = "Received"; + else + action = "Sending"; + + PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED", + dlg->obj_name, action)); + + /* Attach SDP body for outgoing ACK. */ + if (event->src_type == PJSIP_EVENT_TX_MSG && + event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) + { + struct dialog_data *dlg_data = dlg->user_data; + event->src.tdata->msg->body = dlg_data->body; + } + + /* Auto-hangup if option is specified. */ + if (global.auto_hangup >= 0) { + pj_time_val delay = { 0, 0}; + struct dialog_data *dlg_data = dlg->user_data; + + PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds", + global.auto_hangup)); + + delay.sec = global.auto_hangup; + dlg_data->auto_timer.user_data = dlg; + dlg_data->auto_timer.id = AUTO_HANGUP; + dlg_data->auto_timer.cb = &dlg_auto_timer_callback; + dlg_data->has_auto_timer = 1; + pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); + } +} + + +/* This callback is called when dialog is disconnected (because of final + * response, BYE, or timer). + */ +static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event) +{ + struct dialog_data *dlg_data = dlg->user_data; + int status_code; + const pj_str_t *reason; + + PJ_UNUSED_ARG(event) + + /* Cancel auto-answer/auto-hangup timer. */ + if (dlg_data->has_auto_timer) { + pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer); + dlg_data->has_auto_timer = 0; + } + + if (dlg->invite_tsx) + status_code = dlg->invite_tsx->status_code; + else + status_code = 200; + + if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) { + if (event->src_type == PJSIP_EVENT_RX_MSG) + reason = &event->src.rdata->msg->line.status.reason; + else if (event->src_type == PJSIP_EVENT_TX_MSG) + reason = &event->src.tdata->msg->line.status.reason; + else + reason = pjsip_get_status_text(event->obj.tsx->status_code); + } else { + reason = &event->obj.tsx->method.name; + } + + PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)", + dlg->obj_name, status_code, + reason->slen, reason->ptr)); + + if (dlg_data->msession) { + pj_media_session_destroy (dlg_data->msession); + dlg_data->msession = NULL; + } +} + +/* This callback is called when dialog is about to be destroyed. */ +static void dlg_on_terminated(pjsip_dlg *dlg) +{ + PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name)); + + /* If current dialog is equal to this dialog, update it. */ + if (global.cur_dlg == dlg) { + global.cur_dlg = global.cur_dlg->next; + if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { + global.cur_dlg = NULL; + } + } +} + +/* This callback is called for any requests when dialog is established. */ +static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event) +{ + pjsip_transaction *tsx = event->obj.tsx; + + if (event->src_type == PJSIP_EVENT_RX_MSG && + event->src.rdata->msg->type == PJSIP_REQUEST_MSG) + { + if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) { + /* Re-invitation. */ + pjsip_tx_data *tdata; + + PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)", + dlg->obj_name)); + tdata = pjsip_dlg_answer(dlg, 200); + if (tdata) { + struct dialog_data *dlg_data = dlg->user_data; + tdata->msg->body = dlg_data->body; + pjsip_dlg_send_msg(dlg, tdata); + } + } else { + /* Don't worry, endpoint will answer with 500 or whetever. */ + } + + } else if (tsx->status_code/100 == 2) { + PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)", + dlg->obj_name, + tsx->method.name.slen, tsx->method.name.ptr, + tsx->status_code, + pjsip_get_status_text(tsx->status_code)->ptr)); + + + } else if (tsx->status_code >= 300) { + pj_bool_t report_failure = PJ_TRUE; + + /* Check for authentication failures. */ + if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( global.endpt, + dlg->pool, &dlg->auth_sess, + dlg->cred_count, dlg->cred_info, + tsx->last_tx, event->src.rdata ); + if (tdata) { + int rc; + rc = pjsip_dlg_send_msg( dlg, tdata); + report_failure = (rc != 0); + } + } + if (report_failure) { + const pj_str_t *reason; + if (event->src_type == PJSIP_EVENT_RX_MSG) { + reason = &event->src.rdata->msg->line.status.reason; + } else { + reason = pjsip_get_status_text(tsx->status_code); + } + PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)", + dlg->obj_name, tsx->status_code, + reason->slen, reason->ptr)); + } + } +} + +/* Initialize sockets and optionally get the public address via STUN. */ +static pj_status_t init_sockets() +{ + enum { + RTP_START_PORT = 4000, + RTP_RANDOM_START = 2, + RTP_RETRY = 10 + }; + enum { + SIP_SOCK, + RTP_SOCK, + RTCP_SOCK, + }; + int i; + int rtp_port; + pj_sock_t sock[3]; + pj_sockaddr_in mapped_addr[3]; + + for (i=0; i<3; ++i) + sock[i] = PJ_INVALID_SOCKET; + + /* Create and bind SIP UDP socket. */ + sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); + if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) { + PJ_LOG(2,(THIS_FILE, "Unable to create socket")); + goto on_error; + } + if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) { + PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port")); + goto on_error; + } + + /* Initialize start of RTP port to try. */ + rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2; + + /* Loop retry to bind RTP and RTCP sockets. */ + for (i=0; iuser.slen) + strncpy(global.user_id, url->user.ptr, url->user.slen); + } + } + pj_pool_release(pool); + } + + if (global.user_id[0]=='\0') { + strcpy(global.user_id, "user"); + } + + /* build contact */ + global.real_contact.ptr = local_uri; + global.real_contact.slen = + sprintf(local_uri, "", global.user_id, local_addr, global.sip_port); + + if (global.contact.slen == 0) + global.contact = global.real_contact; + + /* initialize local_uri with contact if it's not specified in cmdline */ + if (global.local_uri.slen == 0) + global.local_uri = global.contact; + + /* Init proxy. */ + if (global.proxy.slen || global.outbound_proxy.slen) { + int count = 0; + pj_str_t proxy_url[2]; + + if (global.outbound_proxy.slen) { + proxy_url[count++] = global.outbound_proxy; + } + if (global.proxy.slen) { + proxy_url[count++] = global.proxy; + } + + if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) { + PJ_LOG(2,(THIS_FILE, "Error setting proxy address!")); + return -1; + } + } + + /* initialize SIP registration if registrar is configured */ + if (global.registrar_uri.slen) { + global.regc = pjsip_regc_create( global.endpt, NULL, ®c_cb); + pjsip_regc_init( global.regc, &global.registrar_uri, + &global.local_uri, + &global.local_uri, + 1, &global.contact, + global.reg_timeout); + pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info ); + } + + return PJ_SUCCESS; +} + +/* Worker thread function, only used when threading is enabled. */ +static void *PJ_THREAD_FUNC worker_thread(void *unused) +{ + PJ_UNUSED_ARG(unused) + + while (!global.worker_quit_flag) { + pj_time_val timeout = { 0, 10 }; + pjsip_endpt_handle_events (global.endpt, &timeout); + } + return NULL; +} + + +/* Make call to the specified URI. */ +static pjsip_dlg *make_call(pj_str_t *remote_uri) +{ + pjsip_dlg *dlg; + pj_str_t local = global.contact; + pj_str_t remote = *remote_uri; + struct dialog_data *dlg_data; + pjsip_tx_data *tdata; + pj_media_sock_info sock_info; + + /* Create new dialog instance. */ + dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC); + + /* Attach our own user data. */ + dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); + dlg->user_data = dlg_data; + + /* Create media session. */ + pj_memset(&sock_info, 0, sizeof(sock_info)); + sock_info.rtp_sock = global.rtp_sock; + sock_info.rtcp_sock = global.rtcp_sock; + pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); + + dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info); + dlg_data->x_ms_msg_session = -1; + + if (global.offer_x_ms_msg) { + const pj_media_stream_info *minfo[32]; + unsigned cnt; + + cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo); + if (cnt > 0) + dlg_data->x_ms_msg_session = cnt; + } + + /* Initialize dialog with local and remote URI. */ + if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) { + pjsip_ua_destroy_dialog(dlg); + return NULL; + } + + /* Initialize credentials. */ + pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); + + /* Send INVITE! */ + tdata = pjsip_dlg_invite(dlg); + tdata->msg->body = create_msg_body (dlg, 0); + + if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) { + pjsip_ua_destroy_dialog(dlg); + return NULL; + } + + return dlg; +} + +/* + * Callback to receive incoming IM message. + */ +static int on_incoming_im_msg(pjsip_rx_data *rdata) +{ + pjsip_msg *msg = rdata->msg; + pjsip_msg_body *body = msg->body; + int len; + char to[128], from[128]; + + + len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, + rdata->from->uri, from, sizeof(from)); + if (len > 0) from[len] = '\0'; + else strcpy(from, ""); + + len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, + rdata->to->uri, to, sizeof(to)); + if (len > 0) to[len] = '\0'; + else strcpy(to, ""); + + PJ_LOG(3,(THIS_FILE, "Incoming instant message:")); + + printf("----- BEGIN INSTANT MESSAGE ----->\n"); + printf("From:\t%s\n", from); + printf("To:\t%s\n", to); + printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : "")); + printf("<------ END INSTANT MESSAGE ------\n"); + + fflush(stdout); + + /* Must answer with final response. */ + return 200; +} + +/* + * Input URL. + */ +static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection) +{ + int i; + + *selection = -1; + + printf("\nBuddy list:\n"); + printf("---------------------------------------\n"); + for (i=0; i\n", i+1, global.buddy[i].ptr, + (global.buddy_status[i]?"Online":"Offline")); + } + printf("-------------------------------------\n"); + + printf("Choices\n" + "\t0 For current dialog.\n" + "\t[1-%02d] Select from buddy list\n" + "\tURL An URL\n" + , global.buddy_cnt); + printf("Input: "); + + fflush(stdout); + fgets(buf, len, stdin); + buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */ + + while (isspace(*buf)) ++buf; + + if (!*buf || *buf=='\n' || *buf=='\r') + return NULL; + + i = atoi(buf); + + if (i == 0) { + if (isdigit(*buf)) { + *selection = 0; + *out = pj_str("0"); + return out; + } else { + if (verify_sip_url(buf) != 0) { + puts("Invalid URL specified!"); + return NULL; + } + *out = pj_str(buf); + return out; + } + } else if (i > global.buddy_cnt || i < 0) { + printf("Error: invalid selection!\n"); + return NULL; + } else { + *out = global.buddy[i-1]; + *selection = i; + return out; + } +} + + +static void generic_request_callback( void *token, pjsip_event *event ) +{ + pjsip_transaction *tsx = event->obj.tsx; + + PJ_UNUSED_ARG(token) + + if (tsx->status_code/100 == 2) { + PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)", + event->obj.tsx->method.name.slen, + event->obj.tsx->method.name.ptr, + tsx->status_code, + pjsip_get_status_text(tsx->status_code)->ptr)); + } else if (tsx->status_code==401 || tsx->status_code==407) { + pjsip_tx_data *tdata; + tdata = pjsip_auth_reinit_req( global.endpt, + global.pool, NULL, global.cred_count, global.cred_info, + tsx->last_tx, event->src.rdata); + if (tdata) { + int rc; + pjsip_cseq_hdr *cseq; + cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + cseq->cseq++; + rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, + &generic_request_callback); + if (rc == 0) + return; + } + PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)", + event->obj.tsx->method.name.slen, + event->obj.tsx->method.name.ptr, + event->obj.tsx->status_code, + pjsip_get_status_text(event->obj.tsx->status_code)->ptr)); + } else { + const pj_str_t *reason; + if (event->src_type == PJSIP_EVENT_RX_MSG) + reason = &event->src.rdata->msg->line.status.reason; + else + reason = pjsip_get_status_text(tsx->status_code); + PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)", + event->obj.tsx->method.name.slen, + event->obj.tsx->method.name.ptr, + event->obj.tsx->status_code, + reason->slen, reason->ptr)); + } +} + + +static void ui_send_im_message() +{ + char line[100]; + char text_buf[100]; + pj_str_t str; + pj_str_t text_msg; + int selection, rc; + pjsip_tx_data *tdata; + + if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) + return; + + + printf("Enter text to send (empty to cancel): "); fflush(stdout); + fgets(text_buf, sizeof(text_buf), stdin); + text_buf[strlen(text_buf)-1] = '\0'; + if (!*text_buf) + return; + + text_msg = pj_str(text_buf); + + if (selection==0) { + pjsip_method message_method; + pj_str_t str_MESSAGE = { "MESSAGE", 7 }; + + /* Send IM to current dialog. */ + if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { + printf("No current dialog or dialog state is not ESTABLISHED!\n"); + return; + } + + pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE); + tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 ); + + if (tdata) { + /* Create message body for the text. */ + pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body)); + body->content_type.type = pj_str("text"); + body->content_type.subtype = pj_str("plain"); + body->data = pj_pool_alloc(tdata->pool, text_msg.slen); + pj_memcpy(body->data, text_msg.ptr, text_msg.slen); + body->len = text_msg.slen; + body->print_body = &pjsip_print_text_body; + + /* Assign body to message, and send the message! */ + tdata->msg->body = body; + pjsip_dlg_send_msg( global.cur_dlg, tdata ); + } + + } else { + /* Send IM to buddy list. */ + pjsip_method message; + static pj_str_t MESSAGE = { "MESSAGE", 7 }; + pjsip_method_init_np(&message, &MESSAGE); + tdata = pjsip_endpt_create_request(global.endpt, &message, + &str, + &global.real_contact, + &str, &global.real_contact, NULL, -1, + &text_msg); + if (!tdata) { + puts("Error creating request"); + return; + } + rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback); + if (rc == 0) { + printf("Sending IM message %d\n", global.im_counter); + ++global.im_counter; + } else { + printf("Error: unable to send IM message!\n"); + } + } +} + +static void ui_send_options() +{ + char line[100]; + pj_str_t str; + int selection, rc; + pjsip_tx_data *tdata; + pjsip_method options; + + if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) + return; + + pjsip_method_set( &options, PJSIP_OPTIONS_METHOD ); + + if (selection == 0) { + /* Send OPTIONS to current dialog. */ + tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1); + if (tdata) + pjsip_dlg_send_msg( global.cur_dlg, tdata ); + } else { + /* Send OPTIONS to arbitrary party. */ + tdata = pjsip_endpt_create_request( global.endpt, &options, + &str, + &global.local_uri, &str, + &global.real_contact, + NULL, -1, NULL); + if (tdata) { + rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, + &generic_request_callback); + if (rc != 0) + PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!")); + } + } +} + +static void init_presence() +{ + const pjsip_presence_cb pres_cb = { + NULL, + &pres_on_received_request, + &pres_on_received_refresh, + &pres_on_received_update, + &pres_on_terminated + }; + + pjsip_presence_init(&pres_cb); +} + +/* Subscribe presence information for all buddies. */ +static void subscribe_buddies_presence() +{ + int i; + for (i=0; i=0; --i) { + pj_thread_join(thread[i]); + pj_thread_destroy(thread[i]); + } + pj_caching_pool_destroy(&cp); + return 1; + } + } + + printf("Worker thread count: %d\n", global.worker_cnt); + + /* Perform registration, if required. */ + if (global.regc) { + update_registration(global.regc, 1); + } + + /* Initialize media manager. */ + global.mmgr = pj_med_mgr_create(global.pf); + + /* Init presence. */ + init_presence(); + + /* Subscribe presence information of all buddies. */ + if (!global.no_presence) + subscribe_buddies_presence(); + + /* Initializatio completes, loop waiting for commands. */ + for (;!global.worker_quit_flag;) { + pj_str_t str; + char line[128]; + +#if WORKER_COUNT==0 + /* If worker thread does not exist, main thread must poll for evetns. + * But this won't work very well since main thread is blocked by + * fgets(). So keep pressing the ENTER key to get the events! + */ + pj_time_val timeout = { 0, 100 }; + pjsip_endpt_handle_events(global.endpt, &timeout); + puts("Keep pressing ENTER key to get the events!"); +#endif + + printf("\nCurrent dialog: "); + print_dialog(global.cur_dlg); + puts(""); + + keystroke_help(); + + fgets(line, sizeof(line), stdin); + + switch (*line) { + case 'm': + puts("Make outgoing call"); + if (ui_input_url(&str, line, sizeof(line), &i) != NULL) { + pjsip_dlg *dlg = make_call(&str); + if (global.cur_dlg == NULL) { + global.cur_dlg = dlg; + } + } + break; + case 'i': + puts("Send Instant Messaging"); + ui_send_im_message(); + break; + case 'o': + puts("Send OPTIONS"); + ui_send_options(); + break; + case 'a': + if (global.cur_dlg) { + unsigned code; + pjsip_tx_data *tdata; + struct dialog_data *dlg_data = global.cur_dlg->user_data; + + printf("Answer with status code (1xx-6xx): "); + fflush(stdout); + fgets(line, sizeof(line), stdin); + str = pj_str(line); + str.slen -= 1; + + code = pj_strtoul(&str); + tdata = pjsip_dlg_answer(global.cur_dlg, code); + if (tdata) { + if (code/100 == 2) { + tdata->msg->body = dlg_data->body; + } + pjsip_dlg_send_msg(global.cur_dlg, tdata); + + } + } else { + puts("No current dialog"); + } + break; + case 'h': + if (global.cur_dlg) { + pjsip_tx_data *tdata; + tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE); + if (tdata) { + pjsip_dlg_send_msg(global.cur_dlg, tdata); + } + } else { + puts("No current dialog"); + } + break; + case ']': + if (global.cur_dlg) { + global.cur_dlg = global.cur_dlg->next; + if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { + global.cur_dlg = global.cur_dlg->next; + } + } else { + puts("No current dialog"); + } + break; + case '[': + if (global.cur_dlg) { + global.cur_dlg = global.cur_dlg->prev; + if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { + global.cur_dlg = global.cur_dlg->prev; + } + } else { + puts("No current dialog"); + } + break; + case 'd': + pjsip_endpt_dump(global.endpt, *(line+1)=='1'); + pjsip_ua_dump(global.user_agent); + break; + case 's': + if (*(line+1) == 'u') + subscribe_buddies_presence(); + break; + case 'u': + if (*(line+1) == 's') + unsubscribe_presence(); + break; + case 't': + global.hide_status = !global.hide_status; + update_im_status(); + break; + case 'q': + goto on_exit; + case 'l': + print_all_dialogs(); + break; + } + } + +on_exit: + /* Unregister, if required. */ + if (global.regc) { + update_registration(global.regc, 0); + } + + /* Unsubscribe presence. */ + unsubscribe_presence(); + + /* Allow one second to get all events. */ + if (1) { + pj_time_val end_time; + + pj_gettimeofday(&end_time); + end_time.sec++; + + PJ_LOG(3,(THIS_FILE, "Shutting down..")); + for (;;) { + pj_time_val timeout = { 0, 20 }, now; + pjsip_endpt_handle_events (global.endpt, &timeout); + pj_gettimeofday(&now); + PJ_TIME_VAL_SUB(now, end_time); + if (now.sec >= 1) + break; + } + } + + global.worker_quit_flag = 1; + + pj_med_mgr_destroy(global.mmgr); + + /* Wait all threads to quit. */ + for (i=0; imod_data; + modules[(*count)++] = pjsip_messaging_get_module(); + modules[(*count)++] = pjsip_event_sub_get_module(); + + return PJ_SUCCESS; +} diff --git a/pjsip/src/pjsua/misc.c b/pjsip/src/pjsua/misc.c index 185870f10..927a4ec68 100644 --- a/pjsip/src/pjsua/misc.c +++ b/pjsip/src/pjsua/misc.c @@ -1,468 +1,468 @@ -/* $Header: /pjproject/pjsip/src/pjsua/misc.c 21 6/23/05 12:36a Bennylp $ */ - -/* - * THIS FILE IS INCLUDED BY main.c. - * IT WON'T COMPILE BY ITSELF. - */ - -#include "getopt.h" -#include - - -/* - * Display program usage - */ -static void usage() -{ - puts("Usage:"); - puts(" pjsua [options] [sip-url]"); - puts(""); - puts(" [sip-url] Default URL to invite."); - puts(""); - puts("General options:"); - puts(" --config-file=file Read the config/arguments from file."); - puts(" --log-file=fname Log to filename (default stderr)"); - puts(" --log-level=N Set log max level to N (0(none) to 6(trace))"); - puts(" --app-log-level=N Set log max level for stdout display to N"); - puts(" --help Display this help screen"); - puts(" --version Display version info"); - puts(""); - puts("Media options:"); - puts(" --null-audio Use NULL audio device"); - puts(""); - puts("User Agent options:"); - puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds."); - puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds."); - puts(""); - puts("SIP options:"); - puts(" --local-port=port Set TCP/UDP port"); - puts(" --id=url Set the URL of local ID (used in From header)"); - puts(" --contact=url Override the Contact information"); - puts(" --proxy=url Set the URL of proxy server"); - puts(" --outbound=url Set the URL of outbound proxy server"); - puts(" --registrar=url Set the URL of registrar server"); - puts(" --reg-timeout=secs Set registration interval to secs (default 3600)"); - puts(""); - puts("Authentication options:"); - puts(" --realm=string Set realm"); - puts(" --username=string Set authentication username"); - puts(" --password=string Set authentication password"); - puts(""); - puts("STUN options (all must be specified):"); - puts(" --use-stun1=host[:port]"); - puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers"); - puts(""); - puts("SIMPLE options (may be specified more than once):"); - puts(" --add-buddy url Add the specified URL to the buddy list."); - puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE"); - puts(" --no-presence Do not subscribe presence of buddies"); - puts(""); - fflush(stdout); -} - -/* Display keystroke help. */ -static void keystroke_help() -{ - int i; - - printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online")); - puts(""); - puts("Buddy list:"); - puts("-------------------------------------------------------------------------------"); - for (i=0; i\n", i+1, global.buddy[i].ptr, - (global.buddy_status[i]?"Online":"Offline")); - } - //printf("-------------------------------------\n"); - puts(""); - //puts("Commands:"); - puts("+=============================================================================+"); - puts("| Call Commands: | IM & Presence: | Misc: |"); - puts("| | | |"); - puts("| m Make new call | i Send IM | o Send OPTIONS |"); - puts("| a Answer call | su Subscribe presence | d Dump status |"); - puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |"); - puts("| ] Select next dialog | t Toggle Online status | |"); - puts("| [ Select previous dialog | | |"); - puts("+-----------------------------------------------------------------------------+"); - puts("| q QUIT |"); - puts("+=============================================================================+"); - puts(""); - - - fflush(stdout); -} - -/* - * Verify that valid SIP url is given. - */ -static pj_status_t verify_sip_url(char *url) -{ - pjsip_uri *p; - pj_pool_t *pool; - int len = (url ? strlen(url) : 0); - - if (!len) return -1; - - pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL); - if (!pool) return -1; - - p = pjsip_parse_uri(pool, url, len, 0); - if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) - p = NULL; - - pj_pool_release(pool); - return p ? 0 : -1; -} - -/* - * Read command arguments from config file. - */ -static int read_config_file(pj_pool_t *pool, const char *filename, - int *app_argc, char ***app_argv) -{ - int i; - FILE *fhnd; - char line[200]; - int argc = 0; - char **argv; - enum { MAX_ARGS = 64 }; - - /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ - argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); - argv[argc++] = *app_argv[0]; - - /* Open config file. */ - fhnd = fopen(filename, "rt"); - if (!fhnd) { - printf("Unable to open config file %s\n", filename); - return -1; - } - - /* Scan tokens in the file. */ - while (argc < MAX_ARGS && !feof(fhnd)) { - char *token, *p = line; - - if (fgets(line, sizeof(line), fhnd) == NULL) break; - - for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; - token = strtok(NULL, " \t\r\n")) - { - int token_len; - - if (!token) break; - if (*token == '#') break; - - token_len = strlen(token); - if (!token_len) - continue; - argv[argc] = pj_pool_alloc(pool, token_len+1); - pj_memcpy(argv[argc], token, token_len+1); - ++argc; - } - } - - /* Copy arguments from command line */ - for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) - argv[argc++] = (*app_argv)[i]; - - if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { - printf("Too many arguments specified in cmd line/config file\n"); - fclose(fhnd); - return -1; - } - - fclose(fhnd); - - /* Assign the new command line back to the original command line. */ - *app_argc = argc; - *app_argv = argv; - return 0; - -} - -/* - * Parse program arguments - */ -static int parse_args(pj_pool_t *pool, int argc, char *argv[]) -{ - int c; - int option_index; - enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, - OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, - OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, - OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, - OPT_REALM, OPT_USERNAME, OPT_PASSWORD, - OPT_USE_STUN1, OPT_USE_STUN2, - OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, - OPT_AUTO_ANSWER, OPT_AUTO_HANGUP}; - struct option long_options[] = { - { "config-file",1, 0, OPT_CONFIG_FILE}, - { "log-file", 1, 0, OPT_LOG_FILE}, - { "log-level", 1, 0, OPT_LOG_LEVEL}, - { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, - { "help", 0, 0, OPT_HELP}, - { "version", 0, 0, OPT_VERSION}, - { "null-audio", 0, 0, OPT_NULL_AUDIO}, - { "local-port", 1, 0, OPT_LOCAL_PORT}, - { "proxy", 1, 0, OPT_PROXY}, - { "outbound", 1, 0, OPT_OUTBOUND_PROXY}, - { "registrar", 1, 0, OPT_REGISTRAR}, - { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, - { "id", 1, 0, OPT_ID}, - { "contact", 1, 0, OPT_CONTACT}, - { "realm", 1, 0, OPT_REALM}, - { "username", 1, 0, OPT_USERNAME}, - { "password", 1, 0, OPT_PASSWORD}, - { "use-stun1", 1, 0, OPT_USE_STUN1}, - { "use-stun2", 1, 0, OPT_USE_STUN2}, - { "add-buddy", 1, 0, OPT_ADD_BUDDY}, - { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, - { "no-presence", 0, 0, OPT_NO_PRESENCE}, - { "auto-answer",1, 0, OPT_AUTO_ANSWER}, - { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, - { NULL, 0, 0, 0} - }; - char *config_file = NULL; - - /* Run getopt once to see if user specifies config file to read. */ - while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { - switch (c) { - case 0: - config_file = optarg; - break; - } - if (config_file) - break; - } - - if (config_file) { - if (read_config_file(pool, config_file, &argc, &argv) != 0) - return -1; - } - - /* Reinitialize and re-run getopt again, possibly with new arguments - * read from config file. - */ - optind = 0; - while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { - char *err, *p; - - switch (c) { - case OPT_LOG_FILE: - global.log_filename = optarg; - break; - case OPT_LOG_LEVEL: - c = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value 0-6 for --log-level\n"); - return -1; - } - pj_log_set_level( c ); - break; - case OPT_APP_LOG_LEVEL: - global.app_log_level = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value 0-6 for --app-log-level\n"); - return -1; - } - break; - case OPT_HELP: - usage(); - return -1; - case OPT_VERSION: /* version */ - pj_dump_config(); - return -1; - case OPT_NULL_AUDIO: - global.null_audio = 1; - break; - case OPT_LOCAL_PORT: /* local-port */ - global.sip_port = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --local-port\n"); - return -1; - } - break; - case OPT_PROXY: /* proxy */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg); - return -1; - } - global.proxy = pj_str(optarg); - break; - case OPT_OUTBOUND_PROXY: /* outbound proxy */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg); - return -1; - } - global.outbound_proxy = pj_str(optarg); - break; - case OPT_REGISTRAR: /* registrar */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg); - return -1; - } - global.registrar_uri = pj_str(optarg); - break; - case OPT_REG_TIMEOUT: /* reg-timeout */ - global.reg_timeout = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --reg-timeout\n"); - return -1; - } - break; - case OPT_ID: /* id */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in local id argument\n", optarg); - return -1; - } - global.local_uri = pj_str(optarg); - break; - case OPT_CONTACT: /* contact */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in contact argument\n", optarg); - return -1; - } - global.contact = pj_str(optarg); - break; - case OPT_USERNAME: /* Default authentication user */ - if (!global.cred_count) global.cred_count = 1; - global.cred_info[0].username = pj_str(optarg); - break; - case OPT_REALM: /* Default authentication realm. */ - if (!global.cred_count) global.cred_count = 1; - global.cred_info[0].realm = pj_str(optarg); - break; - case OPT_PASSWORD: /* authentication password */ - if (!global.cred_count) global.cred_count = 1; - global.cred_info[0].data_type = 0; - global.cred_info[0].data = pj_str(optarg); - break; - case OPT_USE_STUN1: /* STUN server 1 */ - p = strchr(optarg, ':'); - if (p) { - *p = '\0'; - global.stun_srv1 = pj_str(optarg); - global.stun_port1 = strtoul(p+1, &err, 10); - if (*err || global.stun_port1==0) { - printf("Error: expecting port number with option --use-stun1\n"); - return -1; - } - } else { - global.stun_port1 = 3478; - global.stun_srv1 = pj_str(optarg); - } - break; - case OPT_USE_STUN2: /* STUN server 2 */ - p = strchr(optarg, ':'); - if (p) { - *p = '\0'; - global.stun_srv2 = pj_str(optarg); - global.stun_port2 = strtoul(p+1, &err, 10); - if (*err || global.stun_port2==0) { - printf("Error: expecting port number with option --use-stun2\n"); - return -1; - } - } else { - global.stun_port2 = 3478; - global.stun_srv2 = pj_str(optarg); - } - break; - case OPT_ADD_BUDDY: /* Add to buddy list. */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid URL '%s' in --add-buddy option\n", optarg); - return -1; - } - if (global.buddy_cnt == MAX_BUDDIES) { - printf("Error: too many buddies in buddy list.\n"); - return -1; - } - global.buddy[global.buddy_cnt++] = pj_str(optarg); - break; - case OPT_OFFER_X_MS_MSG: - global.offer_x_ms_msg = 1; - break; - case OPT_NO_PRESENCE: - global.no_presence = 1; - break; - case OPT_AUTO_ANSWER: - global.auto_answer = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --auto-answer option\n"); - return -1; - } - break; - case OPT_AUTO_HANGUP: - global.auto_hangup = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --auto-hangup option\n"); - return -1; - } - break; - } - } - - if (optind != argc) { - printf("Error: unknown options %s\n", argv[optind]); - return -1; - } - - if (global.reg_timeout == 0) - global.reg_timeout = 3600; - - return 0; -} - -/* Print dialog. */ -static void print_dialog(pjsip_dlg *dlg) -{ - if (!dlg) { - puts("none"); - return; - } - - printf("%s: call-id=%.*s", dlg->obj_name, - (int)dlg->call_id->id.slen, - dlg->call_id->id.ptr); - - printf(" (%s, %s)\n", pjsip_role_name(dlg->role), - pjsip_dlg_state_str(dlg->state)); -} - -/* Dump media statistic */ -void dump_media_statistic(pjsip_dlg *dlg) -{ - struct dialog_data *dlg_data = dlg->user_data; - pj_media_stream_stat stat[2]; - const char *statname[2] = { "TX", "RX" }; - int i; - - pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]); - - printf("Media statistic:\n"); - for (i=0; i<2; ++i) { - printf(" %s statistics:\n", statname[i]); - printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx); - printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx); - printf(" Jitter %d ms\n", stat[i].jitter); - printf(" Pkt lost %d\n", stat[i].pkt_lost); - } - printf("\n"); -} - -/* Print all dialogs. */ -static void print_all_dialogs() -{ - pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next; - - puts("List all dialogs:"); - - while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) { - printf("%c", (dlg==global.cur_dlg ? '*' : ' ')); - print_dialog(dlg); - dlg = dlg->next; - } - - puts(""); -} - +/* $Header: /pjproject/pjsip/src/pjsua/misc.c 21 6/23/05 12:36a Bennylp $ */ + +/* + * THIS FILE IS INCLUDED BY main.c. + * IT WON'T COMPILE BY ITSELF. + */ + +#include "getopt.h" +#include + + +/* + * Display program usage + */ +static void usage() +{ + puts("Usage:"); + puts(" pjsua [options] [sip-url]"); + puts(""); + puts(" [sip-url] Default URL to invite."); + puts(""); + puts("General options:"); + puts(" --config-file=file Read the config/arguments from file."); + puts(" --log-file=fname Log to filename (default stderr)"); + puts(" --log-level=N Set log max level to N (0(none) to 6(trace))"); + puts(" --app-log-level=N Set log max level for stdout display to N"); + puts(" --help Display this help screen"); + puts(" --version Display version info"); + puts(""); + puts("Media options:"); + puts(" --null-audio Use NULL audio device"); + puts(""); + puts("User Agent options:"); + puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds."); + puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds."); + puts(""); + puts("SIP options:"); + puts(" --local-port=port Set TCP/UDP port"); + puts(" --id=url Set the URL of local ID (used in From header)"); + puts(" --contact=url Override the Contact information"); + puts(" --proxy=url Set the URL of proxy server"); + puts(" --outbound=url Set the URL of outbound proxy server"); + puts(" --registrar=url Set the URL of registrar server"); + puts(" --reg-timeout=secs Set registration interval to secs (default 3600)"); + puts(""); + puts("Authentication options:"); + puts(" --realm=string Set realm"); + puts(" --username=string Set authentication username"); + puts(" --password=string Set authentication password"); + puts(""); + puts("STUN options (all must be specified):"); + puts(" --use-stun1=host[:port]"); + puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers"); + puts(""); + puts("SIMPLE options (may be specified more than once):"); + puts(" --add-buddy url Add the specified URL to the buddy list."); + puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE"); + puts(" --no-presence Do not subscribe presence of buddies"); + puts(""); + fflush(stdout); +} + +/* Display keystroke help. */ +static void keystroke_help() +{ + int i; + + printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online")); + puts(""); + puts("Buddy list:"); + puts("-------------------------------------------------------------------------------"); + for (i=0; i\n", i+1, global.buddy[i].ptr, + (global.buddy_status[i]?"Online":"Offline")); + } + //printf("-------------------------------------\n"); + puts(""); + //puts("Commands:"); + puts("+=============================================================================+"); + puts("| Call Commands: | IM & Presence: | Misc: |"); + puts("| | | |"); + puts("| m Make new call | i Send IM | o Send OPTIONS |"); + puts("| a Answer call | su Subscribe presence | d Dump status |"); + puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |"); + puts("| ] Select next dialog | t Toggle Online status | |"); + puts("| [ Select previous dialog | | |"); + puts("+-----------------------------------------------------------------------------+"); + puts("| q QUIT |"); + puts("+=============================================================================+"); + puts(""); + + + fflush(stdout); +} + +/* + * Verify that valid SIP url is given. + */ +static pj_status_t verify_sip_url(char *url) +{ + pjsip_uri *p; + pj_pool_t *pool; + int len = (url ? strlen(url) : 0); + + if (!len) return -1; + + pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL); + if (!pool) return -1; + + p = pjsip_parse_uri(pool, url, len, 0); + if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) + p = NULL; + + pj_pool_release(pool); + return p ? 0 : -1; +} + +/* + * Read command arguments from config file. + */ +static int read_config_file(pj_pool_t *pool, const char *filename, + int *app_argc, char ***app_argv) +{ + int i; + FILE *fhnd; + char line[200]; + int argc = 0; + char **argv; + enum { MAX_ARGS = 64 }; + + /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ + argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); + argv[argc++] = *app_argv[0]; + + /* Open config file. */ + fhnd = fopen(filename, "rt"); + if (!fhnd) { + printf("Unable to open config file %s\n", filename); + return -1; + } + + /* Scan tokens in the file. */ + while (argc < MAX_ARGS && !feof(fhnd)) { + char *token, *p = line; + + if (fgets(line, sizeof(line), fhnd) == NULL) break; + + for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; + token = strtok(NULL, " \t\r\n")) + { + int token_len; + + if (!token) break; + if (*token == '#') break; + + token_len = strlen(token); + if (!token_len) + continue; + argv[argc] = pj_pool_alloc(pool, token_len+1); + pj_memcpy(argv[argc], token, token_len+1); + ++argc; + } + } + + /* Copy arguments from command line */ + for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) + argv[argc++] = (*app_argv)[i]; + + if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { + printf("Too many arguments specified in cmd line/config file\n"); + fclose(fhnd); + return -1; + } + + fclose(fhnd); + + /* Assign the new command line back to the original command line. */ + *app_argc = argc; + *app_argv = argv; + return 0; + +} + +/* + * Parse program arguments + */ +static int parse_args(pj_pool_t *pool, int argc, char *argv[]) +{ + int c; + int option_index; + enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, + OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, + OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, + OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, + OPT_REALM, OPT_USERNAME, OPT_PASSWORD, + OPT_USE_STUN1, OPT_USE_STUN2, + OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, + OPT_AUTO_ANSWER, OPT_AUTO_HANGUP}; + struct option long_options[] = { + { "config-file",1, 0, OPT_CONFIG_FILE}, + { "log-file", 1, 0, OPT_LOG_FILE}, + { "log-level", 1, 0, OPT_LOG_LEVEL}, + { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, + { "help", 0, 0, OPT_HELP}, + { "version", 0, 0, OPT_VERSION}, + { "null-audio", 0, 0, OPT_NULL_AUDIO}, + { "local-port", 1, 0, OPT_LOCAL_PORT}, + { "proxy", 1, 0, OPT_PROXY}, + { "outbound", 1, 0, OPT_OUTBOUND_PROXY}, + { "registrar", 1, 0, OPT_REGISTRAR}, + { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, + { "id", 1, 0, OPT_ID}, + { "contact", 1, 0, OPT_CONTACT}, + { "realm", 1, 0, OPT_REALM}, + { "username", 1, 0, OPT_USERNAME}, + { "password", 1, 0, OPT_PASSWORD}, + { "use-stun1", 1, 0, OPT_USE_STUN1}, + { "use-stun2", 1, 0, OPT_USE_STUN2}, + { "add-buddy", 1, 0, OPT_ADD_BUDDY}, + { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, + { "no-presence", 0, 0, OPT_NO_PRESENCE}, + { "auto-answer",1, 0, OPT_AUTO_ANSWER}, + { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, + { NULL, 0, 0, 0} + }; + char *config_file = NULL; + + /* Run getopt once to see if user specifies config file to read. */ + while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case 0: + config_file = optarg; + break; + } + if (config_file) + break; + } + + if (config_file) { + if (read_config_file(pool, config_file, &argc, &argv) != 0) + return -1; + } + + /* Reinitialize and re-run getopt again, possibly with new arguments + * read from config file. + */ + optind = 0; + while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + char *err, *p; + + switch (c) { + case OPT_LOG_FILE: + global.log_filename = optarg; + break; + case OPT_LOG_LEVEL: + c = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value 0-6 for --log-level\n"); + return -1; + } + pj_log_set_level( c ); + break; + case OPT_APP_LOG_LEVEL: + global.app_log_level = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value 0-6 for --app-log-level\n"); + return -1; + } + break; + case OPT_HELP: + usage(); + return -1; + case OPT_VERSION: /* version */ + pj_dump_config(); + return -1; + case OPT_NULL_AUDIO: + global.null_audio = 1; + break; + case OPT_LOCAL_PORT: /* local-port */ + global.sip_port = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --local-port\n"); + return -1; + } + break; + case OPT_PROXY: /* proxy */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg); + return -1; + } + global.proxy = pj_str(optarg); + break; + case OPT_OUTBOUND_PROXY: /* outbound proxy */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg); + return -1; + } + global.outbound_proxy = pj_str(optarg); + break; + case OPT_REGISTRAR: /* registrar */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg); + return -1; + } + global.registrar_uri = pj_str(optarg); + break; + case OPT_REG_TIMEOUT: /* reg-timeout */ + global.reg_timeout = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --reg-timeout\n"); + return -1; + } + break; + case OPT_ID: /* id */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in local id argument\n", optarg); + return -1; + } + global.local_uri = pj_str(optarg); + break; + case OPT_CONTACT: /* contact */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in contact argument\n", optarg); + return -1; + } + global.contact = pj_str(optarg); + break; + case OPT_USERNAME: /* Default authentication user */ + if (!global.cred_count) global.cred_count = 1; + global.cred_info[0].username = pj_str(optarg); + break; + case OPT_REALM: /* Default authentication realm. */ + if (!global.cred_count) global.cred_count = 1; + global.cred_info[0].realm = pj_str(optarg); + break; + case OPT_PASSWORD: /* authentication password */ + if (!global.cred_count) global.cred_count = 1; + global.cred_info[0].data_type = 0; + global.cred_info[0].data = pj_str(optarg); + break; + case OPT_USE_STUN1: /* STUN server 1 */ + p = strchr(optarg, ':'); + if (p) { + *p = '\0'; + global.stun_srv1 = pj_str(optarg); + global.stun_port1 = strtoul(p+1, &err, 10); + if (*err || global.stun_port1==0) { + printf("Error: expecting port number with option --use-stun1\n"); + return -1; + } + } else { + global.stun_port1 = 3478; + global.stun_srv1 = pj_str(optarg); + } + break; + case OPT_USE_STUN2: /* STUN server 2 */ + p = strchr(optarg, ':'); + if (p) { + *p = '\0'; + global.stun_srv2 = pj_str(optarg); + global.stun_port2 = strtoul(p+1, &err, 10); + if (*err || global.stun_port2==0) { + printf("Error: expecting port number with option --use-stun2\n"); + return -1; + } + } else { + global.stun_port2 = 3478; + global.stun_srv2 = pj_str(optarg); + } + break; + case OPT_ADD_BUDDY: /* Add to buddy list. */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid URL '%s' in --add-buddy option\n", optarg); + return -1; + } + if (global.buddy_cnt == MAX_BUDDIES) { + printf("Error: too many buddies in buddy list.\n"); + return -1; + } + global.buddy[global.buddy_cnt++] = pj_str(optarg); + break; + case OPT_OFFER_X_MS_MSG: + global.offer_x_ms_msg = 1; + break; + case OPT_NO_PRESENCE: + global.no_presence = 1; + break; + case OPT_AUTO_ANSWER: + global.auto_answer = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --auto-answer option\n"); + return -1; + } + break; + case OPT_AUTO_HANGUP: + global.auto_hangup = strtoul(optarg, &err, 10); + if (*err) { + printf("Error: expecting integer value for --auto-hangup option\n"); + return -1; + } + break; + } + } + + if (optind != argc) { + printf("Error: unknown options %s\n", argv[optind]); + return -1; + } + + if (global.reg_timeout == 0) + global.reg_timeout = 3600; + + return 0; +} + +/* Print dialog. */ +static void print_dialog(pjsip_dlg *dlg) +{ + if (!dlg) { + puts("none"); + return; + } + + printf("%s: call-id=%.*s", dlg->obj_name, + (int)dlg->call_id->id.slen, + dlg->call_id->id.ptr); + + printf(" (%s, %s)\n", pjsip_role_name(dlg->role), + pjsip_dlg_state_str(dlg->state)); +} + +/* Dump media statistic */ +void dump_media_statistic(pjsip_dlg *dlg) +{ + struct dialog_data *dlg_data = dlg->user_data; + pj_media_stream_stat stat[2]; + const char *statname[2] = { "TX", "RX" }; + int i; + + pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]); + + printf("Media statistic:\n"); + for (i=0; i<2; ++i) { + printf(" %s statistics:\n", statname[i]); + printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx); + printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx); + printf(" Jitter %d ms\n", stat[i].jitter); + printf(" Pkt lost %d\n", stat[i].pkt_lost); + } + printf("\n"); +} + +/* Print all dialogs. */ +static void print_all_dialogs() +{ + pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next; + + puts("List all dialogs:"); + + while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) { + printf("%c", (dlg==global.cur_dlg ? '*' : ' ')); + print_dialog(dlg); + dlg = dlg->next; + } + + puts(""); +} + diff --git a/pjsip/src/tests/pjsip_core/main.c b/pjsip/src/tests/pjsip_core/main.c index 60f2c3f53..c4c0f5581 100644 --- a/pjsip/src/tests/pjsip_core/main.c +++ b/pjsip/src/tests/pjsip_core/main.c @@ -1,15 +1,15 @@ -/* $Header: /pjproject/pjsip/src/tests/pjsip_core/main.c 2 2/24/05 10:46a Bennylp $ */ -#include "test.h" -#include - -int main() -{ - test_uri(); - test_msg(); - -#if !IS_PROFILING - puts("Press to quit."); - fgets( s, sizeof(s), stdin); -#endif - return 0; -} +/* $Header: /pjproject/pjsip/src/tests/pjsip_core/main.c 2 2/24/05 10:46a Bennylp $ */ +#include "test.h" +#include + +int main() +{ + test_uri(); + test_msg(); + +#if !IS_PROFILING + puts("Press to quit."); + fgets( s, sizeof(s), stdin); +#endif + return 0; +} diff --git a/pjsip/src/tests/pjsip_core/test.h b/pjsip/src/tests/pjsip_core/test.h index b5433dba6..6e62aca81 100644 --- a/pjsip/src/tests/pjsip_core/test.h +++ b/pjsip/src/tests/pjsip_core/test.h @@ -1,9 +1,9 @@ -/* $Header: /pjproject/pjsip/src/tests/pjsip_core/test.h 2 2/24/05 10:46a Bennylp $ */ -#include - -pj_status_t test_uri(void); -pj_status_t test_msg(void); - -#define SILENT 1 -#define IS_PROFILING 1 -#define LOOP 2000 +/* $Header: /pjproject/pjsip/src/tests/pjsip_core/test.h 2 2/24/05 10:46a Bennylp $ */ +#include + +pj_status_t test_uri(void); +pj_status_t test_msg(void); + +#define SILENT 1 +#define IS_PROFILING 1 +#define LOOP 2000 diff --git a/pjsip/src/tests/pjsip_core/test_msg.c b/pjsip/src/tests/pjsip_core/test_msg.c index 67a666eea..ebabc4b75 100644 --- a/pjsip/src/tests/pjsip_core/test_msg.c +++ b/pjsip/src/tests/pjsip_core/test_msg.c @@ -1,423 +1,423 @@ -/* $Header: /pjproject-0.3/pjsip/src/tests/pjsip_core/test_msg.c 10 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include "test.h" - -#define ERR_SYNTAX_ERR (-2) -#define ERR_NOT_EQUAL (-3) -#define ERR_SYSTEM (-4) - - -static pjsip_msg *create_msg0(pj_pool_t *pool); - -struct test_msg -{ - char msg[1024]; - pjsip_msg *(*creator)(pj_pool_t *pool); - pj_size_t len; -} test_array[] = -{ - { - /* 'Normal' message with all headers. */ - "INVITE sip:user@foo SIP/2.0\n" - "From: Hi I'm Joe ;tag=1234578901234567890\r" - "To: Fellow User \r\n" - "Call-ID: 12345678901234567890@bar\r\n" - "Content-Length: 0\r\n" - "CSeq: 123456 INVITE\n" - "Contact: ; q=0.5;expires=3600,sip:user@host;q=0.500\r" - " ,sip:user2@host2\n" - "Content-Type: text/html ; charset=ISO-8859-4\r" - "Route: ,\r\n" - " \r" - "Record-Route: ,\r\n" - " \n" - "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" - "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" - " ;received=192.0.2.1\r\n" - "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n" - "Organization: \r" - "Max-Forwards: 70\n" - "X-Header: \r\n" - "\r", - &create_msg0 - } -}; - -static pj_caching_pool cp; -static pj_pool_factory *pf = &cp.factory; -static pj_uint32_t parse_len, parse_time, print_time; - -static void pool_error(pj_pool_t *pool, pj_size_t sz) -{ - PJ_UNUSED_ARG(pool) - PJ_UNUSED_ARG(sz) - - pj_assert(0); - exit(1); -} - -static const char *STATUS_STR(pj_status_t status) -{ - switch (status) { - case 0: return "OK"; - case ERR_SYNTAX_ERR: return "Syntax Error"; - case ERR_NOT_EQUAL: return "Not Equal"; - case ERR_SYSTEM: return "System Error"; - } - return "???"; -} - -static pj_status_t test_entry( struct test_msg *entry ) -{ - pjsip_msg *parsed_msg, *ref_msg; - pj_pool_t *pool; - pj_status_t status = PJ_SUCCESS; - int len; - pj_str_t str1, str2; - pjsip_hdr *hdr1, *hdr2; - pj_hr_timestamp t1, t2; - char *msgbuf; - - enum { BUFLEN = 512 }; - - pool = pj_pool_create( pf, "", - PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, - &pool_error); - - if (entry->len == 0) { - entry->len = strlen(entry->msg); - } - - /* Parse message. */ - parse_len += entry->len; - pj_hr_gettimestamp(&t1); - parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL); - if (parsed_msg == NULL) { - status = ERR_SYNTAX_ERR; - goto on_return; - } - pj_hr_gettimestamp(&t2); - parse_time += t2.u32.lo - t1.u32.lo; - -#if IS_PROFILING - goto print_msg; -#endif - - /* Create reference message. */ - ref_msg = entry->creator(pool); - - /* Create buffer for comparison. */ - str1.ptr = pj_pool_alloc(pool, BUFLEN); - str2.ptr = pj_pool_alloc(pool, BUFLEN); - - /* Compare message type. */ - if (parsed_msg->type != ref_msg->type) { - status = ERR_NOT_EQUAL; - goto on_return; - } - - /* Compare request or status line. */ - if (parsed_msg->type == PJSIP_REQUEST_MSG) { - pjsip_method *m1 = &parsed_msg->line.req.method; - pjsip_method *m2 = &ref_msg->line.req.method; - - if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) { - status = ERR_NOT_EQUAL; - goto on_return; - } - } else { - - } - - /* Compare headers. */ - hdr1 = parsed_msg->hdr.next; - hdr2 = ref_msg->hdr.next; - - while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) { - len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN); - if (len < 1) { - status = ERR_SYSTEM; - goto on_return; - } - str1.slen = len; - - len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN); - if (len < 1) { - status = ERR_SYSTEM; - goto on_return; - } - str2.slen = len; - - if (!SILENT) { - printf("hdr1='%.*s'\n" - "hdr2='%.*s'\n\n", - str1.slen, str1.ptr, - str2.slen, str2.ptr); - } - if (pj_strcmp(&str1, &str2) != 0) { - status = ERR_NOT_EQUAL; - goto on_return; - } - - hdr1 = hdr1->next; - hdr2 = hdr2->next; - } - - if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) { - status = ERR_NOT_EQUAL; - goto on_return; - } - - /* Print message. */ -#if IS_PROFILING -print_msg: -#endif - msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN); - if (msgbuf == NULL) { - status = ERR_SYSTEM; - goto on_return; - } - pj_hr_gettimestamp(&t1); - len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN); - if (len < 1) { - status = ERR_SYSTEM; - goto on_return; - } - pj_hr_gettimestamp(&t2); - print_time += t2.u32.lo - t1.u32.lo; - status = PJ_SUCCESS; - -on_return: - pj_pool_release(pool); - return status; -} - -static void warm_up() -{ - pj_pool_t *pool; - pool = pj_pool_create( pf, "", - PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, - &pool_error); - pj_pool_release(pool); -} - - -pj_status_t test_msg(void) -{ - pj_status_t status; - unsigned i; - - pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); - warm_up(); - - for (i=0; iline.req.method, PJSIP_INVITE_METHOD); - url = pjsip_url_create(pool, 0); - msg->line.req.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "foo"); - - /* "From: Hi I'm Joe ;tag=1234578901234567890\r" */ - fromto = pjsip_from_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); - pj_strdup2(pool, &fromto->tag, "1234578901234567890"); - name_addr = pjsip_name_addr_create(pool); - fromto->uri = (pjsip_uri*)name_addr; - pj_strdup2(pool, &name_addr->display, "Hi I'm Joe"); - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "joe.user"); - pj_strdup2(pool, &url->host, "bar.otherdomain.com"); - - /* "To: Fellow User \r\n" */ - fromto = pjsip_to_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); - name_addr = pjsip_name_addr_create(pool); - fromto->uri = (pjsip_uri*)name_addr; - pj_strdup2(pool, &name_addr->display, "Fellow User"); - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "foo.bar.domain.com"); - - /* "Call-ID: 12345678901234567890@bar\r\n" */ - cid = pjsip_cid_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid); - pj_strdup2(pool, &cid->id, "12345678901234567890@bar"); - - /* "Content-Length: 0\r\n" */ - clen = pjsip_clen_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen); - clen->len = 0; - - /* "CSeq: 123456 INVITE\n" */ - cseq = pjsip_cseq_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq); - cseq->cseq = 123456; - pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD); - - /* "Contact: ;q=0.5;expires=3600*/ - contact = pjsip_contact_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); - contact->q1000 = 500; - contact->expires = 3600; - name_addr = pjsip_name_addr_create(pool); - contact->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "joe"); - pj_strdup2(pool, &url->host, "bar"); - - /*, sip:user@host;q=0.500\r" */ - contact = pjsip_contact_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); - contact->q1000 = 500; - url = pjsip_url_create(pool, 0); - contact->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "host"); - - /* " ,sip:user2@host2\n" */ - contact = pjsip_contact_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); - url = pjsip_url_create(pool, 0); - contact->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user2"); - pj_strdup2(pool, &url->host, "host2"); - - /* "Content-Type: text/html; charset=ISO-8859-4\r" */ - ctype = pjsip_ctype_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype); - pj_strdup2(pool, &ctype->media.type, "text"); - pj_strdup2(pool, &ctype->media.subtype, "html"); - pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4"); - - /* "Route: ,\r\n" */ - routing = pjsip_route_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); - url->lr_param = 1; - - /* " \r" */ - routing = pjsip_route_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "server10.biloxi.com"); - url->lr_param = 1; - - /* "Record-Route: ,\r\n" */ - routing = pjsip_rr_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "server10.biloxi.com"); - url->lr_param = 0; - - /* " \n" */ - routing = pjsip_rr_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); - url->lr_param = 1; - - /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "SCTP"); - pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com"); - pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1"); - - /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" - " ;received=192.0.2.1\r\n" */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "UDP"); - pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com"); - pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8"); - pj_strdup2(pool, &via->recvd_param, "192.0.2.1"); - - - /* "Via: SIP/2.0/UDP 10.2.1.1, */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "UDP"); - pj_strdup2(pool, &via->sent_by.host, "10.2.1.1"); - - - /*SIP/2.0/TCP 192.168.1.1\n" */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "TCP"); - pj_strdup2(pool, &via->sent_by.host, "192.168.1.1"); - - /* "Organization: \r" */ - str.ptr = "Organization"; - str.slen = 12; - generic = pjsip_generic_string_hdr_create(pool, &str); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); - generic->hvalue.ptr = NULL; - generic->hvalue.slen = 0; - - /* "Max-Forwards: 70\n" */ - str.ptr = "Max-Forwards"; - str.slen = 12; - generic = pjsip_generic_string_hdr_create(pool, &str); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); - str.ptr = "70"; - str.slen = 2; - generic->hvalue = str; - - /* "X-Header: \r\n" */ - str.ptr = "X-Header"; - str.slen = 8; - generic = pjsip_generic_string_hdr_create(pool, &str); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); - str.ptr = NULL; - str.slen = 0; - generic->hvalue = str; - - return msg; -} +/* $Header: /pjproject-0.3/pjsip/src/tests/pjsip_core/test_msg.c 10 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include "test.h" + +#define ERR_SYNTAX_ERR (-2) +#define ERR_NOT_EQUAL (-3) +#define ERR_SYSTEM (-4) + + +static pjsip_msg *create_msg0(pj_pool_t *pool); + +struct test_msg +{ + char msg[1024]; + pjsip_msg *(*creator)(pj_pool_t *pool); + pj_size_t len; +} test_array[] = +{ + { + /* 'Normal' message with all headers. */ + "INVITE sip:user@foo SIP/2.0\n" + "From: Hi I'm Joe ;tag=1234578901234567890\r" + "To: Fellow User \r\n" + "Call-ID: 12345678901234567890@bar\r\n" + "Content-Length: 0\r\n" + "CSeq: 123456 INVITE\n" + "Contact: ; q=0.5;expires=3600,sip:user@host;q=0.500\r" + " ,sip:user2@host2\n" + "Content-Type: text/html ; charset=ISO-8859-4\r" + "Route: ,\r\n" + " \r" + "Record-Route: ,\r\n" + " \n" + "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" + " ;received=192.0.2.1\r\n" + "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n" + "Organization: \r" + "Max-Forwards: 70\n" + "X-Header: \r\n" + "\r", + &create_msg0 + } +}; + +static pj_caching_pool cp; +static pj_pool_factory *pf = &cp.factory; +static pj_uint32_t parse_len, parse_time, print_time; + +static void pool_error(pj_pool_t *pool, pj_size_t sz) +{ + PJ_UNUSED_ARG(pool) + PJ_UNUSED_ARG(sz) + + pj_assert(0); + exit(1); +} + +static const char *STATUS_STR(pj_status_t status) +{ + switch (status) { + case 0: return "OK"; + case ERR_SYNTAX_ERR: return "Syntax Error"; + case ERR_NOT_EQUAL: return "Not Equal"; + case ERR_SYSTEM: return "System Error"; + } + return "???"; +} + +static pj_status_t test_entry( struct test_msg *entry ) +{ + pjsip_msg *parsed_msg, *ref_msg; + pj_pool_t *pool; + pj_status_t status = PJ_SUCCESS; + int len; + pj_str_t str1, str2; + pjsip_hdr *hdr1, *hdr2; + pj_hr_timestamp t1, t2; + char *msgbuf; + + enum { BUFLEN = 512 }; + + pool = pj_pool_create( pf, "", + PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, + &pool_error); + + if (entry->len == 0) { + entry->len = strlen(entry->msg); + } + + /* Parse message. */ + parse_len += entry->len; + pj_hr_gettimestamp(&t1); + parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL); + if (parsed_msg == NULL) { + status = ERR_SYNTAX_ERR; + goto on_return; + } + pj_hr_gettimestamp(&t2); + parse_time += t2.u32.lo - t1.u32.lo; + +#if IS_PROFILING + goto print_msg; +#endif + + /* Create reference message. */ + ref_msg = entry->creator(pool); + + /* Create buffer for comparison. */ + str1.ptr = pj_pool_alloc(pool, BUFLEN); + str2.ptr = pj_pool_alloc(pool, BUFLEN); + + /* Compare message type. */ + if (parsed_msg->type != ref_msg->type) { + status = ERR_NOT_EQUAL; + goto on_return; + } + + /* Compare request or status line. */ + if (parsed_msg->type == PJSIP_REQUEST_MSG) { + pjsip_method *m1 = &parsed_msg->line.req.method; + pjsip_method *m2 = &ref_msg->line.req.method; + + if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) { + status = ERR_NOT_EQUAL; + goto on_return; + } + } else { + + } + + /* Compare headers. */ + hdr1 = parsed_msg->hdr.next; + hdr2 = ref_msg->hdr.next; + + while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) { + len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN); + if (len < 1) { + status = ERR_SYSTEM; + goto on_return; + } + str1.slen = len; + + len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN); + if (len < 1) { + status = ERR_SYSTEM; + goto on_return; + } + str2.slen = len; + + if (!SILENT) { + printf("hdr1='%.*s'\n" + "hdr2='%.*s'\n\n", + str1.slen, str1.ptr, + str2.slen, str2.ptr); + } + if (pj_strcmp(&str1, &str2) != 0) { + status = ERR_NOT_EQUAL; + goto on_return; + } + + hdr1 = hdr1->next; + hdr2 = hdr2->next; + } + + if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) { + status = ERR_NOT_EQUAL; + goto on_return; + } + + /* Print message. */ +#if IS_PROFILING +print_msg: +#endif + msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN); + if (msgbuf == NULL) { + status = ERR_SYSTEM; + goto on_return; + } + pj_hr_gettimestamp(&t1); + len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN); + if (len < 1) { + status = ERR_SYSTEM; + goto on_return; + } + pj_hr_gettimestamp(&t2); + print_time += t2.u32.lo - t1.u32.lo; + status = PJ_SUCCESS; + +on_return: + pj_pool_release(pool); + return status; +} + +static void warm_up() +{ + pj_pool_t *pool; + pool = pj_pool_create( pf, "", + PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, + &pool_error); + pj_pool_release(pool); +} + + +pj_status_t test_msg(void) +{ + pj_status_t status; + unsigned i; + + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + warm_up(); + + for (i=0; iline.req.method, PJSIP_INVITE_METHOD); + url = pjsip_url_create(pool, 0); + msg->line.req.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "foo"); + + /* "From: Hi I'm Joe ;tag=1234578901234567890\r" */ + fromto = pjsip_from_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); + pj_strdup2(pool, &fromto->tag, "1234578901234567890"); + name_addr = pjsip_name_addr_create(pool); + fromto->uri = (pjsip_uri*)name_addr; + pj_strdup2(pool, &name_addr->display, "Hi I'm Joe"); + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "joe.user"); + pj_strdup2(pool, &url->host, "bar.otherdomain.com"); + + /* "To: Fellow User \r\n" */ + fromto = pjsip_to_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); + name_addr = pjsip_name_addr_create(pool); + fromto->uri = (pjsip_uri*)name_addr; + pj_strdup2(pool, &name_addr->display, "Fellow User"); + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "foo.bar.domain.com"); + + /* "Call-ID: 12345678901234567890@bar\r\n" */ + cid = pjsip_cid_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid); + pj_strdup2(pool, &cid->id, "12345678901234567890@bar"); + + /* "Content-Length: 0\r\n" */ + clen = pjsip_clen_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen); + clen->len = 0; + + /* "CSeq: 123456 INVITE\n" */ + cseq = pjsip_cseq_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq); + cseq->cseq = 123456; + pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD); + + /* "Contact: ;q=0.5;expires=3600*/ + contact = pjsip_contact_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); + contact->q1000 = 500; + contact->expires = 3600; + name_addr = pjsip_name_addr_create(pool); + contact->uri = (pjsip_uri*)name_addr; + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "joe"); + pj_strdup2(pool, &url->host, "bar"); + + /*, sip:user@host;q=0.500\r" */ + contact = pjsip_contact_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); + contact->q1000 = 500; + url = pjsip_url_create(pool, 0); + contact->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "host"); + + /* " ,sip:user2@host2\n" */ + contact = pjsip_contact_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); + url = pjsip_url_create(pool, 0); + contact->uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->user, "user2"); + pj_strdup2(pool, &url->host, "host2"); + + /* "Content-Type: text/html; charset=ISO-8859-4\r" */ + ctype = pjsip_ctype_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype); + pj_strdup2(pool, &ctype->media.type, "text"); + pj_strdup2(pool, &ctype->media.subtype, "html"); + pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4"); + + /* "Route: ,\r\n" */ + routing = pjsip_route_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); + url->lr_param = 1; + + /* " \r" */ + routing = pjsip_route_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "server10.biloxi.com"); + url->lr_param = 1; + + /* "Record-Route: ,\r\n" */ + routing = pjsip_rr_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "server10.biloxi.com"); + url->lr_param = 0; + + /* " \n" */ + routing = pjsip_rr_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); + url = pjsip_url_create(pool, 0); + routing->name_addr.uri = (pjsip_uri*)url; + pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); + url->lr_param = 1; + + /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "SCTP"); + pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com"); + pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1"); + + /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" + " ;received=192.0.2.1\r\n" */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "UDP"); + pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com"); + pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8"); + pj_strdup2(pool, &via->recvd_param, "192.0.2.1"); + + + /* "Via: SIP/2.0/UDP 10.2.1.1, */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "UDP"); + pj_strdup2(pool, &via->sent_by.host, "10.2.1.1"); + + + /*SIP/2.0/TCP 192.168.1.1\n" */ + via = pjsip_via_hdr_create(pool); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); + pj_strdup2(pool, &via->transport, "TCP"); + pj_strdup2(pool, &via->sent_by.host, "192.168.1.1"); + + /* "Organization: \r" */ + str.ptr = "Organization"; + str.slen = 12; + generic = pjsip_generic_string_hdr_create(pool, &str); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); + generic->hvalue.ptr = NULL; + generic->hvalue.slen = 0; + + /* "Max-Forwards: 70\n" */ + str.ptr = "Max-Forwards"; + str.slen = 12; + generic = pjsip_generic_string_hdr_create(pool, &str); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); + str.ptr = "70"; + str.slen = 2; + generic->hvalue = str; + + /* "X-Header: \r\n" */ + str.ptr = "X-Header"; + str.slen = 8; + generic = pjsip_generic_string_hdr_create(pool, &str); + pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); + str.ptr = NULL; + str.slen = 0; + generic->hvalue = str; + + return msg; +} diff --git a/pjsip/src/tests/pjsip_core/test_uri.c b/pjsip/src/tests/pjsip_core/test_uri.c index d75282452..55f2580c9 100644 --- a/pjsip/src/tests/pjsip_core/test_uri.c +++ b/pjsip/src/tests/pjsip_core/test_uri.c @@ -1,643 +1,643 @@ -/* $Header: /pjproject-0.3/pjsip/src/tests/pjsip_core/test_uri.c 9 10/14/05 12:23a Bennylp $ */ -#include -#include -#include -#include -#include -#include -#include -#include "test.h" - -#define ERR_SYNTAX_ERR (-2) -#define ERR_NOT_EQUAL (-3) - -#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "0123456789" -#define MARK "-_.!~*'()" -#define USER "&=+$,;?/%" -#define PASS "&=+$,%" -#define PARAM_CHAR "[]/:&+$" MARK "%" - -#define POOL_SIZE 4096 - -static const char *STATUS_STR(pj_status_t status) -{ - switch (status) { - case 0: return "OK"; - case ERR_SYNTAX_ERR: return "Syntax Error"; - case ERR_NOT_EQUAL: return "Not Equal"; - } - return "???"; -} - -static pj_uint32_t parse_len, parse_time, print_time; -static pj_caching_pool cp; - - -/* URI creator functions. */ -static pjsip_uri *create_uri1( pj_pool_t *pool ); -static pjsip_uri *create_uri2( pj_pool_t *pool ); -static pjsip_uri *create_uri3( pj_pool_t *pool ); -static pjsip_uri *create_uri4( pj_pool_t *pool ); -static pjsip_uri *create_uri5( pj_pool_t *pool ); -static pjsip_uri *create_uri6( pj_pool_t *pool ); -static pjsip_uri *create_uri7( pj_pool_t *pool ); -static pjsip_uri *create_uri8( pj_pool_t *pool ); -static pjsip_uri *create_uri9( pj_pool_t *pool ); -static pjsip_uri *create_uri10( pj_pool_t *pool ); -static pjsip_uri *create_uri11( pj_pool_t *pool ); -static pjsip_uri *create_uri12( pj_pool_t *pool ); -static pjsip_uri *create_uri13( pj_pool_t *pool ); -static pjsip_uri *create_uri14( pj_pool_t *pool ); -static pjsip_uri *create_uri15( pj_pool_t *pool ); -static pjsip_uri *create_uri16( pj_pool_t *pool ); -static pjsip_uri *create_uri17( pj_pool_t *pool ); -static pjsip_uri *create_uri18( pj_pool_t *pool ); -static pjsip_uri *create_uri19( pj_pool_t *pool ); -static pjsip_uri *create_dummy( pj_pool_t *pool ); - -struct uri_test -{ - pj_status_t status; - char str[PJSIP_MAX_URL_SIZE]; - pjsip_uri *(*creator)(pj_pool_t *pool); - pj_size_t len; -} uri_test_array[] = -{ - { - PJ_SUCCESS, - "sip:localhost", - &create_uri1 - }, - { - PJ_SUCCESS, - "sip:user@localhost", - &create_uri2 - }, - { - PJ_SUCCESS, - "sip:user:password@localhost:5060", - &create_uri3, - }, - { - /* Port is specified should not match unspecified port. */ - ERR_NOT_EQUAL, - "sip:localhost:5060", - &create_uri4 - }, - { - /* All recognized parameters. */ - PJ_SUCCESS, - "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK", - &create_uri5 - }, - { - /* Params mixed with other params and header params. */ - PJ_SUCCESS, - "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" - "?Subject=Hello%20There&Server=SIP%20Server", - &create_uri6 - }, - { - /* SIPS. */ - PJ_SUCCESS, - "sips:localhost", - &create_uri7, - }, - { - /* Name address */ - PJ_SUCCESS, - "", - &create_uri8 - }, - { - /* Name address with display name and SIPS scheme with some redundant - * whitespaced. - */ - PJ_SUCCESS, - " Power Administrator ", - &create_uri9 - }, - { - /* Name address. */ - PJ_SUCCESS, - " \"User\" ", - &create_uri10 - }, - { - /* Escaped sequence in display name (display=Strange User\"\\\"). */ - PJ_SUCCESS, - " \"Strange User\\\"\\\\\\\"\" ", - &create_uri11, - }, - { - /* Errorneous escaping in display name. */ - ERR_SYNTAX_ERR, - " \"Rogue User\\\" ", - &create_uri12, - }, - { - /* Dangling quote in display name, but that should be OK. */ - PJ_SUCCESS, - "Strange User\" ", - &create_uri13, - }, - { - /* Special characters in parameter value must be quoted. */ - PJ_SUCCESS, - "sip:localhost;pvalue=\"hello world\"", - &create_uri14, - }, - { - /* Excercise strange character sets allowed in display, user, password, - * host, and port. - */ - PJ_SUCCESS, - "This is -. !% *_+`'~ me ", - &create_uri15, - }, - { - /* Another excercise to the allowed character sets to the hostname. */ - PJ_SUCCESS, - "sip:" ALPHANUM "-_.com", - &create_uri16, - }, - { - /* Another excercise to the allowed character sets to the username - * and password. - */ - PJ_SUCCESS, - "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host", - &create_uri17, - }, - { - /* Excercise to the pname and pvalue, and mixup of other-param - * between 'recognized' params. - */ - PJ_SUCCESS, - "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR - ";lr;other=1;transport=sctp;other2", - &create_uri18, - }, - { - /* This should trigger syntax error. */ - ERR_SYNTAX_ERR, - "sip:", - &create_dummy, - }, - { - /* Syntax error: whitespace after scheme. */ - ERR_SYNTAX_ERR, - "sip :host", - &create_dummy, - }, - { - /* Syntax error: whitespace before hostname. */ - ERR_SYNTAX_ERR, - "sip: host", - &create_dummy, - }, - { - /* Syntax error: invalid port. */ - ERR_SYNTAX_ERR, - "sip:user:password", - &create_dummy, - }, - { - /* Syntax error: no host. */ - ERR_SYNTAX_ERR, - "sip:user@", - &create_dummy, - }, - { - /* Syntax error: no user/host. */ - ERR_SYNTAX_ERR, - "sip:@", - &create_dummy, - }, - { - /* Syntax error: empty string. */ - ERR_SYNTAX_ERR, - "", - &create_dummy, - }, - { - PJ_SUCCESS, - "", - NULL, - }, -}; - -static pjsip_uri *create_uri1(pj_pool_t *pool) -{ - /* "sip:localhost" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri2(pj_pool_t *pool) -{ - /* "sip:user@localhost" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2( pool, &url->user, "user"); - pj_strdup2( pool, &url->host, "localhost"); - - return (pjsip_uri*) url; -} - -static pjsip_uri *create_uri3(pj_pool_t *pool) -{ - /* "sip:user:password@localhost:5060" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2( pool, &url->user, "user"); - pj_strdup2( pool, &url->passwd, "password"); - pj_strdup2( pool, &url->host, "localhost"); - url->port = 5060; - - return (pjsip_uri*) url; -} - -static pjsip_uri *create_uri4(pj_pool_t *pool) -{ - /* Like: "sip:localhost:5060", but without the port. */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri5(pj_pool_t *pool) -{ - /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - pj_strdup2(pool, &url->transport_param, "tcp"); - pj_strdup2(pool, &url->user_param, "ip"); - url->ttl_param = 255; - url->lr_param = 1; - pj_strdup2(pool, &url->maddr_param, "127.0.0.1"); - pj_strdup2(pool, &url->method_param, "ACK"); - - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri6(pj_pool_t *pool) -{ - /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" - "?Subject=Hello%20There&Server=SIP%20Server" - */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - pj_strdup2(pool, &url->user_param, "phone"); - pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry"); - pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server"); - return (pjsip_uri*)url; - -} - -static pjsip_uri *create_uri7(pj_pool_t *pool) -{ - /* "sips:localhost" */ - pjsip_url *url = pjsip_url_create(pool, 1); - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri8(pj_pool_t *pool) -{ - /* "" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri9(pj_pool_t *pool) -{ - /* " Power Administrator " */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 1); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "Power Administrator"); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri10(pj_pool_t *pool) -{ - /* " \"User\" " */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "\"User\""); - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "localhost"); - url->port = 5071; - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri11(pj_pool_t *pool) -{ - /* " \"Strange User\\\"\\\\\\\"\" " */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "\"Strange User\\\"\\\\\\\"\""); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri12(pj_pool_t *pool) -{ - /* " \"Rogue User\\\" " */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "\"Rogue User\\\""); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri13(pj_pool_t *pool) -{ - /* "Strange User\" " */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "Strange User\""); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri14(pj_pool_t *pool) -{ - /* "sip:localhost;pvalue=\"hello world\"" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->host, "localhost"); - pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\""); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri15(pj_pool_t *pool) -{ - /* "This is -. !% *_+`'~ me " */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me"); - pj_strdup2(pool, &url->user, "a19A&=+$,;?/%2c"); - pj_strdup2(pool, &url->passwd, "%09a&Zz=+$,"); - pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com"); - url->port = 9801; - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri16(pj_pool_t *pool) -{ - /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->host, ALPHANUM "-_.com"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri17(pj_pool_t *pool) -{ - /* "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->user, ALPHANUM USER); - pj_strdup2(pool, &url->passwd, ALPHANUM PASS); - pj_strdup2(pool, &url->host, "host"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri18(pj_pool_t *pool) -{ - /* "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";lr;other=1;transport=sctp;other2" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->host, "host"); - pj_strdup2(pool, &url->user_param, "ip"); - pj_strdup2(pool, &url->transport_param, "sctp"); - pj_strdup2(pool, &url->other_param, ";" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";other=1;other2"); - url->lr_param = 1; - return (pjsip_uri*)url; -} - -static pjsip_uri *create_dummy(pj_pool_t *pool) -{ - PJ_UNUSED_ARG(pool) - return NULL; -} - -/*****************************************************************************/ - -static void pool_error(pj_pool_t *pool, pj_size_t sz) -{ - PJ_UNUSED_ARG(pool) - PJ_UNUSED_ARG(sz) - - pj_assert(0); - exit(1); -} - -/* - * Test one test entry. - */ -static pj_status_t test_entry(struct uri_test *entry) -{ - pj_status_t status; - pj_pool_t *pool; - int len; - pjsip_uri *parsed_uri, *ref_uri; - pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0}; - pj_hr_timestamp t1, t2; - - pool = (*cp.factory.create_pool)( &cp.factory, "", POOL_SIZE, 0, &pool_error); - - /* Parse URI text. */ - pj_hr_gettimestamp(&t1); - parse_len += entry->len; - parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0); - if (!parsed_uri) { - /* Parsing failed. If the entry says that this is expected, then - * return OK. - */ - status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : ERR_SYNTAX_ERR; - goto on_return; - } - pj_hr_gettimestamp(&t2); - parse_time += t2.u32.lo - t1.u32.lo; - - /* Create the reference URI. */ - ref_uri = entry->creator(pool); - - /* Print both URI. */ - s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); - s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); - - pj_hr_gettimestamp(&t1); - len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE); - if (len < 1) { - status = -1; - goto on_return; - } - s1.slen = len; - - len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE); - if (len < 1) { - status = -1; - goto on_return; - } - s2.slen = len; - pj_hr_gettimestamp(&t2); - print_time += t2.u32.lo - t1.u32.lo; - - /* Full comparison of parsed URI with reference URI. */ - if (pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri) != 0) { - /* Not equal. See if this is the expected status. */ - status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : ERR_NOT_EQUAL; - goto on_return; - - } else { - /* Equal. See if this is the expected status. */ - status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -1; - if (status != PJ_SUCCESS) { - goto on_return; - } - } - - /* Compare text. */ - if (pj_strcmp(&s1, &s2) != 0) { - /* Not equal. */ - status = ERR_NOT_EQUAL; - } - -on_return: - if (!SILENT) { - printf("%.2d %s (expected status=%s)\n" - " str=%s\n" - " uri=%.*s\n" - " ref=%.*s\n\n", - entry-uri_test_array, - STATUS_STR(status), - STATUS_STR(entry->status), - entry->str, - (int)s1.slen, s1.ptr, (int)s2.slen, s2.ptr); - } - - pj_pool_release(pool); - return status; -} - -static void warm_up(pj_pool_factory *pf) -{ - pj_pool_t *pool; - struct uri_test *entry; - - pool = pj_pool_create(pf, "", POOL_SIZE, 0, &pool_error); - pjsip_parse_uri(pool, "sip:host", 8, 0); - entry = &uri_test_array[0]; - while (entry->creator) { - entry->len = strlen(entry->str); - ++entry; - } - pj_pool_release(pool); -} - -//#if !IS_PROFILING -#if 1 -pj_status_t test_uri() -{ - struct uri_test *entry; - int i=0, err=0; - pj_status_t status; - pj_hr_timestamp t1, t2; - pj_uint32_t total_time; - - pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); - warm_up(&cp.factory); - - pj_hr_gettimestamp(&t1); - for (i=0; icreator) { - status = test_entry(entry); - if (status != PJ_SUCCESS) { - ++err; - } - ++entry; - } - } - pj_hr_gettimestamp(&t2); - total_time = t2.u32.lo - t1.u32.lo; - - printf("Error=%d\n", err); - printf("Total parse len: %u bytes\n", parse_len); - printf("Total parse time: %u (%f/char), print time: %u (%f/char)\n", - parse_time, parse_time*1.0/parse_len, - print_time, print_time*1.0/parse_len); - printf("Total time: %u (%f/char)\n", total_time, total_time*1.0/parse_len); - return err; -} - -#else - -pj_status_t test_uri() -{ - struct uri_test *entry; - unsigned i; - - warm_up(); - pj_caching_pool_init(&cp, 1024*1024); - - for (i=0; icreator) { - pj_pool_t *pool; - pjsip_uri *uri1, *uri2; - - pool = pj_pool_create( &cp.factory, "", POOL_SIZE, 0, &pool_error); - uri1 = pjsip_parse_uri(pool, entry->str, strlen(entry->str)); - pj_pool_release(pool); - ++entry; - } - } - - return 0; -} - -#endif +/* $Header: /pjproject-0.3/pjsip/src/tests/pjsip_core/test_uri.c 9 10/14/05 12:23a Bennylp $ */ +#include +#include +#include +#include +#include +#include +#include +#include "test.h" + +#define ERR_SYNTAX_ERR (-2) +#define ERR_NOT_EQUAL (-3) + +#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789" +#define MARK "-_.!~*'()" +#define USER "&=+$,;?/%" +#define PASS "&=+$,%" +#define PARAM_CHAR "[]/:&+$" MARK "%" + +#define POOL_SIZE 4096 + +static const char *STATUS_STR(pj_status_t status) +{ + switch (status) { + case 0: return "OK"; + case ERR_SYNTAX_ERR: return "Syntax Error"; + case ERR_NOT_EQUAL: return "Not Equal"; + } + return "???"; +} + +static pj_uint32_t parse_len, parse_time, print_time; +static pj_caching_pool cp; + + +/* URI creator functions. */ +static pjsip_uri *create_uri1( pj_pool_t *pool ); +static pjsip_uri *create_uri2( pj_pool_t *pool ); +static pjsip_uri *create_uri3( pj_pool_t *pool ); +static pjsip_uri *create_uri4( pj_pool_t *pool ); +static pjsip_uri *create_uri5( pj_pool_t *pool ); +static pjsip_uri *create_uri6( pj_pool_t *pool ); +static pjsip_uri *create_uri7( pj_pool_t *pool ); +static pjsip_uri *create_uri8( pj_pool_t *pool ); +static pjsip_uri *create_uri9( pj_pool_t *pool ); +static pjsip_uri *create_uri10( pj_pool_t *pool ); +static pjsip_uri *create_uri11( pj_pool_t *pool ); +static pjsip_uri *create_uri12( pj_pool_t *pool ); +static pjsip_uri *create_uri13( pj_pool_t *pool ); +static pjsip_uri *create_uri14( pj_pool_t *pool ); +static pjsip_uri *create_uri15( pj_pool_t *pool ); +static pjsip_uri *create_uri16( pj_pool_t *pool ); +static pjsip_uri *create_uri17( pj_pool_t *pool ); +static pjsip_uri *create_uri18( pj_pool_t *pool ); +static pjsip_uri *create_uri19( pj_pool_t *pool ); +static pjsip_uri *create_dummy( pj_pool_t *pool ); + +struct uri_test +{ + pj_status_t status; + char str[PJSIP_MAX_URL_SIZE]; + pjsip_uri *(*creator)(pj_pool_t *pool); + pj_size_t len; +} uri_test_array[] = +{ + { + PJ_SUCCESS, + "sip:localhost", + &create_uri1 + }, + { + PJ_SUCCESS, + "sip:user@localhost", + &create_uri2 + }, + { + PJ_SUCCESS, + "sip:user:password@localhost:5060", + &create_uri3, + }, + { + /* Port is specified should not match unspecified port. */ + ERR_NOT_EQUAL, + "sip:localhost:5060", + &create_uri4 + }, + { + /* All recognized parameters. */ + PJ_SUCCESS, + "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK", + &create_uri5 + }, + { + /* Params mixed with other params and header params. */ + PJ_SUCCESS, + "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" + "?Subject=Hello%20There&Server=SIP%20Server", + &create_uri6 + }, + { + /* SIPS. */ + PJ_SUCCESS, + "sips:localhost", + &create_uri7, + }, + { + /* Name address */ + PJ_SUCCESS, + "", + &create_uri8 + }, + { + /* Name address with display name and SIPS scheme with some redundant + * whitespaced. + */ + PJ_SUCCESS, + " Power Administrator ", + &create_uri9 + }, + { + /* Name address. */ + PJ_SUCCESS, + " \"User\" ", + &create_uri10 + }, + { + /* Escaped sequence in display name (display=Strange User\"\\\"). */ + PJ_SUCCESS, + " \"Strange User\\\"\\\\\\\"\" ", + &create_uri11, + }, + { + /* Errorneous escaping in display name. */ + ERR_SYNTAX_ERR, + " \"Rogue User\\\" ", + &create_uri12, + }, + { + /* Dangling quote in display name, but that should be OK. */ + PJ_SUCCESS, + "Strange User\" ", + &create_uri13, + }, + { + /* Special characters in parameter value must be quoted. */ + PJ_SUCCESS, + "sip:localhost;pvalue=\"hello world\"", + &create_uri14, + }, + { + /* Excercise strange character sets allowed in display, user, password, + * host, and port. + */ + PJ_SUCCESS, + "This is -. !% *_+`'~ me ", + &create_uri15, + }, + { + /* Another excercise to the allowed character sets to the hostname. */ + PJ_SUCCESS, + "sip:" ALPHANUM "-_.com", + &create_uri16, + }, + { + /* Another excercise to the allowed character sets to the username + * and password. + */ + PJ_SUCCESS, + "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host", + &create_uri17, + }, + { + /* Excercise to the pname and pvalue, and mixup of other-param + * between 'recognized' params. + */ + PJ_SUCCESS, + "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR + ";lr;other=1;transport=sctp;other2", + &create_uri18, + }, + { + /* This should trigger syntax error. */ + ERR_SYNTAX_ERR, + "sip:", + &create_dummy, + }, + { + /* Syntax error: whitespace after scheme. */ + ERR_SYNTAX_ERR, + "sip :host", + &create_dummy, + }, + { + /* Syntax error: whitespace before hostname. */ + ERR_SYNTAX_ERR, + "sip: host", + &create_dummy, + }, + { + /* Syntax error: invalid port. */ + ERR_SYNTAX_ERR, + "sip:user:password", + &create_dummy, + }, + { + /* Syntax error: no host. */ + ERR_SYNTAX_ERR, + "sip:user@", + &create_dummy, + }, + { + /* Syntax error: no user/host. */ + ERR_SYNTAX_ERR, + "sip:@", + &create_dummy, + }, + { + /* Syntax error: empty string. */ + ERR_SYNTAX_ERR, + "", + &create_dummy, + }, + { + PJ_SUCCESS, + "", + NULL, + }, +}; + +static pjsip_uri *create_uri1(pj_pool_t *pool) +{ + /* "sip:localhost" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri2(pj_pool_t *pool) +{ + /* "sip:user@localhost" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2( pool, &url->user, "user"); + pj_strdup2( pool, &url->host, "localhost"); + + return (pjsip_uri*) url; +} + +static pjsip_uri *create_uri3(pj_pool_t *pool) +{ + /* "sip:user:password@localhost:5060" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2( pool, &url->user, "user"); + pj_strdup2( pool, &url->passwd, "password"); + pj_strdup2( pool, &url->host, "localhost"); + url->port = 5060; + + return (pjsip_uri*) url; +} + +static pjsip_uri *create_uri4(pj_pool_t *pool) +{ + /* Like: "sip:localhost:5060", but without the port. */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri5(pj_pool_t *pool) +{ + /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + pj_strdup2(pool, &url->transport_param, "tcp"); + pj_strdup2(pool, &url->user_param, "ip"); + url->ttl_param = 255; + url->lr_param = 1; + pj_strdup2(pool, &url->maddr_param, "127.0.0.1"); + pj_strdup2(pool, &url->method_param, "ACK"); + + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri6(pj_pool_t *pool) +{ + /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" + "?Subject=Hello%20There&Server=SIP%20Server" + */ + pjsip_url *url = pjsip_url_create(pool, 0); + + pj_strdup2(pool, &url->host, "localhost"); + pj_strdup2(pool, &url->user_param, "phone"); + pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry"); + pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server"); + return (pjsip_uri*)url; + +} + +static pjsip_uri *create_uri7(pj_pool_t *pool) +{ + /* "sips:localhost" */ + pjsip_url *url = pjsip_url_create(pool, 1); + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri8(pj_pool_t *pool) +{ + /* "" */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri9(pj_pool_t *pool) +{ + /* " Power Administrator " */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 1); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "Power Administrator"); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri10(pj_pool_t *pool) +{ + /* " \"User\" " */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "\"User\""); + pj_strdup2(pool, &url->user, "user"); + pj_strdup2(pool, &url->host, "localhost"); + url->port = 5071; + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri11(pj_pool_t *pool) +{ + /* " \"Strange User\\\"\\\\\\\"\" " */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "\"Strange User\\\"\\\\\\\"\""); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri12(pj_pool_t *pool) +{ + /* " \"Rogue User\\\" " */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "\"Rogue User\\\""); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri13(pj_pool_t *pool) +{ + /* "Strange User\" " */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "Strange User\""); + pj_strdup2(pool, &url->host, "localhost"); + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri14(pj_pool_t *pool) +{ + /* "sip:localhost;pvalue=\"hello world\"" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->host, "localhost"); + pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\""); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri15(pj_pool_t *pool) +{ + /* "This is -. !% *_+`'~ me " */ + pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); + pjsip_url *url; + + url = pjsip_url_create(pool, 0); + name_addr->uri = (pjsip_uri*) url; + + pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me"); + pj_strdup2(pool, &url->user, "a19A&=+$,;?/%2c"); + pj_strdup2(pool, &url->passwd, "%09a&Zz=+$,"); + pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com"); + url->port = 9801; + return (pjsip_uri*)name_addr; +} + +static pjsip_uri *create_uri16(pj_pool_t *pool) +{ + /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->host, ALPHANUM "-_.com"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri17(pj_pool_t *pool) +{ + /* "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->user, ALPHANUM USER); + pj_strdup2(pool, &url->passwd, ALPHANUM PASS); + pj_strdup2(pool, &url->host, "host"); + return (pjsip_uri*)url; +} + +static pjsip_uri *create_uri18(pj_pool_t *pool) +{ + /* "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";lr;other=1;transport=sctp;other2" */ + pjsip_url *url; + url = pjsip_url_create(pool, 0); + pj_strdup2(pool, &url->host, "host"); + pj_strdup2(pool, &url->user_param, "ip"); + pj_strdup2(pool, &url->transport_param, "sctp"); + pj_strdup2(pool, &url->other_param, ";" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";other=1;other2"); + url->lr_param = 1; + return (pjsip_uri*)url; +} + +static pjsip_uri *create_dummy(pj_pool_t *pool) +{ + PJ_UNUSED_ARG(pool) + return NULL; +} + +/*****************************************************************************/ + +static void pool_error(pj_pool_t *pool, pj_size_t sz) +{ + PJ_UNUSED_ARG(pool) + PJ_UNUSED_ARG(sz) + + pj_assert(0); + exit(1); +} + +/* + * Test one test entry. + */ +static pj_status_t test_entry(struct uri_test *entry) +{ + pj_status_t status; + pj_pool_t *pool; + int len; + pjsip_uri *parsed_uri, *ref_uri; + pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0}; + pj_hr_timestamp t1, t2; + + pool = (*cp.factory.create_pool)( &cp.factory, "", POOL_SIZE, 0, &pool_error); + + /* Parse URI text. */ + pj_hr_gettimestamp(&t1); + parse_len += entry->len; + parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0); + if (!parsed_uri) { + /* Parsing failed. If the entry says that this is expected, then + * return OK. + */ + status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : ERR_SYNTAX_ERR; + goto on_return; + } + pj_hr_gettimestamp(&t2); + parse_time += t2.u32.lo - t1.u32.lo; + + /* Create the reference URI. */ + ref_uri = entry->creator(pool); + + /* Print both URI. */ + s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); + s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); + + pj_hr_gettimestamp(&t1); + len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE); + if (len < 1) { + status = -1; + goto on_return; + } + s1.slen = len; + + len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE); + if (len < 1) { + status = -1; + goto on_return; + } + s2.slen = len; + pj_hr_gettimestamp(&t2); + print_time += t2.u32.lo - t1.u32.lo; + + /* Full comparison of parsed URI with reference URI. */ + if (pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri) != 0) { + /* Not equal. See if this is the expected status. */ + status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : ERR_NOT_EQUAL; + goto on_return; + + } else { + /* Equal. See if this is the expected status. */ + status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -1; + if (status != PJ_SUCCESS) { + goto on_return; + } + } + + /* Compare text. */ + if (pj_strcmp(&s1, &s2) != 0) { + /* Not equal. */ + status = ERR_NOT_EQUAL; + } + +on_return: + if (!SILENT) { + printf("%.2d %s (expected status=%s)\n" + " str=%s\n" + " uri=%.*s\n" + " ref=%.*s\n\n", + entry-uri_test_array, + STATUS_STR(status), + STATUS_STR(entry->status), + entry->str, + (int)s1.slen, s1.ptr, (int)s2.slen, s2.ptr); + } + + pj_pool_release(pool); + return status; +} + +static void warm_up(pj_pool_factory *pf) +{ + pj_pool_t *pool; + struct uri_test *entry; + + pool = pj_pool_create(pf, "", POOL_SIZE, 0, &pool_error); + pjsip_parse_uri(pool, "sip:host", 8, 0); + entry = &uri_test_array[0]; + while (entry->creator) { + entry->len = strlen(entry->str); + ++entry; + } + pj_pool_release(pool); +} + +//#if !IS_PROFILING +#if 1 +pj_status_t test_uri() +{ + struct uri_test *entry; + int i=0, err=0; + pj_status_t status; + pj_hr_timestamp t1, t2; + pj_uint32_t total_time; + + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + warm_up(&cp.factory); + + pj_hr_gettimestamp(&t1); + for (i=0; icreator) { + status = test_entry(entry); + if (status != PJ_SUCCESS) { + ++err; + } + ++entry; + } + } + pj_hr_gettimestamp(&t2); + total_time = t2.u32.lo - t1.u32.lo; + + printf("Error=%d\n", err); + printf("Total parse len: %u bytes\n", parse_len); + printf("Total parse time: %u (%f/char), print time: %u (%f/char)\n", + parse_time, parse_time*1.0/parse_len, + print_time, print_time*1.0/parse_len); + printf("Total time: %u (%f/char)\n", total_time, total_time*1.0/parse_len); + return err; +} + +#else + +pj_status_t test_uri() +{ + struct uri_test *entry; + unsigned i; + + warm_up(); + pj_caching_pool_init(&cp, 1024*1024); + + for (i=0; icreator) { + pj_pool_t *pool; + pjsip_uri *uri1, *uri2; + + pool = pj_pool_create( &cp.factory, "", POOL_SIZE, 0, &pool_error); + uri1 = pjsip_parse_uri(pool, entry->str, strlen(entry->str)); + pj_pool_release(pool); + ++entry; + } + } + + return 0; +} + +#endif diff --git a/user.mak b/user.mak index f7c213a96..07823a1fc 100644 --- a/user.mak +++ b/user.mak @@ -1,54 +1,54 @@ -ifeq ($(OS_NAME),palmos) - export PALMOS_CYGWIN := /cygdrive/c/PalmOSCygwin -# export PALMOS_SDK := /cygdrive/c/progra~1/PalmSource/PalmOS~1/sdk-5r4 -# export PALMOS_INCLUDES = \ -# $(CC_INC)$(PALMOS_SDK)/include \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Telephony \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Telephony/UI \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/SSL \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Sms \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Simulator \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Simulator/Locale \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Pdi \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/PalmOSGlue \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Lz77 \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/INet \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/exglocal \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/CPMLib \ -# $(CC_INC)$(PALMOS_SDK)/include/Libraries/AddressSort \ -# $(CC_INC)$(PALMOS_SDK)/include/Extensions \ -# $(CC_INC)$(PALMOS_SDK)/include/Extensions/ExpansionMgr \ -# $(CC_INC)$(PALMOS_SDK)/include/Extensions/Bluetooth \ -# $(CC_INC)$(PALMOS_SDK)/include/Dynamic \ -# $(CC_INC)$(PALMOS_SDK)/include/Core \ -# $(CC_INC)$(PALMOS_SDK)/include/Core/UI \ -# $(CC_INC)$(PALMOS_SDK)/include/Core/System \ -# $(CC_INC)$(PALMOS_SDK)/include/Core/System/Unix \ -# $(CC_INC)$(PALMOS_SDK)/include/Core/Hardware \ -# \ -# $(CC_INC)$(PALMOS_CYGWIN)/lib/gcc-lib/m68k-palmos/2.95.3-kgpd/include \ -# $(CC_INC)$(PALMOS_CYGWIN)usr/m68k-palmos/include \ -# $(CC_INC)$(PALMOS_CYGWIN)/usr/share/prc-tools/include - - export PALMOS_SDK := /cygdrive/c/progra~1/PalmSource/PalmOS~1/sdk-6.1 - export PALMOS_INCLUDES := \ - $(CC_INC)$(PALMOS_SDK)/headers \ - $(CC_INC)$(PALMOS_SDK)/headers/posix \ - \ - $(CC_INC)$(PALMOS_CYGWIN)/lib/gcc-lib/m68k-palmos/2.95.3-kgpd/include \ - $(CC_INC)$(PALMOS_CYGWIN)usr/m68k-palmos/include \ - $(CC_INC)$(PALMOS_CYGWIN)/usr/share/prc-tools/include - - export CFLAGS += -Wall \ - $(subst /,$(HOST_PSEP),$(PALMOS_INCLUDES)) - #-I/cygdrive/c/PalmOSCygwin/usr/include -endif - -ifeq ($(CC_NAME),vc) - export CC_LDFLAGS += /link /LIBPATH:C:\Progra~1\Micros~2\vc98\lib -endif - -ifeq ($(CC_NAME),gcc) - export CFLAGS += -endif +ifeq ($(OS_NAME),palmos) + export PALMOS_CYGWIN := /cygdrive/c/PalmOSCygwin +# export PALMOS_SDK := /cygdrive/c/progra~1/PalmSource/PalmOS~1/sdk-5r4 +# export PALMOS_INCLUDES = \ +# $(CC_INC)$(PALMOS_SDK)/include \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Telephony \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Telephony/UI \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/SSL \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Sms \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Simulator \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Simulator/Locale \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Pdi \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/PalmOSGlue \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/Lz77 \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/INet \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/exglocal \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/CPMLib \ +# $(CC_INC)$(PALMOS_SDK)/include/Libraries/AddressSort \ +# $(CC_INC)$(PALMOS_SDK)/include/Extensions \ +# $(CC_INC)$(PALMOS_SDK)/include/Extensions/ExpansionMgr \ +# $(CC_INC)$(PALMOS_SDK)/include/Extensions/Bluetooth \ +# $(CC_INC)$(PALMOS_SDK)/include/Dynamic \ +# $(CC_INC)$(PALMOS_SDK)/include/Core \ +# $(CC_INC)$(PALMOS_SDK)/include/Core/UI \ +# $(CC_INC)$(PALMOS_SDK)/include/Core/System \ +# $(CC_INC)$(PALMOS_SDK)/include/Core/System/Unix \ +# $(CC_INC)$(PALMOS_SDK)/include/Core/Hardware \ +# \ +# $(CC_INC)$(PALMOS_CYGWIN)/lib/gcc-lib/m68k-palmos/2.95.3-kgpd/include \ +# $(CC_INC)$(PALMOS_CYGWIN)usr/m68k-palmos/include \ +# $(CC_INC)$(PALMOS_CYGWIN)/usr/share/prc-tools/include + + export PALMOS_SDK := /cygdrive/c/progra~1/PalmSource/PalmOS~1/sdk-6.1 + export PALMOS_INCLUDES := \ + $(CC_INC)$(PALMOS_SDK)/headers \ + $(CC_INC)$(PALMOS_SDK)/headers/posix \ + \ + $(CC_INC)$(PALMOS_CYGWIN)/lib/gcc-lib/m68k-palmos/2.95.3-kgpd/include \ + $(CC_INC)$(PALMOS_CYGWIN)usr/m68k-palmos/include \ + $(CC_INC)$(PALMOS_CYGWIN)/usr/share/prc-tools/include + + export CFLAGS += -Wall \ + $(subst /,$(HOST_PSEP),$(PALMOS_INCLUDES)) + #-I/cygdrive/c/PalmOSCygwin/usr/include +endif + +ifeq ($(CC_NAME),vc) + export CC_LDFLAGS += /link /LIBPATH:C:\Progra~1\Micros~2\vc98\lib +endif + +ifeq ($(CC_NAME),gcc) + export CFLAGS += +endif