live/ 000755 001752 001752 00000000000 13242237477 011462 5 ustar 00rsf rsf 000000 000000 live/liveMedia/ 000755 001752 001752 00000000000 13242237370 013351 5 ustar 00rsf rsf 000000 000000 live/groupsock/ 000755 001752 001752 00000000000 13242237370 013466 5 ustar 00rsf rsf 000000 000000 live/UsageEnvironment/ 000755 001752 001752 00000000000 13242237370 014743 5 ustar 00rsf rsf 000000 000000 live/BasicUsageEnvironment/ 000755 001752 001752 00000000000 13242237370 015705 5 ustar 00rsf rsf 000000 000000 live/testProgs/ 000755 001752 001752 00000000000 13242237370 013444 5 ustar 00rsf rsf 000000 000000 live/mediaServer/ 000755 001752 001752 00000000000 13242237370 013720 5 ustar 00rsf rsf 000000 000000 live/proxyServer/ 000755 001752 001752 00000000000 13242237370 014022 5 ustar 00rsf rsf 000000 000000 live/WindowsAudioInputDevice/ 000755 001752 001752 00000000000 13242237370 016226 5 ustar 00rsf rsf 000000 000000 live/configure 000551 001752 001752 00000000571 13242237370 013356 0 ustar 00rsf rsf 000000 000000 #!/bin/sh
echo "Whoa! This software distribution does NOT use the normal Unix \"configure\" mechanism for generating a Makefile. For instructions on how to build this software, see ."
echo "Also, please make sure that you're using the most up-to-date version of the source code - available from ."
live/#config.macosx# 000444 001752 001752 00000000712 13242237476 014246 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -DTIME_BASE=in
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = libtool -s -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.aix 000444 001752 001752 00000000661 13242237476 013432 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -DBSD=1 -O -DTIME_BASE=int -DSOCKLEN_T=socklen_t
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DAIX=1
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.alpha 000444 001752 001752 00000000655 13242237476 013741 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DTIME_BASE=int
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS) -DALPHA
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1 -DALPHA
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -B static
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.armeb-uclibc 000444 001752 001752 00000001274 13242237476 015177 0 ustar 00rsf rsf 000000 000000 CROSS_COMPILE= armeb-linux-uclibc-
COMPILE_OPTS = $(INCLUDES) -I. -Os -DSOCKLEN_T=socklen_t -DNO_SSTREAM=1 -D
LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = $(CROSS_COMPILE)gcc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILE)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1
OBJ = o
LINK = $(CROSS_COMPILE)gcc -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CROSS_COMPILE)ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.armlinux 000444 001752 001752 00000001054 13242237476 014505 0 ustar 00rsf rsf 000000 000000 CROSS_COMPILE?= arm-elf-
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -DNO_SSTREAM=1 -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = $(CROSS_COMPILE)gcc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILE)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1
OBJ = o
LINK = $(CROSS_COMPILE)g++ -o
LINK_OPTS =
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CROSS_COMPILE)ar cr
LIBRARY_LINK_OPTS = $(LINK_OPTS)
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.avr32-linux 000444 001752 001752 00000001263 13242237476 014742 0 ustar 00rsf rsf 000000 000000 CROSS_COMPILE= avr32-linux-uclibc-
COMPILE_OPTS = -Os $(INCLUDES) -msoft-float -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -DSOCKLEN_T=socklen_t -DNO_SSTREAM=1 C = c
C_COMPILER = $(CROSS_COMPILE)gcc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILE)c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -fuse-cxa-atexit -DBSD=1 OBJ = o
LINK = $(CROSS_COMPILE)c++ -o
LINK_OPTS =
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CROSS_COMPILE)ar cr LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.bfin-linux-uclibc 000444 001752 001752 00000001215 13242237476 016157 0 ustar 00rsf rsf 000000 000000 CROSS_COMPILER = bfin-linux-uclibc-
COMPILE_OPTS = $(INCLUDES) -I. -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -DUCLINUX -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = $(CROSS_COMPILER)gcc
C_FLAGS = $(COMPILE_OPTS) -Wall
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILER)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = $(CROSS_COMPILER)g++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CROSS_COMPILER)ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.bfin-uclinux 000444 001752 001752 00000001213 13242237476 015246 0 ustar 00rsf rsf 000000 000000 CROSS_COMPILER= bfin-uclinux-
COMPILE_OPTS = $(INCLUDES) -I. -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -DUCLINUX -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = $(CROSS_COMPILER)gcc
C_FLAGS = $(COMPILE_OPTS) -Wall
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILER)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = $(CROSS_COMPILER)g++ -Wl,-elf2flt -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CROSS_COMPILER)ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.bsplinux 000444 001752 001752 00000001311 13242237476 014506 0 ustar 00rsf rsf 000000 000000 CROSS_COMPILE=
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -DNO_SSTREAM=1 -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = $(CROSS_COMPILE)ecc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILE)e++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1
OBJ = o
LINK = $(CROSS_COMPILE)e++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CROSS_COMPILE)eld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -Bstatic
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION = -lm
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.cris-axis-linux-gnu 000444 001752 001752 00000001603 13242237476 016474 0 ustar 00rsf rsf 000000 000000 # Note: AXIS_TOP_DIR is assumed to already be set in your environment.
# You can set this using the "init_env" script.
# See http://developer.axis.com/doc/software/apps/apps-howto.html
# for more information.
AXIS_DIR = $(AXIS_TOP_DIR)/target/cris-axis-linux-gnu
COMPILE_OPTS = $(INCLUDES) -I. -mlinux -isystem $(AXIS_DIR)/include -Wall -O2 -DSOCKLEN_T=socklen_t -DCRIS -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = gcc-cris
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++-cris
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wno-ctor-dtor-privacy -ansi -pipe
OBJ = o
LINK = c++-cris -static -o
AXIS_LINK_OPTS = -L$(AXIS_DIR)/lib
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS) -L$(AXIS_DIR)/lib -mlinux
LIBRARY_LINK = ld-cris -mcrislinux -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -Bstatic
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.cygwin 000444 001752 001752 00000000733 13242237476 014151 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DSOCKLEN_T=socklen_t -DNEWLOCALE_NOT_USED=1
C = c
C_COMPILER = gcc
C_FLAGS = $(COMPILE_OPTS) -DUSE_OUR_BZERO=1 -D__CYGWIN__
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -Bstatic
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.cygwin-for-vlc 000444 001752 001752 00000001005 13242237476 015510 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DSOCKLEN_T=socklen_t -DNEWLOCALE_NOT_USED=1
C = c
C_COMPILER = gcc
C_FLAGS = $(COMPILE_OPTS) -DUSE_OUR_BZERO=1 -D_WIN32 -mno-cygwin
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1 -D_WIN32 -Wno-deprecated -mno-cygwin
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -Bstatic
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.freebsd 000444 001752 001752 00000000670 13242237476 014263 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DBSD=1 -DNEWLOCALE_NOT_USED=1 -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.iphone-simulator 000444 001752 001752 00000002634 13242237476 016152 0 ustar 00rsf rsf 000000 000000 # **Note: You must install the relevant "Command line tools (OSX *.*) for Xcode - Xcode *.*"
# for this configuration file to work.
# Change the following version number, if necessary, before running "genMakefiles iphone-simulator"
IOS_VERSION = 8.3
MIN_IOS_VERSION = 7.0
DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer
TOOL_PATH = $(DEVELOPER_PATH)/usr/bin
SDK_PATH = $(DEVELOPER_PATH)/SDKs
SDK = $(SDK_PATH)/iPhoneSimulator$(IOS_VERSION).sdk
COMPILE_OPTS = $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O2 -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -miphoneos-version-min=$(MIN_IOS_VERSION) -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fPIC -arch i386 --sysroot=$(SDK) -isysroot $(SDK)
C = c
C_COMPILER = /usr/bin/xcrun clang
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = /usr/bin/xcrun clang
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = /usr/bin/xcrun clang -o
LINK_OPTS = -L. -arch i386 -miphoneos-version-min=$(MIN_IOS_VERSION) --sysroot=$(SDK) -isysroot -L$(SDK)/usr/lib/system -I$(SDK)/usr/lib /usr/lib/libc++.dylib
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = /usr/bin/xcrun libtool -static -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.iphoneos 000444 001752 001752 00000002370 13242237476 014474 0 ustar 00rsf rsf 000000 000000 # **Note: You must install the relevant "Command line tools (OSX *.*) for Xcode - Xcode *.*"
# for this configuration file to work.
#
# Change the following version number, if necessary, before running "genMakefiles iphoneos"
IOS_VERSION = 8.3
DEVELOPER_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer
TOOL_PATH = $(DEVELOPER_PATH)/usr/bin
SDK_PATH = $(DEVELOPER_PATH)/SDKs
SDK = $(SDK_PATH)/iPhoneOS$(IOS_VERSION).sdk
COMPILE_OPTS = $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O2 -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fPIC -arch armv7 --sysroot=$(SDK)
C = c
C_COMPILER = /usr/bin/xcrun clang
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = /usr/bin/xcrun clang
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = /usr/bin/xcrun clang -o
LINK_OPTS = -v -L. -arch armv7 --sysroot=$(SDK) -L$(SDK)/usr/lib/system /usr/lib/libc++.dylib
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = /usr/bin/xcrun libtool -static -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.irix 000444 001752 001752 00000000623 13242237476 013622 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS) -DIRIX
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DIRIX
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -B static
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux 000444 001752 001752 00000000762 13242237476 014012 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1 $(CPPFLAGS) $(CXXFLAGS)
OBJ = o
LINK = c++ -o
LINK_OPTS = -L. $(LDFLAGS)
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-64bit 000444 001752 001752 00000000705 13242237476 014735 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -m64 -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-gdb 000444 001752 001752 00000000673 13242237476 014545 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DSOCKLEN_T=socklen_t -g -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-with-shared-libraries 000444 001752 001752 00000004457 13242237476 020206 0 ustar 00rsf rsf 000000 000000 # 'CURRENT':'REVISION':'AGE' are updated - whenever a library changes - as follows:
# The library code changes, but without any changes to the API (i.e., interfaces) => increment REVISION
# At least one interface changes, or is removed => CURRENT += 1; REVISION = 0; AGE = 0
# One or more interfaces were added, but no existing interfaces were changed or removed => CURRENT += 1; REVISION = 0; AGE += 1
libliveMedia_VERSION_CURRENT=62
libliveMedia_VERSION_REVISION=2
libliveMedia_VERSION_AGE=0
libliveMedia_LIB_SUFFIX=so.$(shell expr $(libliveMedia_VERSION_CURRENT) - $(libliveMedia_VERSION_AGE)).$(libliveMedia_VERSION_AGE).$(libliveMedia_VERSION_REVISION)
libBasicUsageEnvironment_VERSION_CURRENT=1
libBasicUsageEnvironment_VERSION_REVISION=1
libBasicUsageEnvironment_VERSION_AGE=0
libBasicUsageEnvironment_LIB_SUFFIX=so.$(shell expr $(libBasicUsageEnvironment_VERSION_CURRENT) - $(libBasicUsageEnvironment_VERSION_AGE)).$(libBasicUsageEnvironment_VERSION_AGE).$(libBasicUsageEnvironment_VERSION_REVISION)
libUsageEnvironment_VERSION_CURRENT=4
libUsageEnvironment_VERSION_REVISION=0
libUsageEnvironment_VERSION_AGE=1
libUsageEnvironment_LIB_SUFFIX=so.$(shell expr $(libUsageEnvironment_VERSION_CURRENT) - $(libUsageEnvironment_VERSION_AGE)).$(libUsageEnvironment_VERSION_AGE).$(libUsageEnvironment_VERSION_REVISION)
libgroupsock_VERSION_CURRENT=9
libgroupsock_VERSION_REVISION=1
libgroupsock_VERSION_AGE=1
libgroupsock_LIB_SUFFIX=so.$(shell expr $(libgroupsock_VERSION_CURRENT) - $(libgroupsock_VERSION_AGE)).$(libgroupsock_VERSION_AGE).$(libgroupsock_VERSION_REVISION)
#####
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -fPIC
C = c
C_COMPILER = $(CC)
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
CPP = cpp
CPLUSPLUS_COMPILER = $(CXX)
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1 $(CPPFLAGS) $(CXXFLAGS)
OBJ = o
LINK = $(CXX) -o
LINK_OPTS = -L. $(LDFLAGS)
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CC) -o
SHORT_LIB_SUFFIX = so.$(shell expr $($(NAME)_VERSION_CURRENT) - $($(NAME)_VERSION_AGE))
LIB_SUFFIX = $(SHORT_LIB_SUFFIX).$($(NAME)_VERSION_AGE).$($(NAME)_VERSION_REVISION)
LIBRARY_LINK_OPTS = -shared -Wl,-soname,$(NAME).$(SHORT_LIB_SUFFIX) $(LDFLAGS)
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
INSTALL2 = install_shared_libraries
live/config.macosx 000444 001752 001752 00000000736 13242237476 014146 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -DTIME_BASE=int -DNEED_XLOCALE_H=1
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = libtool -s -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.macosx-32bit 000444 001752 001752 00000000725 13242237476 015065 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = -m32 $(INCLUDES) -I. $(EXTRA_LDFLAGS) -DBSD=1 -O -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -DTIME_BASE=int
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L. -m32
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = libtool -s -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.macosx-before-version-10.4 000444 001752 001752 00000000646 13242237476 017451 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -DBSD=1 -O -DSOCKLEN_T=int -DTIME_BASE=int
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.mingw 000444 001752 001752 00000001213 13242237476 013764 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DSOCKLEN_T=int -DLOCALE_NOT_USED
C = c
C_COMPILER = $(CC)
C_FLAGS = $(COMPILE_OPTS) -DUSE_OUR_BZERO=1 -D__MINGW32__
CPP = cpp
CPLUSPLUS_COMPILER = $(CXX)
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -D__MINGW32__ -Wall -Wno-deprecated
OBJ = o
LINK = $(CXX) -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(LD) -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -Bstatic
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION = -lws2_32
LIBS_FOR_GUI_APPLICATION = -lws2_32
EXE =
live/config.openbsd 000444 001752 001752 00000000661 13242237476 014303 0 ustar 00rsf rsf 000000 000000 .SUFFIXES: .cpp
COMPILE_OPTS = $(INCLUDES) -I. -DBSD=1 -O -DSOCKLEN_T=socklen_t
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DAIX=1
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.qnx4 000444 001752 001752 00000001070 13242237476 013536 0 ustar 00rsf rsf 000000 000000 #
# Requires:
# QNX 4.25
# Watcom 10.6
# TCP/IP 5.0
#
COMPILE_OPTS = $(INCLUDES) -I. -D_QNX4 -DBSD -DSOCKLEN_T=uint32_t -I/usr/watcom/10.6/usr/include
C = c
C_COMPILER = cc32
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = cc32
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -WC,-xs
OBJ = o
LINK = cc32 -b -M -N30000 -o
LINK_OPTS = -l.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = wlib -n -b -c
LIBRARY_LINK_OPTS = $(LINK_OPTS)
LIB_SUFFIX = lib
LIBS_FOR_CONSOLE_APPLICATION = -lsocket
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/config.solaris-32bit 000444 001752 001752 00000000743 13242237476 015247 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DSOLARIS -DNEWLOCALE_NOT_USED -DSOCKLEN_T=socklen_t
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -dn
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION = -lsocket -lnsl
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/config.solaris-64bit 000444 001752 001752 00000001207 13242237476 015250 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -m64 -I. -O -DSOLARIS -DNEWLOCALE_NOT_USED -DSOCKLEN_T=socklen_t
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -m64 -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -64 -r -dn
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION = -lsocket -lnsl
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/config.sunos 000444 001752 001752 00000000613 13242237476 014015 0 ustar 00rsf rsf 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -DBSD=1 -O
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cc
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L.
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ld -o
LIBRARY_LINK_OPTS = $(LINK_OPTS) -r -Bstatic
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.uClinux 000444 001752 001752 00000001357 13242237476 014303 0 ustar 00rsf rsf 000000 000000 CROSS_COMPILE= arc-linux-uclibc-
COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
C = c
C_COMPILER = $(CROSS_COMPILE)gcc
CFLAGS += $(COMPILE_OPTS)
C_FLAGS = $(CFLAGS)
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILE)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -Wall -DBSD=1
CPLUSPLUS_FLAGS += $(CPPFLAGS) -fexceptions
OBJ = o
LINK = $(CROSS_COMPILE)g++ -o
LINK_OPTS = -L. $(LDFLAGS)
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = $(CROSS_COMPILE)ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION = $(CXXLIBS)
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/genMakefiles 000551 001752 001752 00000000767 13242237476 014005 0 ustar 00rsf rsf 000000 000000 #!/bin/sh
usage() {
echo "Usage: $0 "
exit 1
}
if [ $# -ne 1 ]
then
usage $*
fi
platform=$1
subdirs="liveMedia groupsock UsageEnvironment BasicUsageEnvironment testProgs mediaServer proxyServer"
for subdir in $subdirs
do
/bin/rm -f $subdir/Makefile
cat $subdir/Makefile.head config.$platform $subdir/Makefile.tail > $subdir/Makefile
chmod a-w $subdir/Makefile
done
/bin/rm -f Makefile
cat Makefile.head config.$1 Makefile.tail > Makefile
chmod a-w Makefile
live/COPYING 000444 001752 001752 00000104513 13242237476 012516 0 ustar 00rsf rsf 000000 000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
live/COPYING.LESSER 000444 001752 001752 00000016743 13242237476 013521 0 ustar 00rsf rsf 000000 000000 GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
live/README 000444 001752 001752 00000000147 13242237476 012341 0 ustar 00rsf rsf 000000 000000 For documentation and instructions for building this software,
see
live/Makefile.head 000444 001752 001752 00000000061 13242237476 014014 0 ustar 00rsf rsf 000000 000000 ##### Change the following for your environment:
live/Makefile.tail 000444 001752 001752 00000003020 13242237476 014042 0 ustar 00rsf rsf 000000 000000 ##### End of variables to change
LIVEMEDIA_DIR = liveMedia
GROUPSOCK_DIR = groupsock
USAGE_ENVIRONMENT_DIR = UsageEnvironment
BASIC_USAGE_ENVIRONMENT_DIR = BasicUsageEnvironment
TESTPROGS_DIR = testProgs
MEDIA_SERVER_DIR = mediaServer
PROXY_SERVER_DIR = proxyServer
all:
cd $(LIVEMEDIA_DIR) ; $(MAKE)
cd $(GROUPSOCK_DIR) ; $(MAKE)
cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE)
cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE)
cd $(TESTPROGS_DIR) ; $(MAKE)
cd $(MEDIA_SERVER_DIR) ; $(MAKE)
cd $(PROXY_SERVER_DIR) ; $(MAKE)
@echo
@echo "For more information about this source code (including your obligations under the LGPL), please see our FAQ at http://live555.com/liveMedia/faq.html"
install:
cd $(LIVEMEDIA_DIR) ; $(MAKE) install
cd $(GROUPSOCK_DIR) ; $(MAKE) install
cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE) install
cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE) install
cd $(TESTPROGS_DIR) ; $(MAKE) install
cd $(MEDIA_SERVER_DIR) ; $(MAKE) install
cd $(PROXY_SERVER_DIR) ; $(MAKE) install
clean:
cd $(LIVEMEDIA_DIR) ; $(MAKE) clean
cd $(GROUPSOCK_DIR) ; $(MAKE) clean
cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE) clean
cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE) clean
cd $(TESTPROGS_DIR) ; $(MAKE) clean
cd $(MEDIA_SERVER_DIR) ; $(MAKE) clean
cd $(PROXY_SERVER_DIR) ; $(MAKE) clean
distclean: clean
-rm -f $(LIVEMEDIA_DIR)/Makefile $(GROUPSOCK_DIR)/Makefile \
$(USAGE_ENVIRONMENT_DIR)/Makefile $(BASIC_USAGE_ENVIRONMENT_DIR)/Makefile \
$(TESTPROGS_DIR)/Makefile $(MEDIA_SERVER_DIR)/Makefile \
$(PROXY_SERVER_DIR)/Makefile Makefile
live/fix-makefile 000555 001752 001752 00000001043 13242237476 013744 0 ustar 00rsf rsf 000000 000000 #!/bin/sh
# the next line restarts using tclsh \
exec tclsh8.4 "$0" "$@"
set makefileName [lindex $argv 0]
set tmpfileName /tmp/rsftmp
set inFid [open $makefileName r]
set outFid [open $tmpfileName w]
while {![eof $inFid]} {
set line [gets $inFid]
if {[string match *\)\$* $line]} {
set pos [string first \)\$ $line]
set prefix [string range $line 0 $pos]
incr pos
set suffix [string range $line $pos end]
set line $prefix\ $suffix
}
puts $outFid $line
}
close $inFid
close $outFid
file rename -force $tmpfileName $makefileName
live/win32config 000444 001752 001752 00000002650 13242237476 013535 0 ustar 00rsf rsf 000000 000000 # Comment out the following line to produce Makefiles that generate debuggable code:
NODEBUG=1
# The following definition ensures that we are properly matching
# the WinSock2 library file with the correct header files.
# (will link with "ws2_32.lib" and include "winsock2.h" & "Ws2tcpip.h")
TARGETOS = WINNT
# If for some reason you wish to use WinSock1 instead, uncomment the
# following two definitions.
# (will link with "wsock32.lib" and include "winsock.h")
#TARGETOS = WIN95
#APPVER = 4.0
!include
UI_OPTS = $(guilflags) $(guilibsdll)
# Use the following to get a console (e.g., for debugging):
CONSOLE_UI_OPTS = $(conlflags) $(conlibsdll)
CPU=i386
TOOLS32 = c:\Program Files\DevStudio\Vc
COMPILE_OPTS = $(INCLUDES) $(cdebug) $(cflags) $(cvarsdll) -I. -I"$(TOOLS32)\include"
C = c
C_COMPILER = "$(TOOLS32)\bin\cl"
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = $(C_COMPILER)
CPLUSPLUS_FLAGS = $(COMPILE_OPTS)
OBJ = obj
LINK = $(link) -out:
LIBRARY_LINK = lib -out:
LINK_OPTS_0 = $(linkdebug) msvcirt.lib
LIBRARY_LINK_OPTS =
LINK_OPTS = $(LINK_OPTS_0) $(UI_OPTS)
CONSOLE_LINK_OPTS = $(LINK_OPTS_0) $(CONSOLE_UI_OPTS)
SERVICE_LINK_OPTS = kernel32.lib advapi32.lib shell32.lib -subsystem:console,$(APPVER)
LIB_SUFFIX = lib
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
MULTIMEDIA_LIBS = winmm.lib
EXE = .exe
PLATFORM = Windows
rc32 = "$(TOOLS32)\bin\rc"
.rc.res:
$(rc32) $<
live/win32config.Borland 000444 001752 001752 00000002616 13242237476 015117 0 ustar 00rsf rsf 000000 000000 # Comment out the following line to produce Makefiles that generate debuggable code:
NODEBUG=1
# The following definition ensures that we are properly matching
# the WinSock2 library file with the correct header files.
# (will link with "ws2_32.lib" and include "winsock2.h" & "Ws2tcpip.h")
TARGETOS = WINNT
# If for some reason you wish to use WinSock1 instead, uncomment the
# following two definitions.
# (will link with "wsock32.lib" and include "winsock.h")
#TARGETOS = WIN95
#APPVER = 4.0
#!include
UI_OPTS = $(guilflags) $(guilibsdll)
# Use the following to get a console (e.g., for debugging):
CONSOLE_UI_OPTS = $(conlflags) $(conlibsdll)
CPU=i386
TOOLS32 = C:\Progra~1\Borland\CBuilder5
COMPILE_OPTS = $(INCLUDES) $(cdebug) $(cflags) $(cvarsdll) -I. -I$(TOOLS32)\include
C = c
C_COMPILER = $(TOOLS32)\bin\bcc32
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = $(C_COMPILER)
CPLUSPLUS_FLAGS = $(COMPILE_OPTS)
OBJ = obj
LINK = $(TOOLS32)\bin\ilink32
LIBRARY_LINK = $(TOOLS32)\bin\tlib
LINK_OPTS_0 = $(linkdebug) msvcirt.lib
LIBRARY_LINK_OPTS = /u
LINK_OPTS = $(LINK_OPTS_0) $(UI_OPTS)
CONSOLE_LINK_OPTS = c0x32
SERVICE_LINK_OPTS = kernel32.lib advapi32.lib shell32.lib -subsystem:console,$(APPVER)
LIB_SUFFIX = lib
LIBS_FOR_CONSOLE_APPLICATION = cw32.lib import32.lib
LIBS_FOR_GUI_APPLICATION = ,,cw32
EXE =
rc32 = $(TOOLS32)\bin\brc32"
.rc.res:
$(rc32) $<
live/genWindowsMakefiles 000555 001752 001752 00000001667 13242237477 015365 0 ustar 00rsf rsf 000000 000000 #!/bin/sh
cd liveMedia
/bin/rm -f liveMedia.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > liveMedia.mak
cd ../groupsock
/bin/rm -f groupsock.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > groupsock.mak
cd ../UsageEnvironment
/bin/rm -f UsageEnvironment.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > UsageEnvironment.mak
cd ../BasicUsageEnvironment
/bin/rm -f BasicUsageEnvironment.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > BasicUsageEnvironment.mak
cd ../testProgs
/bin/rm -f testProgs.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > testProgs.mak
cd ../mediaServer
/bin/rm -f mediaServer.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > mediaServer.mak
cd ../proxyServer
/bin/rm -f proxyServer.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > proxyServer.mak
live/genWindowsMakefiles.cmd 000444 001752 001752 00000001531 13242237477 016112 0 ustar 00rsf rsf 000000 000000 @Echo OFF
SETLOCAL
for %%I in (%0) do %%~dI
for %%I in (%0) do cd "%%~pI"
cd liveMedia
del /Q liveMedia.mak
type Makefile.head ..\win32config Makefile.tail > liveMedia.mak
cd ../groupsock
del /Q groupsock.mak
type Makefile.head ..\win32config Makefile.tail > groupsock.mak
cd ../UsageEnvironment
del /Q UsageEnvironment.mak
type Makefile.head ..\win32config Makefile.tail > UsageEnvironment.mak
cd ../BasicUsageEnvironment
del /Q BasicUsageEnvironment.mak
type Makefile.head ..\win32config Makefile.tail > BasicUsageEnvironment.mak
cd ../testProgs
del /Q testProgs.mak
type Makefile.head ..\win32config Makefile.tail > testProgs.mak
cd ../mediaServer
del /Q mediaServer.mak
type Makefile.head ..\win32config Makefile.tail > mediaServer.mak
cd ../proxyServer
del /Q proxyServer.mak
type Makefile.head ..\win32config Makefile.tail > proxyServer.mak
ENDLOCAL
live/WindowsAudioInputDevice/showAudioInputPorts.cpp 000444 001752 001752 00000002505 13242237370 022744 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A program that prints out this computer's audio input ports
#include "AudioInputDevice.hh"
#include
int main(int argc, char** argv) {
AudioPortNames* portNames = AudioInputDevice::getPortNames();
if (portNames == NULL) {
fprintf(stderr, "AudioInputDevice::getPortNames() failed!\n");
exit(1);
}
printf("%d available audio input ports:\n", portNames->numPorts);
for (unsigned i = 0; i < portNames->numPorts; ++i) {
printf("%d\t%s\n", i, portNames->portName[i]);
}
return 0;
}
live/WindowsAudioInputDevice/WindowsAudioInputDevice.mak 000444 001752 001752 00000011000 13242237370 023462 0 ustar 00rsf rsf 000000 000000 INCLUDES = -I../UsageEnvironment/include -I../groupsock/include -I../liveMedia/include
##### Change the following for your environment:
# Comment out the following line to produce Makefiles that generate debuggable code:
NODEBUG=1
# The following definition ensures that we are properly matching
# the WinSock2 library file with the correct header files.
# (will link with "ws2_32.lib" and include "winsock2.h" & "Ws2tcpip.h")
TARGETOS = WINNT
# If for some reason you wish to use WinSock1 instead, uncomment the
# following two definitions.
# (will link with "wsock32.lib" and include "winsock.h")
#TARGETOS = WIN95
#APPVER = 4.0
!include
UI_OPTS = $(guilflags) $(guilibsdll)
# Use the following to get a console (e.g., for debugging):
CONSOLE_UI_OPTS = $(conlflags) $(conlibsdll)
CPU=i386
TOOLS32 = c:\Program Files\DevStudio\Vc
COMPILE_OPTS = $(INCLUDES) $(cdebug) $(cflags) $(cvarsdll) -I. -I"$(TOOLS32)\include"
C = c
C_COMPILER = "$(TOOLS32)\bin\cl"
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = $(C_COMPILER)
CPLUSPLUS_FLAGS = $(COMPILE_OPTS)
OBJ = obj
LINK = $(link) -out:
LIBRARY_LINK = lib -out:
LINK_OPTS_0 = $(linkdebug) msvcirt.lib
LIBRARY_LINK_OPTS =
LINK_OPTS = $(LINK_OPTS_0) $(UI_OPTS)
CONSOLE_LINK_OPTS = $(LINK_OPTS_0) $(CONSOLE_UI_OPTS)
SERVICE_LINK_OPTS = kernel32.lib advapi32.lib shell32.lib -subsystem:console,$(APPVER)
LIB_SUFFIX = lib
LIBS_FOR_CONSOLE_APPLICATION =
LIBS_FOR_GUI_APPLICATION =
MULTIMEDIA_LIBS = winmm.lib
EXE = .exe
rc32 = "$(TOOLS32)\bin\rc"
.rc.res:
$(rc32) $<
##### End of variables to change
WINDOWSAUDIOINPUTDEVICE_NOMIXER_LIB = libWindowsAudioInputDevice_noMixer.$(LIB_SUFFIX)
WINDOWSAUDIOINPUTDEVICE_MIXER_LIB = libWindowsAudioInputDevice_mixer.$(LIB_SUFFIX)
ALL = $(WINDOWSAUDIOINPUTDEVICE_NOMIXER_LIB) $(WINDOWSAUDIOINPUTDEVICE_MIXER_LIB) \
showAudioInputPorts_noMixer$(EXE) showAudioInputPorts_mixer$(EXE)
all:: $(ALL)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
WINDOWSAUDIOINPUTDEVICE_NOMIXER_LIB_OBJS = WindowsAudioInputDevice_common.$(OBJ) WindowsAudioInputDevice_noMixer.$(OBJ)
WINDOWSAUDIOINPUTDEVICE_MIXER_LIB_OBJS = WindowsAudioInputDevice_common.$(OBJ) WindowsAudioInputDevice_mixer.$(OBJ)
WindowsAudioInputDevice_common.$(CPP): WindowsAudioInputDevice_common.hh
WindowsAudioInputDevice_noMixer.$(CPP): WindowsAudioInputDevice_noMixer.hh
WindowsAudioInputDevice_noMixer.hh: WindowsAudioInputDevice_common.hh
WindowsAudioInputDevice_mixer.$(CPP): WindowsAudioInputDevice_mixer.hh
WindowsAudioInputDevice_mixer.hh: WindowsAudioInputDevice_common.hh
$(WINDOWSAUDIOINPUTDEVICE_NOMIXER_LIB): $(WINDOWSAUDIOINPUTDEVICE_NOMIXER_LIB_OBJS) \
$(PLATFORM_SPECIFIC_LIB_OBJS)
$(LIBRARY_LINK)$@ $(LIBRARY_LINK_OPTS) \
$(WINDOWSAUDIOINPUTDEVICE_NOMIXER_LIB_OBJS)
$(WINDOWSAUDIOINPUTDEVICE_MIXER_LIB): $(WINDOWSAUDIOINPUTDEVICE_MIXER_LIB_OBJS) \
$(PLATFORM_SPECIFIC_LIB_OBJS)
$(LIBRARY_LINK)$@ $(LIBRARY_LINK_OPTS) \
$(WINDOWSAUDIOINPUTDEVICE_MIXER_LIB_OBJS)
USAGE_ENVIRONMENT_DIR = ../UsageEnvironment
USAGE_ENVIRONMENT_LIB = $(USAGE_ENVIRONMENT_DIR)/libUsageEnvironment.$(LIB_SUFFIX)
BASIC_USAGE_ENVIRONMENT_DIR = ../BasicUsageEnvironment
BASIC_USAGE_ENVIRONMENT_LIB = $(BASIC_USAGE_ENVIRONMENT_DIR)/libBasicUsageEnvironment.$(LIB_SUFFIX)
LIVEMEDIA_DIR = ../liveMedia
LIVEMEDIA_LIB = $(LIVEMEDIA_DIR)/libliveMedia.$(LIB_SUFFIX)
GROUPSOCK_DIR = ../groupsock
GROUPSOCK_LIB = $(GROUPSOCK_DIR)/libgroupsock.$(LIB_SUFFIX)
LOCAL_LIBS = $(LIVEMEDIA_LIB) $(GROUPSOCK_LIB) \
$(USAGE_ENVIRONMENT_LIB) $(BASIC_USAGE_ENVIRONMENT_LIB)
LOCAL_LIBS_NOMIXER = $(WINDOWSAUDIOINPUTDEVICE_NOMIXER_LIB) $(LOCAL_LIBS)
LOCAL_LIBS_MIXER = $(WINDOWSAUDIOINPUTDEVICE_MIXER_LIB) $(LOCAL_LIBS)
MULTIMEDIA_LIBS = winmm.lib
LIBS_NOMIXER = $(LOCAL_LIBS_NOMIXER) $(LIBS_FOR_CONSOLE_APPLICATION) $(MULTIMEDIA_LIBS)
LIBS_MIXER = $(LOCAL_LIBS_MIXER) $(LIBS_FOR_CONSOLE_APPLICATION) $(MULTIMEDIA_LIBS)
SHOW_AUDIO_INPUT_PORTS_OBJS = showAudioInputPorts.$(OBJ)
showAudioInputPorts_noMixer$(EXE): $(SHOW_AUDIO_INPUT_PORTS_OBJS) $(LOCAL_LIBS_NOMIXER)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(SHOW_AUDIO_INPUT_PORTS_OBJS) $(LIBS_NOMIXER)
showAudioInputPorts_mixer$(EXE): $(SHOW_AUDIO_INPUT_PORTS_OBJS) $(LOCAL_LIBS_MIXER)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(SHOW_AUDIO_INPUT_PORTS_OBJS) $(LIBS_MIXER)
clean:
-rm -rf *.$(OBJ) $(ALL) tcl2array$(EXE) core *.core *~
-rm -rf $(TCL_EMBEDDED_CPLUSPLUS_FILES) $(TK_EMBEDDED_CPLUSPLUS_FILES) $(MISC_EMBEDDED_CPLUSPLUS_FILES)
##### Any additional, platform-specific rules come here:
live/WindowsAudioInputDevice/WindowsAudioInputDevice_common.cpp 000444 001752 001752 00000025254 13242237370 025064 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 2001-2004 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// Base class for both library versions:
// One that uses Windows' built-in software mixer; another that doesn't.
// Implementation
#include "WindowsAudioInputDevice_common.hh"
#include
////////// WindowsAudioInputDevice_common implementation //////////
unsigned WindowsAudioInputDevice_common::_bitsPerSample = 16;
WindowsAudioInputDevice_common
::WindowsAudioInputDevice_common(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample,
unsigned char numChannels,
unsigned samplingFrequency,
unsigned granularityInMS)
: AudioInputDevice(env, bitsPerSample, numChannels, samplingFrequency, granularityInMS),
fCurPortIndex(-1), fHaveStarted(False) {
_bitsPerSample = bitsPerSample;
}
WindowsAudioInputDevice_common::~WindowsAudioInputDevice_common() {
}
Boolean WindowsAudioInputDevice_common::initialSetInputPort(int portIndex) {
if (!setInputPort(portIndex)) {
char errMsgPrefix[100];
sprintf(errMsgPrefix, "Failed to set audio input port number to %d: ", portIndex);
char* errMsgSuffix = strDup(envir().getResultMsg());
envir().setResultMsg(errMsgPrefix, errMsgSuffix);
delete[] errMsgSuffix;
return False;
} else {
return True;
}
}
void WindowsAudioInputDevice_common::doGetNextFrame() {
if (!fHaveStarted) {
// Before reading the first audio data, flush any existing data:
while (readHead != NULL) releaseHeadBuffer();
fHaveStarted = True;
}
fTotalPollingDelay = 0;
audioReadyPoller1();
}
void WindowsAudioInputDevice_common::doStopGettingFrames() {
// Turn off the audio poller:
envir().taskScheduler().unscheduleDelayedTask(nextTask()); nextTask() = NULL;
}
double WindowsAudioInputDevice_common::getAverageLevel() const {
// If the input audio queue is empty, return the previous level,
// otherwise use the input queue to recompute "averageLevel":
if (readHead != NULL) {
double levelTotal = 0.0;
unsigned totNumSamples = 0;
WAVEHDR* curHdr = readHead;
while (1) {
short* samplePtr = (short*)(curHdr->lpData);
unsigned numSamples = blockSize/2;
totNumSamples += numSamples;
while (numSamples-- > 0) {
short sample = *samplePtr++;
if (sample < 0) sample = -sample;
levelTotal += (unsigned short)sample;
}
if (curHdr == readTail) break;
curHdr = curHdr->lpNext;
}
averageLevel = levelTotal/(totNumSamples*(double)0x8000);
}
return averageLevel;
}
void WindowsAudioInputDevice_common::audioReadyPoller(void* clientData) {
WindowsAudioInputDevice_common* inputDevice = (WindowsAudioInputDevice_common*)clientData;
inputDevice->audioReadyPoller1();
}
void WindowsAudioInputDevice_common::audioReadyPoller1() {
if (readHead != NULL) {
onceAudioIsReady();
} else {
unsigned const maxPollingDelay = (100 + fGranularityInMS)*1000;
if (fTotalPollingDelay > maxPollingDelay) {
// We've waited too long for the audio device - assume it's down:
handleClosure(this);
return;
}
// Try again after a short delay:
unsigned const uSecondsToDelay = fGranularityInMS*1000;
fTotalPollingDelay += uSecondsToDelay;
nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToDelay,
(TaskFunc*)audioReadyPoller, this);
}
}
void WindowsAudioInputDevice_common::onceAudioIsReady() {
fFrameSize = readFromBuffers(fTo, fMaxSize, fPresentationTime);
if (fFrameSize == 0) {
// The source is no longer readable
handleClosure(this);
return;
}
fDurationInMicroseconds = 1000000/fSamplingFrequency;
// Call our own 'after getting' function. Because we sometimes get here
// after returning from a delay, we can call this directly, without risking
// infinite recursion
afterGetting(this);
}
static void CALLBACK waveInCallback(HWAVEIN /*hwi*/, UINT uMsg,
DWORD /*dwInstance*/, DWORD dwParam1, DWORD /*dwParam2*/) {
switch (uMsg) {
case WIM_DATA:
WAVEHDR* hdr = (WAVEHDR*)dwParam1;
WindowsAudioInputDevice_common::waveInProc(hdr);
break;
}
}
Boolean WindowsAudioInputDevice_common::openWavInPort(int index, unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS) {
uSecsPerByte = (8*1e6)/(_bitsPerSample*numChannels*samplingFrequency);
// Configure the port, based on the specified parameters:
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = numChannels;
wfx.nSamplesPerSec = samplingFrequency;
wfx.wBitsPerSample = _bitsPerSample;
wfx.nBlockAlign = (numChannels*_bitsPerSample)/8;
wfx.nAvgBytesPerSec = samplingFrequency*wfx.nBlockAlign;
wfx.cbSize = 0;
blockSize = (wfx.nAvgBytesPerSec*granularityInMS)/1000;
// Use a 10-second input buffer, to allow for CPU competition from video, etc.,
// and also for some audio cards that buffer as much as 5 seconds of audio.
unsigned const bufferSeconds = 10;
numBlocks = (bufferSeconds*1000)/granularityInMS;
if (!waveIn_open(index, wfx)) return False;
// Set this process's priority high. I'm not sure how much this is really needed,
// but the "rat" code does this:
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
return True;
}
Boolean WindowsAudioInputDevice_common::waveIn_open(unsigned uid, WAVEFORMATEX& wfx) {
if (shWaveIn != NULL) return True; // already open
do {
waveIn_reset();
if (waveInOpen(&shWaveIn, uid, &wfx,
(DWORD)waveInCallback, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR) break;
// Allocate read buffers, and headers:
readData = new unsigned char[numBlocks*blockSize];
if (readData == NULL) break;
readHdrs = new WAVEHDR[numBlocks];
if (readHdrs == NULL) break;
readHead = readTail = NULL;
readTimes = new struct timeval[numBlocks];
if (readTimes == NULL) break;
// Initialize headers:
for (unsigned i = 0; i < numBlocks; ++i) {
readHdrs[i].lpData = (char*)&readData[i*blockSize];
readHdrs[i].dwBufferLength = blockSize;
readHdrs[i].dwFlags = 0;
if (waveInPrepareHeader(shWaveIn, &readHdrs[i], sizeof (WAVEHDR)) != MMSYSERR_NOERROR) break;
if (waveInAddBuffer(shWaveIn, &readHdrs[i], sizeof (WAVEHDR)) != MMSYSERR_NOERROR) break;
}
if (waveInStart(shWaveIn) != MMSYSERR_NOERROR) break;
#ifdef UNICODE
hAudioReady = CreateEvent(NULL, TRUE, FALSE, L"waveIn Audio Ready");
#else
hAudioReady = CreateEvent(NULL, TRUE, FALSE, "waveIn Audio Ready");
#endif
return True;
} while (0);
waveIn_reset();
return False;
}
void WindowsAudioInputDevice_common::waveIn_close() {
if (shWaveIn == NULL) return; // already closed
waveInStop(shWaveIn);
waveInReset(shWaveIn);
for (unsigned i = 0; i < numBlocks; ++i) {
if (readHdrs[i].dwFlags & WHDR_PREPARED) {
waveInUnprepareHeader(shWaveIn, &readHdrs[i], sizeof (WAVEHDR));
}
}
waveInClose(shWaveIn);
waveIn_reset();
}
void WindowsAudioInputDevice_common::waveIn_reset() {
shWaveIn = NULL;
delete[] readData; readData = NULL;
bytesUsedAtReadHead = 0;
delete[] readHdrs; readHdrs = NULL;
readHead = readTail = NULL;
delete[] readTimes; readTimes = NULL;
hAudioReady = NULL;
}
unsigned WindowsAudioInputDevice_common::readFromBuffers(unsigned char* to, unsigned numBytesWanted, struct timeval& creationTime) {
// Begin by computing the creation time of (the first bytes of) this returned audio data:
if (readHead != NULL) {
int hdrIndex = readHead - readHdrs;
creationTime = readTimes[hdrIndex];
// Adjust this time to allow for any data that's already been read from this buffer:
if (bytesUsedAtReadHead > 0) {
creationTime.tv_usec += (unsigned)(uSecsPerByte*bytesUsedAtReadHead);
creationTime.tv_sec += creationTime.tv_usec/1000000;
creationTime.tv_usec %= 1000000;
}
}
// Then, read from each available buffer, until we have the data that we want:
unsigned numBytesRead = 0;
while (readHead != NULL && numBytesRead < numBytesWanted) {
unsigned thisRead = min(readHead->dwBytesRecorded - bytesUsedAtReadHead, numBytesWanted - numBytesRead);
memmove(&to[numBytesRead], &readHead->lpData[bytesUsedAtReadHead], thisRead);
numBytesRead += thisRead;
bytesUsedAtReadHead += thisRead;
if (bytesUsedAtReadHead == readHead->dwBytesRecorded) {
// We're finished with the block; give it back to the device:
releaseHeadBuffer();
}
}
return numBytesRead;
}
void WindowsAudioInputDevice_common::releaseHeadBuffer() {
WAVEHDR* toRelease = readHead;
if (readHead == NULL) return;
readHead = readHead->lpNext;
if (readHead == NULL) readTail = NULL;
toRelease->lpNext = NULL;
toRelease->dwBytesRecorded = 0;
toRelease->dwFlags &= ~WHDR_DONE;
waveInAddBuffer(shWaveIn, toRelease, sizeof (WAVEHDR));
bytesUsedAtReadHead = 0;
}
void WindowsAudioInputDevice_common::waveInProc(WAVEHDR* hdr) {
unsigned hdrIndex = hdr - readHdrs;
// Record the time that the data arrived:
int dontCare;
gettimeofday(&readTimes[hdrIndex], &dontCare);
// Add the block to the tail of the queue:
hdr->lpNext = NULL;
if (readTail != NULL) {
readTail->lpNext = hdr;
readTail = hdr;
} else {
readHead = readTail = hdr;
}
SetEvent(hAudioReady);
}
HWAVEIN WindowsAudioInputDevice_common::shWaveIn = NULL;
unsigned WindowsAudioInputDevice_common::blockSize = 0;
unsigned WindowsAudioInputDevice_common::numBlocks = 0;
unsigned char* WindowsAudioInputDevice_common::readData = NULL;
DWORD WindowsAudioInputDevice_common::bytesUsedAtReadHead = 0;
double WindowsAudioInputDevice_common::uSecsPerByte = 0.0;
double WindowsAudioInputDevice_common::averageLevel = 0.0;
WAVEHDR* WindowsAudioInputDevice_common::readHdrs = NULL;
WAVEHDR* WindowsAudioInputDevice_common::readHead = NULL;
WAVEHDR* WindowsAudioInputDevice_common::readTail = NULL;
struct timeval* WindowsAudioInputDevice_common::readTimes = NULL;
HANDLE WindowsAudioInputDevice_common::hAudioReady = NULL;
live/WindowsAudioInputDevice/WindowsAudioInputDevice_common.hh 000444 001752 001752 00000006012 13242237370 024670 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// Base class for both library versions:
// One that uses Windows' built-in software mixer; another that doesn't.
// C++ header
#ifndef _WINDOWS_AUDIO_INPUT_DEVICE_COMMON_HH
#define _WINDOWS_AUDIO_INPUT_DEVICE_COMMON_HH
#ifndef _AUDIO_INPUT_DEVICE_HH
#include "AudioInputDevice.hh"
#endif
class WindowsAudioInputDevice_common: public AudioInputDevice {
public:
static Boolean openWavInPort(int index, unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS);
static void waveIn_close();
static void waveInProc(WAVEHDR* hdr); // Windows audio callback function
protected:
WindowsAudioInputDevice_common(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample, unsigned char numChannels,
unsigned samplingFrequency, unsigned granularityInMS);
// virtual base class
virtual ~WindowsAudioInputDevice_common();
Boolean initialSetInputPort(int portIndex);
protected:
int fCurPortIndex;
private:
// redefined virtual functions:
virtual void doGetNextFrame();
virtual void doStopGettingFrames();
virtual double getAverageLevel() const;
private:
static void audioReadyPoller(void* clientData);
void audioReadyPoller1();
void onceAudioIsReady();
// Audio input buffering:
static Boolean waveIn_open(unsigned uid, WAVEFORMATEX& wfx);
static void waveIn_reset(); // used to implement both of the above
static unsigned readFromBuffers(unsigned char* to, unsigned numBytesWanted, struct timeval& creationTime);
static void releaseHeadBuffer(); // from the input header queue
private:
static unsigned _bitsPerSample;
static HWAVEIN shWaveIn;
static unsigned blockSize, numBlocks;
static unsigned char* readData; // buffer for incoming audio data
static DWORD bytesUsedAtReadHead; // number of bytes that have already been read at head
static double uSecsPerByte; // used to adjust the time for # bytes consumed since arrival
static double averageLevel;
static WAVEHDR *readHdrs, *readHead, *readTail; // input header queue
static struct timeval* readTimes;
static HANDLE hAudioReady; // audio ready event
Boolean fHaveStarted;
unsigned fTotalPollingDelay; // uSeconds
};
#endif
live/WindowsAudioInputDevice/WindowsAudioInputDevice_mixer.cpp 000444 001752 001752 00000037440 13242237370 024720 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 2001-2004 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// This version uses Windows' built-in software mixer.
// Implementation
#include
////////// Mixer and AudioInputPort definition //////////
class AudioInputPort {
public:
int tag;
DWORD dwComponentType;
char name[MIXER_LONG_NAME_CHARS];
};
class Mixer {
public:
Mixer();
virtual ~Mixer();
void open(unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS);
void open(); // open with default parameters
void getPortsInfo();
Boolean enableInputPort(unsigned portIndex, char const*& errReason, MMRESULT& errCode);
void close();
unsigned index;
HMIXER hMixer; // valid when open
DWORD dwRecLineID; // valid when open
unsigned numPorts;
AudioInputPort* ports;
char name[MAXPNAMELEN];
};
////////// AudioInputDevice (remaining) implementation //////////
AudioInputDevice*
AudioInputDevice::createNew(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample,
unsigned char numChannels,
unsigned samplingFrequency,
unsigned granularityInMS) {
Boolean success;
WindowsAudioInputDevice* newSource
= new WindowsAudioInputDevice(env, inputPortNumber,
bitsPerSample, numChannels,
samplingFrequency, granularityInMS,
success);
if (!success) {delete newSource; newSource = NULL;}
return newSource;
}
AudioPortNames* AudioInputDevice::getPortNames() {
WindowsAudioInputDevice::initializeIfNecessary();
AudioPortNames* portNames = new AudioPortNames;
portNames->numPorts = WindowsAudioInputDevice::numInputPortsTotal;
portNames->portName = new char*[WindowsAudioInputDevice::numInputPortsTotal];
// If there's more than one mixer, print only the port name.
// If there's two or more mixers, also include the mixer name
// (to disambiguate port names that may be the same name in different mixers)
char portNameBuffer[2*MAXPNAMELEN+10/*slop*/];
char mixerNameBuffer[MAXPNAMELEN];
char const* portNameFmt;
if (WindowsAudioInputDevice::numMixers <= 1) {
portNameFmt = "%s";
} else {
portNameFmt = "%s (%s)";
}
unsigned curPortNum = 0;
for (unsigned i = 0; i < WindowsAudioInputDevice::numMixers; ++i) {
Mixer& mixer = WindowsAudioInputDevice::ourMixers[i];
if (WindowsAudioInputDevice::numMixers <= 1) {
mixerNameBuffer[0] = '\0';
} else {
strncpy(mixerNameBuffer, mixer.name, sizeof mixerNameBuffer);
#if 0
// Hack: Simplify the mixer name, by truncating after the first space character:
for (int k = 0; k < sizeof mixerNameBuffer && mixerNameBuffer[k] != '\0'; ++k) {
if (mixerNameBuffer[k] == ' ') {
mixerNameBuffer[k] = '\0';
break;
}
}
#endif
}
for (unsigned j = 0; j < mixer.numPorts; ++j) {
sprintf(portNameBuffer, portNameFmt, mixer.ports[j].name, mixerNameBuffer);
portNames->portName[curPortNum++] = strDup(portNameBuffer);
}
}
return portNames;
}
////////// WindowsAudioInputDevice implementation //////////
WindowsAudioInputDevice
::WindowsAudioInputDevice(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample,
unsigned char numChannels,
unsigned samplingFrequency,
unsigned granularityInMS,
Boolean& success)
: WindowsAudioInputDevice_common(env, inputPortNumber,
bitsPerSample, numChannels, samplingFrequency, granularityInMS),
fCurMixerId(-1) {
success = initialSetInputPort(inputPortNumber);
}
WindowsAudioInputDevice::~WindowsAudioInputDevice() {
if (fCurMixerId >= 0) ourMixers[fCurMixerId].close();
delete[] ourMixers; ourMixers = NULL;
numMixers = numInputPortsTotal = 0;
}
void WindowsAudioInputDevice::initializeIfNecessary() {
if (ourMixers != NULL) return; // we've already been initialized
numMixers = mixerGetNumDevs();
ourMixers = new Mixer[numMixers];
// Initialize each mixer:
numInputPortsTotal = 0;
for (unsigned i = 0; i < numMixers; ++i) {
Mixer& mixer = ourMixers[i];
mixer.index = i;
mixer.open();
if (mixer.hMixer != NULL) {
// This device has a valid mixer. Get information about its ports:
mixer.getPortsInfo();
mixer.close();
if (mixer.numPorts == 0) continue;
numInputPortsTotal += mixer.numPorts;
} else {
mixer.ports = NULL;
mixer.numPorts = 0;
}
}
}
Boolean WindowsAudioInputDevice::setInputPort(int portIndex) {
initializeIfNecessary();
if (portIndex < 0 || portIndex >= (int)numInputPortsTotal) { // bad index
envir().setResultMsg("Bad input port index\n");
return False;
}
// Find the mixer and port that corresponds to "portIndex":
int newMixerId, portWithinMixer, portIndexCount = 0;
for (newMixerId = 0; newMixerId < (int)numMixers; ++newMixerId) {
int prevPortIndexCount = portIndexCount;
portIndexCount += ourMixers[newMixerId].numPorts;
if (portIndexCount > portIndex) { // it's with this mixer
portWithinMixer = portIndex - prevPortIndexCount;
break;
}
}
// Check that this mixer is allowed:
if (allowedDeviceNames != NULL) {
int i;
for (i = 0; allowedDeviceNames[i] != NULL; ++i) {
if (strncmp(ourMixers[newMixerId].name, allowedDeviceNames[i],
strlen(allowedDeviceNames[i])) == 0) {
// The allowed device name is a prefix of this mixer's name
break; // this mixer is allowed
}
}
if (allowedDeviceNames[i] == NULL) { // this mixer is not on the allowed list
envir().setResultMsg("Access to this audio device is not allowed\n");
return False;
}
}
if (newMixerId != fCurMixerId) {
// The mixer has changed, so close the old one and open the new one:
if (fCurMixerId >= 0) ourMixers[fCurMixerId].close();
fCurMixerId = newMixerId;
ourMixers[fCurMixerId].open(fNumChannels, fSamplingFrequency, fGranularityInMS);
}
if (portIndex != fCurPortIndex) {
// Change the input port:
fCurPortIndex = portIndex;
char const* errReason;
MMRESULT errCode;
if (!ourMixers[newMixerId].enableInputPort(portWithinMixer, errReason, errCode)) {
char resultMsg[100];
sprintf(resultMsg, "Failed to enable input port: %s failed (0x%08x)\n", errReason, errCode);
envir().setResultMsg(resultMsg);
return False;
}
// Later, may also need to transfer 'gain' to new port #####
}
return True;
}
unsigned WindowsAudioInputDevice::numMixers = 0;
Mixer* WindowsAudioInputDevice::ourMixers = NULL;
unsigned WindowsAudioInputDevice::numInputPortsTotal = 0;
////////// Mixer and AudioInputPort implementation //////////
Mixer::Mixer()
: hMixer(NULL), dwRecLineID(0), numPorts(0), ports(NULL) {
}
Mixer::~Mixer() {
delete[] ports;
}
void Mixer::open(unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS) {
HMIXER newHMixer = NULL;
do {
MIXERCAPS mc;
if (mixerGetDevCaps(index, &mc, sizeof mc) != MMSYSERR_NOERROR) break;
#ifdef UNICODE
// Copy the mixer name:
wcstombs(name, mc.szPname, MAXPNAMELEN);
#else
strncpy(name, mc.szPname, MAXPNAMELEN);
#endif
// Find the correct line for this mixer:
unsigned i, uWavIn;
unsigned nWavIn = waveInGetNumDevs();
for (i = 0; i < nWavIn; ++i) {
WAVEINCAPS wic;
if (waveInGetDevCaps(i, &wic, sizeof wic) != MMSYSERR_NOERROR) continue;
MIXERLINE ml;
ml.cbStruct = sizeof ml;
ml.Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
#ifdef UNICODE
wcsncpy(ml.Target.szPname, wic.szPname, MAXPNAMELEN);
#else
strncpy(ml.Target.szPname, wic.szPname, MAXPNAMELEN);
#endif
ml.Target.vDriverVersion = wic.vDriverVersion;
ml.Target.wMid = wic.wMid;
ml.Target.wPid = wic.wPid;
if (mixerGetLineInfo((HMIXEROBJ)index, &ml, MIXER_GETLINEINFOF_TARGETTYPE/*|MIXER_OBJECTF_MIXER*/) == MMSYSERR_NOERROR) {
// this is the right line
uWavIn = i;
dwRecLineID = ml.dwLineID;
break;
}
}
if (i >= nWavIn) break; // error: we couldn't find the right line
if (mixerOpen(&newHMixer, index, (unsigned long)NULL, (unsigned long)NULL, MIXER_OBJECTF_MIXER) != MMSYSERR_NOERROR) break;
if (newHMixer == NULL) break;
// Sanity check: re-call "mixerGetDevCaps()" using the mixer device handle:
if (mixerGetDevCaps((UINT)newHMixer, &mc, sizeof mc) != MMSYSERR_NOERROR) break;
if (mc.cDestinations < 1) break; // error: this mixer has no destinations
if (!WindowsAudioInputDevice_common::openWavInPort(uWavIn, numChannels, samplingFrequency, granularityInMS)) break;
hMixer = newHMixer;
return;
} while (0);
// An error occurred:
close();
}
void Mixer::open() {
open(1, 8000, 20);
}
void Mixer::getPortsInfo() {
MIXERCAPS mc;
mixerGetDevCaps((UINT)hMixer, &mc, sizeof mc);
MIXERLINE mlt;
unsigned i;
for (i = 0; i < mc.cDestinations; ++i) {
memset(&mlt, 0, sizeof mlt);
mlt.cbStruct = sizeof mlt;
mlt.dwDestination = i;
if (mixerGetLineInfo((HMIXEROBJ)hMixer, &mlt, MIXER_GETLINEINFOF_DESTINATION) != MMSYSERR_NOERROR) continue;
if (mlt.dwLineID == dwRecLineID) break; // this is the destination we're interested in
}
ports = new AudioInputPort[mlt.cConnections];
numPorts = mlt.cConnections;
for (i = 0; i < numPorts; ++i) {
MIXERLINE mlc;
memcpy(&mlc, &mlt, sizeof mlc);
mlc.dwSource = i;
mixerGetLineInfo((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINEINFOF_SOURCE/*|MIXER_OBJECTF_HMIXER*/);
ports[i].tag = mlc.dwLineID;
ports[i].dwComponentType = mlc.dwComponentType;
#ifdef UNICODE
wcstombs(ports[i].name, mlc.szName, MIXER_LONG_NAME_CHARS);
#else
strncpy(ports[i].name, mlc.szName, MIXER_LONG_NAME_CHARS);
#endif
}
// Make the microphone the first port in the list:
for (i = 1; i < numPorts; ++i) {
#ifdef OLD_MICROPHONE_TESTING_CODE
if (_strnicmp("mic", ports[i].name, 3) == 0 ||
_strnicmp("mik", ports[i].name, 3) == 0) {
#else
if (ports[i].dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE) {
#endif
AudioInputPort tmp = ports[0];
ports[0] = ports[i];
ports[i] = tmp;
}
}
}
Boolean Mixer::enableInputPort(unsigned portIndex, char const*& errReason, MMRESULT& errCode) {
errReason = NULL; // unless there's an error
AudioInputPort& port = ports[portIndex];
MIXERCONTROL mc;
mc.cMultipleItems = 1; // in case it doesn't get set below
MIXERLINECONTROLS mlc;
#if 0 // the following doesn't seem to be needed, and can fail:
mlc.cbStruct = sizeof mlc;
mlc.pamxctrl = &mc;
mlc.cbmxctrl = sizeof (MIXERCONTROL);
mlc.dwLineID = port.tag;
mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
if ((errCode = mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
errReason = "mixerGetLineControls()";
return False;
}
#endif
MIXERLINE ml;
memset(&ml, 0, sizeof (MIXERLINE));
ml.cbStruct = sizeof (MIXERLINE);
ml.dwLineID = port.tag;
if ((errCode = mixerGetLineInfo((HMIXEROBJ)hMixer, &ml, MIXER_GETLINEINFOF_LINEID)) != MMSYSERR_NOERROR) {
errReason = "mixerGetLineInfo()1";
return False;
}
#ifdef UNICODE
wchar_t portname[MIXER_LONG_NAME_CHARS+1];
wcsncpy(portname, ml.szName, MIXER_LONG_NAME_CHARS);
#else
char portname[MIXER_LONG_NAME_CHARS+1];
strncpy(portname, ml.szName, MIXER_LONG_NAME_CHARS);
#endif
memset(&ml, 0, sizeof (MIXERLINE));
ml.cbStruct = sizeof (MIXERLINE);
ml.dwLineID = dwRecLineID;
if ((errCode = mixerGetLineInfo((HMIXEROBJ)hMixer, &ml, MIXER_GETLINEINFOF_LINEID/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
errReason = "mixerGetLineInfo()2";
return False;
}
// Get Mixer/MUX control information (need control id to set and get control details)
mlc.cbStruct = sizeof mlc;
mlc.dwLineID = ml.dwLineID;
mlc.cControls = 1;
mc.cbStruct = sizeof mc; // Needed???#####
mc.dwControlID = 0xDEADBEEF; // For testing #####
mlc.pamxctrl = &mc;
mlc.cbmxctrl = sizeof mc;
mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX; // Single Select
if ((errCode = mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_MIXER; // Multiple Select
mixerGetLineControls((HMIXEROBJ)hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE/*|MIXER_OBJECTF_HMIXER*/);
}
unsigned matchLine = 0;
if (mc.cMultipleItems > 1) {
// Before getting control, we need to know which line to grab.
// We figure this out by listing the lines, and comparing names:
MIXERCONTROLDETAILS mcd;
mcd.cbStruct = sizeof mcd;
mcd.cChannels = ml.cChannels;
mcd.cMultipleItems = mc.cMultipleItems;
MIXERCONTROLDETAILS_LISTTEXT* mcdlText = new MIXERCONTROLDETAILS_LISTTEXT[mc.cMultipleItems];
mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_LISTTEXT);
mcd.paDetails = mcdlText;
if (mc.dwControlID != 0xDEADBEEF) { // we know the control id for real
mcd.dwControlID = mc.dwControlID;
if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_LISTTEXT/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
delete[] mcdlText;
errReason = "mixerGetControlDetails()1";
return False;
}
} else {
// Hack: We couldn't find a MUX or MIXER control, so try to guess the control id:
for (mc.dwControlID = 0; mc.dwControlID < 32; ++mc.dwControlID) {
mcd.dwControlID = mc.dwControlID;
if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_LISTTEXT/*|MIXER_OBJECTF_HMIXER*/)) == MMSYSERR_NOERROR) break;
}
if (mc.dwControlID == 32) { // unable to guess mux/mixer control id
delete[] mcdlText;
errReason = "mixerGetControlDetails()2";
return False;
}
}
#ifdef UNICODE
for (unsigned i = 0; i < mcd.cMultipleItems; ++i) {
if (wcscmp(mcdlText[i].szName, portname) == 0) {
matchLine = i;
break;
}
}
#else
for (unsigned i = 0; i < mcd.cMultipleItems; ++i) {
if (strcmp(mcdlText[i].szName, portname) == 0) {
matchLine = i;
break;
}
}
#endif
delete[] mcdlText;
}
// Now get control itself:
MIXERCONTROLDETAILS mcd;
mcd.cbStruct = sizeof mcd;
mcd.dwControlID = mc.dwControlID;
mcd.cChannels = ml.cChannels;
mcd.cMultipleItems = mc.cMultipleItems;
MIXERCONTROLDETAILS_BOOLEAN* mcdbState = new MIXERCONTROLDETAILS_BOOLEAN[mc.cMultipleItems];
mcd.paDetails = mcdbState;
mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_BOOLEAN);
if ((errCode = mixerGetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_GETCONTROLDETAILSF_VALUE/*|MIXER_OBJECTF_HMIXER*/)) != MMSYSERR_NOERROR) {
delete[] mcdbState;
errReason = "mixerGetControlDetails()3";
return False;
}
for (unsigned j = 0; j < mcd.cMultipleItems; ++j) {
mcdbState[j].fValue = (j == matchLine);
}
if ((errCode = mixerSetControlDetails((HMIXEROBJ)hMixer, &mcd, MIXER_OBJECTF_HMIXER)) != MMSYSERR_NOERROR) {
delete[] mcdbState;
errReason = "mixerSetControlDetails()";
return False;
}
delete[] mcdbState;
return True;
}
void Mixer::close() {
WindowsAudioInputDevice_common::waveIn_close();
if (hMixer != NULL) mixerClose(hMixer);
hMixer = NULL; dwRecLineID = 0;
}
live/WindowsAudioInputDevice/WindowsAudioInputDevice_mixer.hh 000444 001752 001752 00000003653 13242237370 024534 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// This version uses Windows' built-in software mixer.
// C++ header
//
// To use this, call "AudioInputDevice::createNew()".
// You can also call "AudioInputDevice::getPortNames()" to get a list
// of port names.
#ifndef _WINDOWS_AUDIO_INPUT_DEVICE_MIXER_HH
#define _WINDOWS_AUDIO_INPUT_DEVICE_MIXER_HH
#ifndef _WINDOWS_AUDIO_INPUT_DEVICE_COMMON_HH
#include "WindowsAudioInputDevice_common.hh"
#endif
class WindowsAudioInputDevice: public WindowsAudioInputDevice_common {
private:
friend class AudioInputDevice;
WindowsAudioInputDevice(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample, unsigned char numChannels,
unsigned samplingFrequency, unsigned granularityInMS,
Boolean& success);
// called only by createNew()
virtual ~WindowsAudioInputDevice();
static void initializeIfNecessary();
private:
// redefined virtual functions:
virtual Boolean setInputPort(int portIndex);
private:
static unsigned numMixers;
static class Mixer* ourMixers;
static unsigned numInputPortsTotal;
int fCurMixerId;
};
#endif
live/WindowsAudioInputDevice/WindowsAudioInputDevice_noMixer.cpp 000444 001752 001752 00000013106 13242237370 025206 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 2001-2004 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// This version does not use Windows' built-in software mixer.
// Implementation
#include
////////// AudioInputPort definition //////////
class AudioInputPort {
public:
void open(unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS);
void open(); // open with default parameters
void close();
public:
int index;
char name[MAXPNAMELEN];
};
////////// AudioInputDevice (remaining) implementation //////////
AudioInputDevice*
AudioInputDevice::createNew(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample,
unsigned char numChannels,
unsigned samplingFrequency,
unsigned granularityInMS) {
Boolean success;
WindowsAudioInputDevice* newSource
= new WindowsAudioInputDevice(env, inputPortNumber,
bitsPerSample, numChannels,
samplingFrequency, granularityInMS,
success);
if (!success) {delete newSource; newSource = NULL;}
return newSource;
}
AudioPortNames* AudioInputDevice::getPortNames() {
WindowsAudioInputDevice::initializeIfNecessary();
AudioPortNames* portNames = new AudioPortNames;
portNames->numPorts = WindowsAudioInputDevice::numAudioInputPorts;
portNames->portName = new char*[WindowsAudioInputDevice::numAudioInputPorts];
for (unsigned i = 0; i < WindowsAudioInputDevice::numAudioInputPorts; ++i) {
AudioInputPort& audioInputPort = WindowsAudioInputDevice::ourAudioInputPorts[i];
portNames->portName[i] = strDup(audioInputPort.name);
}
return portNames;
}
////////// WindowsAudioInputDevice implementation //////////
WindowsAudioInputDevice
::WindowsAudioInputDevice(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample,
unsigned char numChannels,
unsigned samplingFrequency,
unsigned granularityInMS,
Boolean& success)
: WindowsAudioInputDevice_common(env, inputPortNumber,
bitsPerSample, numChannels, samplingFrequency, granularityInMS) {
success = initialSetInputPort(inputPortNumber);
}
WindowsAudioInputDevice::~WindowsAudioInputDevice() {
if (fCurPortIndex >= 0) ourAudioInputPorts[fCurPortIndex].close();
delete[] ourAudioInputPorts; ourAudioInputPorts = NULL;
numAudioInputPorts = 0;
}
void WindowsAudioInputDevice::initializeIfNecessary() {
if (ourAudioInputPorts != NULL) return; // we've already been initialized
numAudioInputPorts = waveInGetNumDevs();
ourAudioInputPorts = new AudioInputPort[numAudioInputPorts];
// Initialize each audio input port
for (unsigned i = 0; i < numAudioInputPorts; ++i) {
AudioInputPort& port = ourAudioInputPorts[i];
port.index = i;
port.open(); // to set the port name
port.close();
}
}
Boolean WindowsAudioInputDevice::setInputPort(int portIndex) {
initializeIfNecessary();
if (portIndex < 0 || portIndex >= (int)numAudioInputPorts) { // bad index
envir().setResultMsg("Bad input port index\n");
return False;
}
// Check that this port is allowed:
if (allowedDeviceNames != NULL) {
int i;
for (i = 0; allowedDeviceNames[i] != NULL; ++i) {
if (strncmp(ourAudioInputPorts[portIndex].name, allowedDeviceNames[i],
strlen(allowedDeviceNames[i])) == 0) {
// The allowed device name is a prefix of this port's name
break; // this port is allowed
}
}
if (allowedDeviceNames[i] == NULL) { // this port is not on the allowed list
envir().setResultMsg("Access to this audio device is not allowed\n");
return False;
}
}
if (portIndex != fCurPortIndex) {
// The port has changed, so close the old one and open the new one:
if (fCurPortIndex >= 0) ourAudioInputPorts[fCurPortIndex].close();;
fCurPortIndex = portIndex;
ourAudioInputPorts[fCurPortIndex].open(fNumChannels, fSamplingFrequency, fGranularityInMS);
}
fCurPortIndex = portIndex;
return True;
}
unsigned WindowsAudioInputDevice::numAudioInputPorts = 0;
AudioInputPort* WindowsAudioInputDevice::ourAudioInputPorts = NULL;
////////// AudioInputPort implementation //////////
void AudioInputPort::open(unsigned numChannels, unsigned samplingFrequency, unsigned granularityInMS) {
do {
// Get the port name:
WAVEINCAPS wic;
if (waveInGetDevCaps(index, &wic, sizeof wic) != MMSYSERR_NOERROR) {
name[0] = '\0';
break;
}
#ifdef UNICODE
// Copy the mixer name:
wcstombs(name, wic.szPname, MAXPNAMELEN);
#else
strncpy(name, wic.szPname, MAXPNAMELEN);
#endif
if (!WindowsAudioInputDevice_common::openWavInPort(index, numChannels, samplingFrequency, granularityInMS)) break;
return;
} while (0);
// An error occurred:
close();
}
void AudioInputPort::open() {
open(1, 8000, 20);
}
void AudioInputPort::close() {
WindowsAudioInputDevice_common::waveIn_close();
}
live/WindowsAudioInputDevice/WindowsAudioInputDevice_noMixer.hh 000444 001752 001752 00000003630 13242237370 025024 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Windows implementation of a generic audio input device
// This version does not use Windows' built-in software mixer.
// C++ header
//
// To use this, call "AudioInputDevice::createNew()".
// You can also call "AudioInputDevice::getPortNames()" to get a list
// of port names.
#ifndef _WINDOWS_AUDIO_INPUT_DEVICE_NOMIXER_HH
#define _WINDOWS_AUDIO_INPUT_DEVICE_NOMIXER_HH
#ifndef _WINDOWS_AUDIO_INPUT_DEVICE_COMMON_HH
#include "WindowsAudioInputDevice_common.hh"
#endif
class WindowsAudioInputDevice: public WindowsAudioInputDevice_common {
private:
friend class AudioInputDevice;
WindowsAudioInputDevice(UsageEnvironment& env, int inputPortNumber,
unsigned char bitsPerSample, unsigned char numChannels,
unsigned samplingFrequency, unsigned granularityInMS,
Boolean& success);
// called only by createNew()
virtual ~WindowsAudioInputDevice();
static void initializeIfNecessary();
private:
// redefined virtual functions:
virtual Boolean setInputPort(int portIndex);
private:
static unsigned numAudioInputPorts;
static class AudioInputPort* ourAudioInputPorts;
};
#endif
live/proxyServer/COPYING 000755 001752 001752 00000000000 13242237367 016322 2../COPYING ustar 00rsf rsf 000000 000000 live/proxyServer/live555ProxyServer.cpp 000444 001752 001752 00000021507 13242237367 020166 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// LIVE555 Proxy Server
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
char const* progName;
UsageEnvironment* env;
UserAuthenticationDatabase* authDB = NULL;
UserAuthenticationDatabase* authDBForREGISTER = NULL;
// Default values of command-line parameters:
int verbosityLevel = 0;
Boolean streamRTPOverTCP = False;
portNumBits tunnelOverHTTPPortNum = 0;
portNumBits rtspServerPortNum = 554;
char* username = NULL;
char* password = NULL;
Boolean proxyREGISTERRequests = False;
char* usernameForREGISTER = NULL;
char* passwordForREGISTER = NULL;
static RTSPServer* createRTSPServer(Port port) {
if (proxyREGISTERRequests) {
return RTSPServerWithREGISTERProxying::createNew(*env, port, authDB, authDBForREGISTER, 65, streamRTPOverTCP, verbosityLevel, username, password);
} else {
return RTSPServer::createNew(*env, port, authDB);
}
}
void usage() {
*env << "Usage: " << progName
<< " [-v|-V]"
<< " [-t|-T ]"
<< " [-p ]"
<< " [-u ]"
<< " [-R] [-U ]"
<< " ... \n";
exit(1);
}
int main(int argc, char** argv) {
// Increase the maximum size of video frames that we can 'proxy' without truncation.
// (Such frames are unreasonably large; the back-end servers should really not be sending frames this large!)
OutPacketBuffer::maxSize = 100000; // bytes
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
*env << "LIVE555 Proxy Server\n"
<< "\t(LIVE555 Streaming Media library version "
<< LIVEMEDIA_LIBRARY_VERSION_STRING
<< "; licensed under the GNU LGPL)\n\n";
// Check command-line arguments: optional parameters, then one or more rtsp:// URLs (of streams to be proxied):
progName = argv[0];
if (argc < 2) usage();
while (argc > 1) {
// Process initial command-line options (beginning with "-"):
char* const opt = argv[1];
if (opt[0] != '-') break; // the remaining parameters are assumed to be "rtsp://" URLs
switch (opt[1]) {
case 'v': { // verbose output
verbosityLevel = 1;
break;
}
case 'V': { // more verbose output
verbosityLevel = 2;
break;
}
case 't': {
// Stream RTP and RTCP over the TCP 'control' connection.
// (This is for the 'back end' (i.e., proxied) stream only.)
streamRTPOverTCP = True;
break;
}
case 'T': {
// stream RTP and RTCP over a HTTP connection
if (argc > 2 && argv[2][0] != '-') {
// The next argument is the HTTP server port number:
if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1
&& tunnelOverHTTPPortNum > 0) {
++argv; --argc;
break;
}
}
// If we get here, the option was specified incorrectly:
usage();
break;
}
case 'p': {
// specify a rtsp server port number
if (argc > 2 && argv[2][0] != '-') {
// The next argument is the rtsp server port number:
if (sscanf(argv[2], "%hu", &rtspServerPortNum) == 1
&& rtspServerPortNum > 0) {
++argv; --argc;
break;
}
}
// If we get here, the option was specified incorrectly:
usage();
break;
}
case 'u': { // specify a username and password (to be used if the 'back end' (i.e., proxied) stream requires authentication)
if (argc < 4) usage(); // there's no argv[3] (for the "password")
username = argv[2];
password = argv[3];
argv += 2; argc -= 2;
break;
}
case 'U': { // specify a username and password to use to authenticate incoming "REGISTER" commands
if (argc < 4) usage(); // there's no argv[3] (for the "password")
usernameForREGISTER = argv[2];
passwordForREGISTER = argv[3];
if (authDBForREGISTER == NULL) authDBForREGISTER = new UserAuthenticationDatabase;
authDBForREGISTER->addUserRecord(usernameForREGISTER, passwordForREGISTER);
argv += 2; argc -= 2;
break;
}
case 'R': { // Handle incoming "REGISTER" requests by proxying the specified stream:
proxyREGISTERRequests = True;
break;
}
default: {
usage();
break;
}
}
++argv; --argc;
}
if (argc < 2 && !proxyREGISTERRequests) usage(); // there must be at least one "rtsp://" URL at the end
// Make sure that the remaining arguments appear to be "rtsp://" URLs:
int i;
for (i = 1; i < argc; ++i) {
if (strncmp(argv[i], "rtsp://", 7) != 0) usage();
}
// Do some additional checking for invalid command-line argument combinations:
if (authDBForREGISTER != NULL && !proxyREGISTERRequests) {
*env << "The '-U ' option can be used only with -R\n";
usage();
}
if (streamRTPOverTCP) {
if (tunnelOverHTTPPortNum > 0) {
*env << "The -t and -T options cannot both be used!\n";
usage();
} else {
tunnelOverHTTPPortNum = (portNumBits)(~0); // hack to tell "ProxyServerMediaSession" to stream over TCP, but not using HTTP
}
}
#ifdef ACCESS_CONTROL
// To implement client access control to the RTSP server, do the following:
authDB = new UserAuthenticationDatabase;
authDB->addUserRecord("username1", "password1"); // replace these with real strings
// Repeat this line with each , that you wish to allow access to the server.
#endif
// Create the RTSP server. Try first with the configured port number,
// and then with the default port number (554) if different,
// and then with the alternative port number (8554):
RTSPServer* rtspServer;
rtspServer = createRTSPServer(rtspServerPortNum);
if (rtspServer == NULL) {
if (rtspServerPortNum != 554) {
*env << "Unable to create a RTSP server with port number " << rtspServerPortNum << ": " << env->getResultMsg() << "\n";
*env << "Trying instead with the standard port numbers (554 and 8554)...\n";
rtspServerPortNum = 554;
rtspServer = createRTSPServer(rtspServerPortNum);
}
}
if (rtspServer == NULL) {
rtspServerPortNum = 8554;
rtspServer = createRTSPServer(rtspServerPortNum);
}
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
// Create a proxy for each "rtsp://" URL specified on the command line:
for (i = 1; i < argc; ++i) {
char const* proxiedStreamURL = argv[i];
char streamName[30];
if (argc == 2) {
sprintf(streamName, "%s", "proxyStream"); // there's just one stream; give it this name
} else {
sprintf(streamName, "proxyStream-%d", i); // there's more than one stream; distinguish them by name
}
ServerMediaSession* sms
= ProxyServerMediaSession::createNew(*env, rtspServer,
proxiedStreamURL, streamName,
username, password, tunnelOverHTTPPortNum, verbosityLevel);
rtspServer->addServerMediaSession(sms);
char* proxyStreamURL = rtspServer->rtspURL(sms);
*env << "RTSP stream, proxying the stream \"" << proxiedStreamURL << "\"\n";
*env << "\tPlay this stream using the URL: " << proxyStreamURL << "\n";
delete[] proxyStreamURL;
}
if (proxyREGISTERRequests) {
*env << "(We handle incoming \"REGISTER\" requests on port " << rtspServerPortNum << ")\n";
}
// Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
// Try first with the default HTTP port (80), and then with the alternative HTTP
// port numbers (8000 and 8080).
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
*env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n";
} else {
*env << "\n(RTSP-over-HTTP tunneling is not available.)\n";
}
// Now, enter the event loop:
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
live/proxyServer/Makefile.head 000440 001752 001752 00000000727 13242237367 016370 0 ustar 00rsf rsf 000000 000000 INCLUDES = -I../UsageEnvironment/include -I../groupsock/include -I../liveMedia/include -I../BasicUsageEnvironment/include
# Default library filename suffixes for each library that we link with. The "config.*" file might redefine these later.
libliveMedia_LIB_SUFFIX = $(LIB_SUFFIX)
libBasicUsageEnvironment_LIB_SUFFIX = $(LIB_SUFFIX)
libUsageEnvironment_LIB_SUFFIX = $(LIB_SUFFIX)
libgroupsock_LIB_SUFFIX = $(LIB_SUFFIX)
##### Change the following for your environment:
live/proxyServer/Makefile.tail 000444 001752 001752 00000002477 13242237367 016430 0 ustar 00rsf rsf 000000 000000 ##### End of variables to change
PROXY_SERVER = live555ProxyServer$(EXE)
PREFIX = /usr/local
ALL = $(PROXY_SERVER)
all: $(ALL)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
PROXY_SERVER_OBJS = live555ProxyServer.$(OBJ)
USAGE_ENVIRONMENT_DIR = ../UsageEnvironment
USAGE_ENVIRONMENT_LIB = $(USAGE_ENVIRONMENT_DIR)/libUsageEnvironment.$(libUsageEnvironment_LIB_SUFFIX)
BASIC_USAGE_ENVIRONMENT_DIR = ../BasicUsageEnvironment
BASIC_USAGE_ENVIRONMENT_LIB = $(BASIC_USAGE_ENVIRONMENT_DIR)/libBasicUsageEnvironment.$(libBasicUsageEnvironment_LIB_SUFFIX)
LIVEMEDIA_DIR = ../liveMedia
LIVEMEDIA_LIB = $(LIVEMEDIA_DIR)/libliveMedia.$(libliveMedia_LIB_SUFFIX)
GROUPSOCK_DIR = ../groupsock
GROUPSOCK_LIB = $(GROUPSOCK_DIR)/libgroupsock.$(libgroupsock_LIB_SUFFIX)
LOCAL_LIBS = $(LIVEMEDIA_LIB) $(GROUPSOCK_LIB) \
$(BASIC_USAGE_ENVIRONMENT_LIB) $(USAGE_ENVIRONMENT_LIB)
LIBS = $(LOCAL_LIBS) $(LIBS_FOR_CONSOLE_APPLICATION)
live555ProxyServer$(EXE): $(PROXY_SERVER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(PROXY_SERVER_OBJS) $(LIBS)
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
install: $(PROXY_SERVER)
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $(PROXY_SERVER) $(DESTDIR)$(PREFIX)/bin
##### Any additional, platform-specific rules come here:
live/proxyServer/COPYING.LESSER 000755 001752 001752 00000000000 13242237367 020312 2../COPYING.LESSER ustar 00rsf rsf 000000 000000 live/mediaServer/COPYING 000755 001752 001752 00000000000 13242237367 016220 2../COPYING ustar 00rsf rsf 000000 000000 live/mediaServer/DynamicRTSPServer.cpp 000444 001752 001752 00000024541 13242237367 017722 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A subclass of "RTSPServer" that creates "ServerMediaSession"s on demand,
// based on whether or not the specified stream name exists as a file
// Implementation
#include "DynamicRTSPServer.hh"
#include
#include
DynamicRTSPServer*
DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationTestSeconds) {
int ourSocket = setUpOurSocket(env, ourPort);
if (ourSocket == -1) return NULL;
return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
}
DynamicRTSPServer::DynamicRTSPServer(UsageEnvironment& env, int ourSocket,
Port ourPort,
UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)
: RTSPServerSupportingHTTPStreaming(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds) {
}
DynamicRTSPServer::~DynamicRTSPServer() {
}
static ServerMediaSession* createNewSMS(UsageEnvironment& env,
char const* fileName, FILE* fid); // forward
ServerMediaSession* DynamicRTSPServer
::lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession) {
// First, check whether the specified "streamName" exists as a local file:
FILE* fid = fopen(streamName, "rb");
Boolean fileExists = fid != NULL;
// Next, check whether we already have a "ServerMediaSession" for this file:
ServerMediaSession* sms = RTSPServer::lookupServerMediaSession(streamName);
Boolean smsExists = sms != NULL;
// Handle the four possibilities for "fileExists" and "smsExists":
if (!fileExists) {
if (smsExists) {
// "sms" was created for a file that no longer exists. Remove it:
removeServerMediaSession(sms);
sms = NULL;
}
return NULL;
} else {
if (smsExists && isFirstLookupInSession) {
// Remove the existing "ServerMediaSession" and create a new one, in case the underlying
// file has changed in some way:
removeServerMediaSession(sms);
sms = NULL;
}
if (sms == NULL) {
sms = createNewSMS(envir(), streamName, fid);
addServerMediaSession(sms);
}
fclose(fid);
return sms;
}
}
// Special code for handling Matroska files:
struct MatroskaDemuxCreationState {
MatroskaFileServerDemux* demux;
char watchVariable;
};
static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* clientData) {
MatroskaDemuxCreationState* creationState = (MatroskaDemuxCreationState*)clientData;
creationState->demux = newDemux;
creationState->watchVariable = 1;
}
// END Special code for handling Matroska files:
// Special code for handling Ogg files:
struct OggDemuxCreationState {
OggFileServerDemux* demux;
char watchVariable;
};
static void onOggDemuxCreation(OggFileServerDemux* newDemux, void* clientData) {
OggDemuxCreationState* creationState = (OggDemuxCreationState*)clientData;
creationState->demux = newDemux;
creationState->watchVariable = 1;
}
// END Special code for handling Ogg files:
#define NEW_SMS(description) do {\
char const* descStr = description\
", streamed by the LIVE555 Media Server";\
sms = ServerMediaSession::createNew(env, fileName, fileName, descStr);\
} while(0)
static ServerMediaSession* createNewSMS(UsageEnvironment& env,
char const* fileName, FILE* /*fid*/) {
// Use the file name extension to determine the type of "ServerMediaSession":
char const* extension = strrchr(fileName, '.');
if (extension == NULL) return NULL;
ServerMediaSession* sms = NULL;
Boolean const reuseSource = False;
if (strcmp(extension, ".aac") == 0) {
// Assumed to be an AAC Audio (ADTS format) file:
NEW_SMS("AAC Audio");
sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".amr") == 0) {
// Assumed to be an AMR Audio file:
NEW_SMS("AMR Audio");
sms->addSubsession(AMRAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".ac3") == 0) {
// Assumed to be an AC-3 Audio file:
NEW_SMS("AC-3 Audio");
sms->addSubsession(AC3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".m4e") == 0) {
// Assumed to be a MPEG-4 Video Elementary Stream file:
NEW_SMS("MPEG-4 Video");
sms->addSubsession(MPEG4VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".264") == 0) {
// Assumed to be a H.264 Video Elementary Stream file:
NEW_SMS("H.264 Video");
OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.264 frames
sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".265") == 0) {
// Assumed to be a H.265 Video Elementary Stream file:
NEW_SMS("H.265 Video");
OutPacketBuffer::maxSize = 100000; // allow for some possibly large H.265 frames
sms->addSubsession(H265VideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".mp3") == 0) {
// Assumed to be a MPEG-1 or 2 Audio file:
NEW_SMS("MPEG-1 or 2 Audio");
// To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
//#define STREAM_USING_ADUS 1
// To also reorder ADUs before streaming, uncomment the following:
//#define INTERLEAVE_ADUS 1
// (For more information about ADUs and interleaving,
// see )
Boolean useADUs = False;
Interleaving* interleaving = NULL;
#ifdef STREAM_USING_ADUS
useADUs = True;
#ifdef INTERLEAVE_ADUS
unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
unsigned const interleaveCycleSize
= (sizeof interleaveCycle)/(sizeof (unsigned char));
interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);
#endif
#endif
sms->addSubsession(MP3AudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, useADUs, interleaving));
} else if (strcmp(extension, ".mpg") == 0) {
// Assumed to be a MPEG-1 or 2 Program Stream (audio+video) file:
NEW_SMS("MPEG-1 or 2 Program Stream");
MPEG1or2FileServerDemux* demux
= MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);
sms->addSubsession(demux->newVideoServerMediaSubsession());
sms->addSubsession(demux->newAudioServerMediaSubsession());
} else if (strcmp(extension, ".vob") == 0) {
// Assumed to be a VOB (MPEG-2 Program Stream, with AC-3 audio) file:
NEW_SMS("VOB (MPEG-2 video with AC-3 audio)");
MPEG1or2FileServerDemux* demux
= MPEG1or2FileServerDemux::createNew(env, fileName, reuseSource);
sms->addSubsession(demux->newVideoServerMediaSubsession());
sms->addSubsession(demux->newAC3AudioServerMediaSubsession());
} else if (strcmp(extension, ".ts") == 0) {
// Assumed to be a MPEG Transport Stream file:
// Use an index file name that's the same as the TS file name, except with ".tsx":
unsigned indexFileNameLen = strlen(fileName) + 2; // allow for trailing "x\0"
char* indexFileName = new char[indexFileNameLen];
sprintf(indexFileName, "%sx", fileName);
NEW_SMS("MPEG Transport Stream");
sms->addSubsession(MPEG2TransportFileServerMediaSubsession::createNew(env, fileName, indexFileName, reuseSource));
delete[] indexFileName;
} else if (strcmp(extension, ".wav") == 0) {
// Assumed to be a WAV Audio file:
NEW_SMS("WAV Audio Stream");
// To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
// change the following to True:
Boolean convertToULaw = False;
sms->addSubsession(WAVAudioFileServerMediaSubsession::createNew(env, fileName, reuseSource, convertToULaw));
} else if (strcmp(extension, ".dv") == 0) {
// Assumed to be a DV Video file
// First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).
OutPacketBuffer::maxSize = 300000;
NEW_SMS("DV Video");
sms->addSubsession(DVVideoFileServerMediaSubsession::createNew(env, fileName, reuseSource));
} else if (strcmp(extension, ".mkv") == 0 || strcmp(extension, ".webm") == 0) {
// Assumed to be a Matroska file (note that WebM ('.webm') files are also Matroska files)
OutPacketBuffer::maxSize = 100000; // allow for some possibly large VP8 or VP9 frames
NEW_SMS("Matroska video+audio+(optional)subtitles");
// Create a Matroska file server demultiplexor for the specified file.
// (We enter the event loop to wait for this to complete.)
MatroskaDemuxCreationState creationState;
creationState.watchVariable = 0;
MatroskaFileServerDemux::createNew(env, fileName, onMatroskaDemuxCreation, &creationState);
env.taskScheduler().doEventLoop(&creationState.watchVariable);
ServerMediaSubsession* smss;
while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
}
} else if (strcmp(extension, ".ogg") == 0 || strcmp(extension, ".ogv") == 0 || strcmp(extension, ".opus") == 0) {
// Assumed to be an Ogg file
NEW_SMS("Ogg video and/or audio");
// Create a Ogg file server demultiplexor for the specified file.
// (We enter the event loop to wait for this to complete.)
OggDemuxCreationState creationState;
creationState.watchVariable = 0;
OggFileServerDemux::createNew(env, fileName, onOggDemuxCreation, &creationState);
env.taskScheduler().doEventLoop(&creationState.watchVariable);
ServerMediaSubsession* smss;
while ((smss = creationState.demux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
}
}
return sms;
}
live/mediaServer/DynamicRTSPServer.hh 000444 001752 001752 00000003443 13242237367 017535 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A subclass of "RTSPServer" that creates "ServerMediaSession"s on demand,
// based on whether or not the specified stream name exists as a file
// Header file
#ifndef _DYNAMIC_RTSP_SERVER_HH
#define _DYNAMIC_RTSP_SERVER_HH
#ifndef _RTSP_SERVER_SUPPORTING_HTTP_STREAMING_HH
#include "RTSPServerSupportingHTTPStreaming.hh"
#endif
class DynamicRTSPServer: public RTSPServerSupportingHTTPStreaming {
public:
static DynamicRTSPServer* createNew(UsageEnvironment& env, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationTestSeconds = 65);
protected:
DynamicRTSPServer(UsageEnvironment& env, int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds);
// called only by createNew();
virtual ~DynamicRTSPServer();
protected: // redefined virtual functions
virtual ServerMediaSession*
lookupServerMediaSession(char const* streamName, Boolean isFirstLookupInSession);
};
#endif
live/mediaServer/live555MediaServer.cpp 000444 001752 001752 00000010464 13242237367 017762 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// LIVE555 Media Server
// main program
#include
#include "DynamicRTSPServer.hh"
#include "version.hh"
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
// To implement client access control to the RTSP server, do the following:
authDB = new UserAuthenticationDatabase;
authDB->addUserRecord("username1", "password1"); // replace these with real strings
// Repeat the above with each , that you wish to allow
// access to the server.
#endif
// Create the RTSP server. Try first with the default port number (554),
// and then with the alternative port number (8554):
RTSPServer* rtspServer;
portNumBits rtspServerPortNum = 554;
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
if (rtspServer == NULL) {
rtspServerPortNum = 8554;
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
}
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
*env << "LIVE555 Media Server\n";
*env << "\tversion " << MEDIA_SERVER_VERSION_STRING
<< " (LIVE555 Streaming Media library version "
<< LIVEMEDIA_LIBRARY_VERSION_STRING << ").\n";
char* urlPrefix = rtspServer->rtspURLPrefix();
*env << "Play streams from this server using the URL\n\t"
<< urlPrefix << "\nwhere is a file present in the current directory.\n";
*env << "Each file's type is inferred from its name suffix:\n";
*env << "\t\".264\" => a H.264 Video Elementary Stream file\n";
*env << "\t\".265\" => a H.265 Video Elementary Stream file\n";
*env << "\t\".aac\" => an AAC Audio (ADTS format) file\n";
*env << "\t\".ac3\" => an AC-3 Audio file\n";
*env << "\t\".amr\" => an AMR Audio file\n";
*env << "\t\".dv\" => a DV Video file\n";
*env << "\t\".m4e\" => a MPEG-4 Video Elementary Stream file\n";
*env << "\t\".mkv\" => a Matroska audio+video+(optional)subtitles file\n";
*env << "\t\".mp3\" => a MPEG-1 or 2 Audio file\n";
*env << "\t\".mpg\" => a MPEG-1 or 2 Program Stream (audio+video) file\n";
*env << "\t\".ogg\" or \".ogv\" or \".opus\" => an Ogg audio and/or video file\n";
*env << "\t\".ts\" => a MPEG Transport Stream file\n";
*env << "\t\t(a \".tsx\" index file - if present - provides server 'trick play' support)\n";
*env << "\t\".vob\" => a VOB (MPEG-2 video with AC-3 audio) file\n";
*env << "\t\".wav\" => a WAV Audio file\n";
*env << "\t\".webm\" => a WebM audio(Vorbis)+video(VP8) file\n";
*env << "See http://www.live555.com/mediaServer/ for additional documentation.\n";
// Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
// Try first with the default HTTP port (80), and then with the alternative HTTP
// port numbers (8000 and 8080).
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
*env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n";
} else {
*env << "(RTSP-over-HTTP tunneling is not available.)\n";
}
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
live/mediaServer/Makefile.head 000440 001752 001752 00000000727 13242237367 016266 0 ustar 00rsf rsf 000000 000000 INCLUDES = -I../UsageEnvironment/include -I../groupsock/include -I../liveMedia/include -I../BasicUsageEnvironment/include
# Default library filename suffixes for each library that we link with. The "config.*" file might redefine these later.
libliveMedia_LIB_SUFFIX = $(LIB_SUFFIX)
libBasicUsageEnvironment_LIB_SUFFIX = $(LIB_SUFFIX)
libUsageEnvironment_LIB_SUFFIX = $(LIB_SUFFIX)
libgroupsock_LIB_SUFFIX = $(LIB_SUFFIX)
##### Change the following for your environment:
live/mediaServer/Makefile.tail 000444 001752 001752 00000002703 13242237367 016316 0 ustar 00rsf rsf 000000 000000 ##### End of variables to change
MEDIA_SERVER = live555MediaServer$(EXE)
PREFIX = /usr/local
ALL = $(MEDIA_SERVER)
all: $(ALL)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
MEDIA_SERVER_OBJS = live555MediaServer.$(OBJ) DynamicRTSPServer.$(OBJ)
live555MediaServer.$(CPP): DynamicRTSPServer.hh version.hh
DynamicRTSPServer.$(CPP): DynamicRTSPServer.hh
USAGE_ENVIRONMENT_DIR = ../UsageEnvironment
USAGE_ENVIRONMENT_LIB = $(USAGE_ENVIRONMENT_DIR)/libUsageEnvironment.$(libUsageEnvironment_LIB_SUFFIX)
BASIC_USAGE_ENVIRONMENT_DIR = ../BasicUsageEnvironment
BASIC_USAGE_ENVIRONMENT_LIB = $(BASIC_USAGE_ENVIRONMENT_DIR)/libBasicUsageEnvironment.$(libBasicUsageEnvironment_LIB_SUFFIX)
LIVEMEDIA_DIR = ../liveMedia
LIVEMEDIA_LIB = $(LIVEMEDIA_DIR)/libliveMedia.$(libliveMedia_LIB_SUFFIX)
GROUPSOCK_DIR = ../groupsock
GROUPSOCK_LIB = $(GROUPSOCK_DIR)/libgroupsock.$(libgroupsock_LIB_SUFFIX)
LOCAL_LIBS = $(LIVEMEDIA_LIB) $(GROUPSOCK_LIB) \
$(BASIC_USAGE_ENVIRONMENT_LIB) $(USAGE_ENVIRONMENT_LIB)
LIBS = $(LOCAL_LIBS) $(LIBS_FOR_CONSOLE_APPLICATION)
live555MediaServer$(EXE): $(MEDIA_SERVER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MEDIA_SERVER_OBJS) $(LIBS)
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
install: $(MEDIA_SERVER)
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $(MEDIA_SERVER) $(DESTDIR)$(PREFIX)/bin
##### Any additional, platform-specific rules come here:
live/mediaServer/version.hh 000444 001752 001752 00000000413 13242237367 015730 0 ustar 00rsf rsf 000000 000000 // Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// Version information for the LIVE555 Media Server application
// Header file
#ifndef _MEDIA_SERVER_VERSION_HH
#define _MEDIA_SERVER_VERSION_HH
#define MEDIA_SERVER_VERSION_STRING "0.91"
#endif
live/mediaServer/COPYING.LESSER 000755 001752 001752 00000000000 13242237367 020210 2../COPYING.LESSER ustar 00rsf rsf 000000 000000 live/testProgs/COPYING 000755 001752 001752 00000000000 13242237367 015744 2../COPYING ustar 00rsf rsf 000000 000000 live/testProgs/Makefile.head 000440 001752 001752 00000000727 13242237367 016012 0 ustar 00rsf rsf 000000 000000 INCLUDES = -I../UsageEnvironment/include -I../groupsock/include -I../liveMedia/include -I../BasicUsageEnvironment/include
# Default library filename suffixes for each library that we link with. The "config.*" file might redefine these later.
libliveMedia_LIB_SUFFIX = $(LIB_SUFFIX)
libBasicUsageEnvironment_LIB_SUFFIX = $(LIB_SUFFIX)
libUsageEnvironment_LIB_SUFFIX = $(LIB_SUFFIX)
libgroupsock_LIB_SUFFIX = $(LIB_SUFFIX)
##### Change the following for your environment:
live/testProgs/Makefile.tail 000444 001752 001752 00000020340 13242237367 016037 0 ustar 00rsf rsf 000000 000000 ##### End of variables to change
MULTICAST_STREAMER_APPS = testMP3Streamer$(EXE) testMPEG1or2VideoStreamer$(EXE) testMPEG1or2AudioVideoStreamer$(EXE) testMPEG2TransportStreamer$(EXE) testMPEG4VideoStreamer$(EXE) testH264VideoStreamer$(EXE) testH265VideoStreamer$(EXE) testDVVideoStreamer$(EXE) testWAVAudioStreamer$(EXE) testAMRAudioStreamer$(EXE) testMKVStreamer$(EXE) testOggStreamer$(EXE) vobStreamer$(EXE)
MULTICAST_RECEIVER_APPS = testMP3Receiver$(EXE) testMPEG1or2VideoReceiver$(EXE) testMPEG2TransportReceiver$(EXE) sapWatch$(EXE)
MULTICAST_MISC_APPS = testRelay$(EXE) testReplicator$(EXE)
MULTICAST_APPS = $(MULTICAST_STREAMER_APPS) $(MULTICAST_RECEIVER_APPS) $(MULTICAST_MISC_APPS)
UNICAST_STREAMER_APPS = testOnDemandRTSPServer$(EXE)
UNICAST_RECEIVER_APPS = testRTSPClient$(EXE) openRTSP$(EXE) playSIP$(EXE)
UNICAST_APPS = $(UNICAST_STREAMER_APPS) $(UNICAST_RECEIVER_APPS)
MISC_APPS = testMPEG1or2Splitter$(EXE) testMPEG1or2ProgramToTransportStream$(EXE) testH264VideoToTransportStream$(EXE) testH265VideoToTransportStream$(EXE) MPEG2TransportStreamIndexer$(EXE) testMPEG2TransportStreamTrickPlay$(EXE) registerRTSPStream$(EXE)
PREFIX = /usr/local
ALL = $(MULTICAST_APPS) $(UNICAST_APPS) $(MISC_APPS)
all: $(ALL)
extra: testGSMStreamer$(EXE)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
MP3_STREAMER_OBJS = testMP3Streamer.$(OBJ)
MP3_RECEIVER_OBJS = testMP3Receiver.$(OBJ)
RELAY_OBJS = testRelay.$(OBJ)
REPLICATOR_OBJS = testReplicator.$(OBJ)
MPEG_1OR2_SPLITTER_OBJS = testMPEG1or2Splitter.$(OBJ)
MPEG_1OR2_VIDEO_STREAMER_OBJS = testMPEG1or2VideoStreamer.$(OBJ)
MPEG_1OR2_VIDEO_RECEIVER_OBJS = testMPEG1or2VideoReceiver.$(OBJ)
MPEG2_TRANSPORT_RECEIVER_OBJS = testMPEG2TransportReceiver.$(OBJ)
MPEG_1OR2_AUDIO_VIDEO_STREAMER_OBJS = testMPEG1or2AudioVideoStreamer.$(OBJ)
MPEG2_TRANSPORT_STREAMER_OBJS = testMPEG2TransportStreamer.$(OBJ)
MPEG4_VIDEO_STREAMER_OBJS = testMPEG4VideoStreamer.$(OBJ)
H264_VIDEO_STREAMER_OBJS = testH264VideoStreamer.$(OBJ)
H265_VIDEO_STREAMER_OBJS = testH265VideoStreamer.$(OBJ)
DV_VIDEO_STREAMER_OBJS = testDVVideoStreamer.$(OBJ)
WAV_AUDIO_STREAMER_OBJS = testWAVAudioStreamer.$(OBJ)
AMR_AUDIO_STREAMER_OBJS = testAMRAudioStreamer.$(OBJ)
ON_DEMAND_RTSP_SERVER_OBJS = testOnDemandRTSPServer.$(OBJ)
MKV_STREAMER_OBJS = testMKVStreamer.$(OBJ)
OGG_STREAMER_OBJS = testOggStreamer.$(OBJ)
VOB_STREAMER_OBJS = vobStreamer.$(OBJ)
TEST_RTSP_CLIENT_OBJS = testRTSPClient.$(OBJ)
OPEN_RTSP_OBJS = openRTSP.$(OBJ) playCommon.$(OBJ)
PLAY_SIP_OBJS = playSIP.$(OBJ) playCommon.$(OBJ)
SAP_WATCH_OBJS = sapWatch.$(OBJ)
MPEG_1OR2_PROGRAM_TO_TRANSPORT_STREAM_OBJS = testMPEG1or2ProgramToTransportStream.$(OBJ)
H264_VIDEO_TO_TRANSPORT_STREAM_OBJS = testH264VideoToTransportStream.$(OBJ)
H265_VIDEO_TO_TRANSPORT_STREAM_OBJS = testH265VideoToTransportStream.$(OBJ)
MPEG2_TRANSPORT_STREAM_INDEXER_OBJS = MPEG2TransportStreamIndexer.$(OBJ)
MPEG2_TRANSPORT_STREAM_TRICK_PLAY_OBJS = testMPEG2TransportStreamTrickPlay.$(OBJ)
REGISTER_RTSP_STREAM_OBJS = registerRTSPStream.$(OBJ)
GSM_STREAMER_OBJS = testGSMStreamer.$(OBJ) testGSMEncoder.$(OBJ)
openRTSP.$(CPP): playCommon.hh
playCommon.$(CPP): playCommon.hh
playSIP.$(CPP): playCommon.hh
USAGE_ENVIRONMENT_DIR = ../UsageEnvironment
USAGE_ENVIRONMENT_LIB = $(USAGE_ENVIRONMENT_DIR)/libUsageEnvironment.$(libUsageEnvironment_LIB_SUFFIX)
BASIC_USAGE_ENVIRONMENT_DIR = ../BasicUsageEnvironment
BASIC_USAGE_ENVIRONMENT_LIB = $(BASIC_USAGE_ENVIRONMENT_DIR)/libBasicUsageEnvironment.$(libBasicUsageEnvironment_LIB_SUFFIX)
LIVEMEDIA_DIR = ../liveMedia
LIVEMEDIA_LIB = $(LIVEMEDIA_DIR)/libliveMedia.$(libliveMedia_LIB_SUFFIX)
GROUPSOCK_DIR = ../groupsock
GROUPSOCK_LIB = $(GROUPSOCK_DIR)/libgroupsock.$(libgroupsock_LIB_SUFFIX)
LOCAL_LIBS = $(LIVEMEDIA_LIB) $(GROUPSOCK_LIB) \
$(BASIC_USAGE_ENVIRONMENT_LIB) $(USAGE_ENVIRONMENT_LIB)
LIBS = $(LOCAL_LIBS) $(LIBS_FOR_CONSOLE_APPLICATION)
testMP3Streamer$(EXE): $(MP3_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MP3_STREAMER_OBJS) $(LIBS)
testMP3Receiver$(EXE): $(MP3_RECEIVER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MP3_RECEIVER_OBJS) $(LIBS)
testRelay$(EXE): $(RELAY_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(RELAY_OBJS) $(LIBS)
testReplicator$(EXE): $(REPLICATOR_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(REPLICATOR_OBJS) $(LIBS)
testMPEG1or2Splitter$(EXE): $(MPEG_1OR2_SPLITTER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG_1OR2_SPLITTER_OBJS) $(LIBS)
testMPEG1or2VideoStreamer$(EXE): $(MPEG_1OR2_VIDEO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG_1OR2_VIDEO_STREAMER_OBJS) $(LIBS)
testMPEG1or2VideoReceiver$(EXE): $(MPEG_1OR2_VIDEO_RECEIVER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG_1OR2_VIDEO_RECEIVER_OBJS) $(LIBS)
testMPEG1or2AudioVideoStreamer$(EXE): $(MPEG_1OR2_AUDIO_VIDEO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG_1OR2_AUDIO_VIDEO_STREAMER_OBJS) $(LIBS)
testMPEG2TransportStreamer$(EXE): $(MPEG2_TRANSPORT_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG2_TRANSPORT_STREAMER_OBJS) $(LIBS)
testMPEG2TransportReceiver$(EXE): $(MPEG2_TRANSPORT_RECEIVER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG2_TRANSPORT_RECEIVER_OBJS) $(LIBS)
testMPEG4VideoStreamer$(EXE): $(MPEG4_VIDEO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG4_VIDEO_STREAMER_OBJS) $(LIBS)
testH264VideoStreamer$(EXE): $(H264_VIDEO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(H264_VIDEO_STREAMER_OBJS) $(LIBS)
testH265VideoStreamer$(EXE): $(H265_VIDEO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(H265_VIDEO_STREAMER_OBJS) $(LIBS)
testDVVideoStreamer$(EXE): $(DV_VIDEO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(DV_VIDEO_STREAMER_OBJS) $(LIBS)
testWAVAudioStreamer$(EXE): $(WAV_AUDIO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(WAV_AUDIO_STREAMER_OBJS) $(LIBS)
testAMRAudioStreamer$(EXE): $(AMR_AUDIO_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(AMR_AUDIO_STREAMER_OBJS) $(LIBS)
testOnDemandRTSPServer$(EXE): $(ON_DEMAND_RTSP_SERVER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(ON_DEMAND_RTSP_SERVER_OBJS) $(LIBS)
testMKVStreamer$(EXE): $(MKV_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MKV_STREAMER_OBJS) $(LIBS)
testOggStreamer$(EXE): $(OGG_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(OGG_STREAMER_OBJS) $(LIBS)
vobStreamer$(EXE): $(VOB_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(VOB_STREAMER_OBJS) $(LIBS)
testRTSPClient$(EXE): $(TEST_RTSP_CLIENT_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(TEST_RTSP_CLIENT_OBJS) $(LIBS)
openRTSP$(EXE): $(OPEN_RTSP_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(OPEN_RTSP_OBJS) $(LIBS)
playSIP$(EXE): $(PLAY_SIP_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(PLAY_SIP_OBJS) $(LIBS)
sapWatch$(EXE): $(SAP_WATCH_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(SAP_WATCH_OBJS) $(LIBS)
testMPEG1or2ProgramToTransportStream$(EXE): $(MPEG_1OR2_PROGRAM_TO_TRANSPORT_STREAM_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG_1OR2_PROGRAM_TO_TRANSPORT_STREAM_OBJS) $(LIBS)
testH264VideoToTransportStream$(EXE): $(H264_VIDEO_TO_TRANSPORT_STREAM_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(H264_VIDEO_TO_TRANSPORT_STREAM_OBJS) $(LIBS)
testH265VideoToTransportStream$(EXE): $(H265_VIDEO_TO_TRANSPORT_STREAM_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(H265_VIDEO_TO_TRANSPORT_STREAM_OBJS) $(LIBS)
MPEG2TransportStreamIndexer$(EXE): $(MPEG2_TRANSPORT_STREAM_INDEXER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG2_TRANSPORT_STREAM_INDEXER_OBJS) $(LIBS)
testMPEG2TransportStreamTrickPlay$(EXE): $(MPEG2_TRANSPORT_STREAM_TRICK_PLAY_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG2_TRANSPORT_STREAM_TRICK_PLAY_OBJS) $(LIBS)
registerRTSPStream$(EXE): $(REGISTER_RTSP_STREAM_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(REGISTER_RTSP_STREAM_OBJS) $(LIBS)
testGSMStreamer$(EXE): $(GSM_STREAMER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(GSM_STREAMER_OBJS) $(LIBS)
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
install: $(ALL)
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $(ALL) $(DESTDIR)$(PREFIX)/bin
##### Any additional, platform-specific rules come here:
live/testProgs/MPEG2TransportStreamIndexer.cpp 000400 001752 001752 00000006325 13242237367 021374 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A program that reads an existing MPEG-2 Transport Stream file,
// and generates a separate index file that can be used - by our RTSP server
// implementation - to support 'trick play' operations when streaming the
// Transport Stream file.
// main program
#include
#include
void afterPlaying(void* clientData); // forward
UsageEnvironment* env;
char const* programName;
void usage() {
*env << "usage: " << programName << " \n";
*env << "\twhere ends with \".ts\"\n";
exit(1);
}
int main(int argc, char const** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Parse the command line:
programName = argv[0];
if (argc != 2) usage();
char const* inputFileName = argv[1];
// Check whether the input file name ends with ".ts":
int len = strlen(inputFileName);
if (len < 4 || strcmp(&inputFileName[len-3], ".ts") != 0) {
*env << "ERROR: input file name \"" << inputFileName
<< "\" does not end with \".ts\"\n";
usage();
}
// Open the input file (as a 'byte stream file source'):
FramedSource* input
= ByteStreamFileSource::createNew(*env, inputFileName, TRANSPORT_PACKET_SIZE);
if (input == NULL) {
*env << "Failed to open input file \"" << inputFileName << "\" (does it exist?)\n";
exit(1);
}
// Create a filter that indexes the input Transport Stream data:
FramedSource* indexer
= MPEG2IFrameIndexFromTransportStream::createNew(*env, input);
// The output file name is the same as the input file name, except with suffix ".tsx":
char* outputFileName = new char[len+2]; // allow for trailing x\0
sprintf(outputFileName, "%sx", inputFileName);
// Open the output file (for writing), as a 'file sink':
MediaSink* output = FileSink::createNew(*env, outputFileName);
if (output == NULL) {
*env << "Failed to open output file \"" << outputFileName << "\"\n";
exit(1);
}
// Start playing, to generate the output index file:
*env << "Writing index file \"" << outputFileName << "\"...";
output->startPlaying(*indexer, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done\n";
exit(0);
}
live/testProgs/openRTSP.cpp 000444 001752 001752 00000006142 13242237367 015631 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A RTSP client application that opens a RTSP URL argument,
// and extracts and records the data from each incoming RTP stream.
//
// NOTE: If you want to develop your own RTSP client application (or embed RTSP client functionality into your own application),
// then we don't recommend using this code as a model, because it is too complex (with many options).
// Instead, we recommend using the "testRTSPClient" application code as a model.
#include "playCommon.hh"
RTSPClient* ourRTSPClient = NULL;
Medium* createClient(UsageEnvironment& env, char const* url, int verbosityLevel, char const* applicationName) {
extern portNumBits tunnelOverHTTPPortNum;
return ourRTSPClient = RTSPClient::createNew(env, url, verbosityLevel, applicationName, tunnelOverHTTPPortNum);
}
void assignClient(Medium* client) {
ourRTSPClient = (RTSPClient*)client;
}
void getOptions(RTSPClient::responseHandler* afterFunc) {
ourRTSPClient->sendOptionsCommand(afterFunc, ourAuthenticator);
}
void getSDPDescription(RTSPClient::responseHandler* afterFunc) {
ourRTSPClient->sendDescribeCommand(afterFunc, ourAuthenticator);
}
void setupSubsession(MediaSubsession* subsession, Boolean streamUsingTCP, Boolean forceMulticastOnUnspecified, RTSPClient::responseHandler* afterFunc) {
ourRTSPClient->sendSetupCommand(*subsession, afterFunc, False, streamUsingTCP, forceMulticastOnUnspecified, ourAuthenticator);
}
void startPlayingSession(MediaSession* session, double start, double end, float scale, RTSPClient::responseHandler* afterFunc) {
ourRTSPClient->sendPlayCommand(*session, afterFunc, start, end, scale, ourAuthenticator);
}
void startPlayingSession(MediaSession* session, char const* absStartTime, char const* absEndTime, float scale, RTSPClient::responseHandler* afterFunc) {
ourRTSPClient->sendPlayCommand(*session, afterFunc, absStartTime, absEndTime, scale, ourAuthenticator);
}
void tearDownSession(MediaSession* session, RTSPClient::responseHandler* afterFunc) {
ourRTSPClient->sendTeardownCommand(*session, afterFunc, ourAuthenticator);
}
void setUserAgentString(char const* userAgentString) {
ourRTSPClient->setUserAgentString(userAgentString);
}
Boolean allowProxyServers = False;
Boolean controlConnectionUsesTCP = True;
Boolean supportCodecSelection = False;
char const* clientProtocolName = "RTSP";
live/testProgs/playCommon.cpp 000444 001752 001752 00000151073 13242237367 016301 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A common framework, used for the "openRTSP" and "playSIP" applications
// Implementation
//
// NOTE: If you want to develop your own RTSP client application (or embed RTSP client functionality into your own application),
// then we don't recommend using this code as a model, because it is too complex (with many options).
// Instead, we recommend using the "testRTSPClient" application code as a model.
#include "playCommon.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
#if defined(__WIN32__) || defined(_WIN32)
#define snprintf _snprintf
#else
#include
#define USE_SIGNALS 1
#endif
// Forward function definitions:
void continueAfterClientCreation0(RTSPClient* client, Boolean requestStreamingOverTCP);
void continueAfterClientCreation1();
void continueAfterOPTIONS(RTSPClient* client, int resultCode, char* resultString);
void continueAfterDESCRIBE(RTSPClient* client, int resultCode, char* resultString);
void continueAfterSETUP(RTSPClient* client, int resultCode, char* resultString);
void continueAfterPLAY(RTSPClient* client, int resultCode, char* resultString);
void continueAfterTEARDOWN(RTSPClient* client, int resultCode, char* resultString);
void createOutputFiles(char const* periodicFilenameSuffix);
void createPeriodicOutputFiles();
void setupStreams();
void closeMediaSinks();
void subsessionAfterPlaying(void* clientData);
void subsessionByeHandler(void* clientData);
void sessionAfterPlaying(void* clientData = NULL);
void sessionTimerHandler(void* clientData);
void periodicFileOutputTimerHandler(void* clientData);
void shutdown(int exitCode = 1);
void signalHandlerShutdown(int sig);
void checkForPacketArrival(void* clientData);
void checkInterPacketGaps(void* clientData);
void checkSessionTimeoutBrokenServer(void* clientData);
void beginQOSMeasurement();
char const* progName;
UsageEnvironment* env;
Medium* ourClient = NULL;
Authenticator* ourAuthenticator = NULL;
char const* streamURL = NULL;
MediaSession* session = NULL;
TaskToken sessionTimerTask = NULL;
TaskToken sessionTimeoutBrokenServerTask = NULL;
TaskToken arrivalCheckTimerTask = NULL;
TaskToken interPacketGapCheckTimerTask = NULL;
TaskToken qosMeasurementTimerTask = NULL;
TaskToken periodicFileOutputTask = NULL;
Boolean createReceivers = True;
Boolean outputQuickTimeFile = False;
Boolean generateMP4Format = False;
QuickTimeFileSink* qtOut = NULL;
Boolean outputAVIFile = False;
AVIFileSink* aviOut = NULL;
Boolean audioOnly = False;
Boolean videoOnly = False;
char const* singleMedium = NULL;
int verbosityLevel = 1; // by default, print verbose output
double duration = 0;
double durationSlop = -1.0; // extra seconds to play at the end
double initialSeekTime = 0.0f;
char* initialAbsoluteSeekTime = NULL;
char* initialAbsoluteSeekEndTime = NULL;
float scale = 1.0f;
double endTime;
unsigned interPacketGapMaxTime = 0;
unsigned totNumPacketsReceived = ~0; // used if checking inter-packet gaps
Boolean playContinuously = False;
int simpleRTPoffsetArg = -1;
Boolean sendOptionsRequest = True;
Boolean sendOptionsRequestOnly = False;
Boolean oneFilePerFrame = False;
Boolean notifyOnPacketArrival = False;
Boolean sendKeepAlivesToBrokenServers = False;
unsigned sessionTimeoutParameter = 0;
Boolean streamUsingTCP = False;
Boolean forceMulticastOnUnspecified = False;
unsigned short desiredPortNum = 0;
portNumBits tunnelOverHTTPPortNum = 0;
char* username = NULL;
char* password = NULL;
char* proxyServerName = NULL;
unsigned short proxyServerPortNum = 0;
unsigned char desiredAudioRTPPayloadFormat = 0;
char* mimeSubtype = NULL;
unsigned short movieWidth = 240; // default
Boolean movieWidthOptionSet = False;
unsigned short movieHeight = 180; // default
Boolean movieHeightOptionSet = False;
unsigned movieFPS = 15; // default
Boolean movieFPSOptionSet = False;
char const* fileNamePrefix = "";
unsigned fileSinkBufferSize = 100000;
unsigned socketInputBufferSize = 0;
Boolean packetLossCompensate = False;
Boolean syncStreams = False;
Boolean generateHintTracks = False;
Boolean waitForResponseToTEARDOWN = True;
unsigned qosMeasurementIntervalMS = 0; // 0 means: Don't output QOS data
char* userAgent = NULL;
unsigned fileOutputInterval = 0; // seconds
unsigned fileOutputSecondsSoFar = 0; // seconds
Boolean createHandlerServerForREGISTERCommand = False;
portNumBits handlerServerForREGISTERCommandPortNum = 0;
HandlerServerForREGISTERCommand* handlerServerForREGISTERCommand;
char* usernameForREGISTER = NULL;
char* passwordForREGISTER = NULL;
UserAuthenticationDatabase* authDBForREGISTER = NULL;
struct timeval startTime;
void usage() {
*env << "Usage: " << progName
<< " [-p ] [-r|-q|-4|-i] [-a|-v] [-V] [-d ] [-D [-c] [-S ] [-n] [-O]"
<< (controlConnectionUsesTCP ? " [-t|-T ]" : "")
<< " [-u "
<< (allowProxyServers ? " [ []]" : "")
<< "]" << (supportCodecSelection ? " [-A |-M ]" : "")
<< " [-s ]|[-U ] [-E ] [-z ] [-g user-agent]"
<< " [-k ]"
<< " [-P ] [-K]"
<< " [-w -h ] [-f ] [-y] [-H] [-Q []] [-F ] [-b ] [-B ] [-I ] [-m] [|-R []] (or " << progName << " -o [-V] )\n";
shutdown();
}
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
progName = argv[0];
gettimeofday(&startTime, NULL);
#ifdef USE_SIGNALS
// Allow ourselves to be shut down gracefully by a SIGHUP or a SIGUSR1:
signal(SIGHUP, signalHandlerShutdown);
signal(SIGUSR1, signalHandlerShutdown);
#endif
// unfortunately we can't use getopt() here, as Windoze doesn't have it
while (argc > 1) {
char* const opt = argv[1];
if (opt[0] != '-') {
if (argc == 2) break; // only the URL is left
usage();
}
switch (opt[1]) {
case 'p': { // specify start port number
int portArg;
if (sscanf(argv[2], "%d", &portArg) != 1) {
usage();
}
if (portArg <= 0 || portArg >= 65536 || portArg&1) {
*env << "bad port number: " << portArg
<< " (must be even, and in the range (0,65536))\n";
usage();
}
desiredPortNum = (unsigned short)portArg;
++argv; --argc;
break;
}
case 'r': { // do not receive data (instead, just 'play' the stream(s))
createReceivers = False;
break;
}
case 'q': { // output a QuickTime file (to stdout)
outputQuickTimeFile = True;
break;
}
case '4': { // output a 'mp4'-format file (to stdout)
outputQuickTimeFile = True;
generateMP4Format = True;
break;
}
case 'i': { // output an AVI file (to stdout)
outputAVIFile = True;
break;
}
case 'I': { // specify input interface...
NetAddressList addresses(argv[2]);
if (addresses.numAddresses() == 0) {
*env << "Failed to find network address for \"" << argv[2] << "\"";
break;
}
ReceivingInterfaceAddr = *(unsigned*)(addresses.firstAddress()->data());
++argv; --argc;
break;
}
case 'a': { // receive/record an audio stream only
audioOnly = True;
singleMedium = "audio";
break;
}
case 'v': { // receive/record a video stream only
videoOnly = True;
singleMedium = "video";
break;
}
case 'V': { // disable verbose output
verbosityLevel = 0;
break;
}
case 'd': { // specify duration, or how much to delay after end time
float arg;
if (sscanf(argv[2], "%g", &arg) != 1) {
usage();
}
if (argv[2][0] == '-') { // not "arg<0", in case argv[2] was "-0"
// a 'negative' argument was specified; use this for "durationSlop":
duration = 0; // use whatever's in the SDP
durationSlop = -arg;
} else {
duration = arg;
durationSlop = 0;
}
++argv; --argc;
break;
}
case 'D': { // specify maximum number of seconds to wait for packets:
if (sscanf(argv[2], "%u", &interPacketGapMaxTime) != 1) {
usage();
}
++argv; --argc;
break;
}
case 'c': { // play continuously
playContinuously = True;
break;
}
case 'S': { // specify an offset to use with "SimpleRTPSource"s
if (sscanf(argv[2], "%d", &simpleRTPoffsetArg) != 1) {
usage();
}
if (simpleRTPoffsetArg < 0) {
*env << "offset argument to \"-S\" must be >= 0\n";
usage();
}
++argv; --argc;
break;
}
case 'm': { // output multiple files - one for each frame
oneFilePerFrame = True;
break;
}
case 'n': { // notify the user when the first data packet arrives
notifyOnPacketArrival = True;
break;
}
case 'O': { // Don't send an "OPTIONS" request before "DESCRIBE"
sendOptionsRequest = False;
break;
}
case 'o': { // Send only the "OPTIONS" request to the server
sendOptionsRequestOnly = True;
break;
}
case 'P': { // specify an interval (in seconds) between writing successive output files
int fileOutputIntervalInt;
if (sscanf(argv[2], "%d", &fileOutputIntervalInt) != 1 || fileOutputIntervalInt <= 0) {
usage();
}
fileOutputInterval = (unsigned)fileOutputIntervalInt;
++argv; --argc;
break;
}
case 't': {
// stream RTP and RTCP over the TCP 'control' connection
if (controlConnectionUsesTCP) {
streamUsingTCP = True;
} else {
usage();
}
break;
}
case 'T': {
// stream RTP and RTCP over a HTTP connection
if (controlConnectionUsesTCP) {
if (argc > 3 && argv[2][0] != '-') {
// The next argument is the HTTP server port number:
if (sscanf(argv[2], "%hu", &tunnelOverHTTPPortNum) == 1
&& tunnelOverHTTPPortNum > 0) {
++argv; --argc;
break;
}
}
}
// If we get here, the option was specified incorrectly:
usage();
break;
}
case 'u': { // specify a username and password
if (argc < 4) usage(); // there's no argv[3] (for the "password")
username = argv[2];
password = argv[3];
argv+=2; argc-=2;
if (allowProxyServers && argc > 3 && argv[2][0] != '-') {
// The next argument is the name of a proxy server:
proxyServerName = argv[2];
++argv; --argc;
if (argc > 3 && argv[2][0] != '-') {
// The next argument is the proxy server port number:
if (sscanf(argv[2], "%hu", &proxyServerPortNum) != 1) {
usage();
}
++argv; --argc;
}
}
ourAuthenticator = new Authenticator(username, password);
break;
}
case 'k': { // specify a username and password to be used to authentication an incoming "REGISTER" command (for use with -R)
if (argc < 4) usage(); // there's no argv[3] (for the "password")
usernameForREGISTER = argv[2];
passwordForREGISTER = argv[3];
argv+=2; argc-=2;
if (authDBForREGISTER == NULL) authDBForREGISTER = new UserAuthenticationDatabase;
authDBForREGISTER->addUserRecord(usernameForREGISTER, passwordForREGISTER);
break;
}
case 'K': { // Send periodic 'keep-alive' requests to keep broken server sessions alive
sendKeepAlivesToBrokenServers = True;
break;
}
case 'A': { // specify a desired audio RTP payload format
unsigned formatArg;
if (sscanf(argv[2], "%u", &formatArg) != 1
|| formatArg >= 96) {
usage();
}
desiredAudioRTPPayloadFormat = (unsigned char)formatArg;
++argv; --argc;
break;
}
case 'M': { // specify a MIME subtype for a dynamic RTP payload type
mimeSubtype = argv[2];
if (desiredAudioRTPPayloadFormat==0) desiredAudioRTPPayloadFormat =96;
++argv; --argc;
break;
}
case 'w': { // specify a width (pixels) for an output QuickTime or AVI movie
if (sscanf(argv[2], "%hu", &movieWidth) != 1) {
usage();
}
movieWidthOptionSet = True;
++argv; --argc;
break;
}
case 'h': { // specify a height (pixels) for an output QuickTime or AVI movie
if (sscanf(argv[2], "%hu", &movieHeight) != 1) {
usage();
}
movieHeightOptionSet = True;
++argv; --argc;
break;
}
case 'f': { // specify a frame rate (per second) for an output QT or AVI movie
if (sscanf(argv[2], "%u", &movieFPS) != 1) {
usage();
}
movieFPSOptionSet = True;
++argv; --argc;
break;
}
case 'F': { // specify a prefix for the audio and video output files
fileNamePrefix = argv[2];
++argv; --argc;
break;
}
case 'g': { // specify a user agent name to use in outgoing requests
userAgent = argv[2];
++argv; --argc;
break;
}
case 'b': { // specify the size of buffers for "FileSink"s
if (sscanf(argv[2], "%u", &fileSinkBufferSize) != 1) {
usage();
}
++argv; --argc;
break;
}
case 'B': { // specify the size of input socket buffers
if (sscanf(argv[2], "%u", &socketInputBufferSize) != 1) {
usage();
}
++argv; --argc;
break;
}
// Note: The following option is deprecated, and may someday be removed:
case 'l': { // try to compensate for packet loss by repeating frames
packetLossCompensate = True;
break;
}
case 'y': { // synchronize audio and video streams
syncStreams = True;
break;
}
case 'H': { // generate hint tracks (as well as the regular data tracks)
generateHintTracks = True;
break;
}
case 'Q': { // output QOS measurements
qosMeasurementIntervalMS = 1000; // default: 1 second
if (argc > 3 && argv[2][0] != '-') {
// The next argument is the measurement interval,
// in multiples of 100 ms
if (sscanf(argv[2], "%u", &qosMeasurementIntervalMS) != 1) {
usage();
}
qosMeasurementIntervalMS *= 100;
++argv; --argc;
}
break;
}
case 's': { // specify initial seek time (trick play)
double arg;
if (sscanf(argv[2], "%lg", &arg) != 1 || arg < 0) {
usage();
}
initialSeekTime = arg;
++argv; --argc;
break;
}
case 'U': {
// specify initial absolute seek time (trick play), using a string of the form "YYYYMMDDTHHMMSSZ" or "YYYYMMDDTHHMMSS.Z"
initialAbsoluteSeekTime = argv[2];
++argv; --argc;
break;
}
case 'E': {
// specify initial absolute seek END time (trick play), using a string of the form "YYYYMMDDTHHMMSSZ" or "YYYYMMDDTHHMMSS.Z"
initialAbsoluteSeekEndTime = argv[2];
++argv; --argc;
break;
}
case 'z': { // scale (trick play)
float arg;
if (sscanf(argv[2], "%g", &arg) != 1 || arg == 0.0f) {
usage();
}
scale = arg;
++argv; --argc;
break;
}
case 'R': {
// set up a handler server for incoming "REGISTER" commands
createHandlerServerForREGISTERCommand = True;
if (argc > 2 && argv[2][0] != '-') {
// The next argument is the REGISTER handler server port number:
if (sscanf(argv[2], "%hu", &handlerServerForREGISTERCommandPortNum) == 1 && handlerServerForREGISTERCommandPortNum > 0) {
++argv; --argc;
break;
}
}
break;
}
case 'C': {
forceMulticastOnUnspecified = True;
break;
}
default: {
*env << "Invalid option: " << opt << "\n";
usage();
break;
}
}
++argv; --argc;
}
// There must be exactly one "rtsp://" URL at the end (unless '-R' was used, in which case there's no URL)
if (!( (argc == 2 && !createHandlerServerForREGISTERCommand) || (argc == 1 && createHandlerServerForREGISTERCommand) )) usage();
if (outputQuickTimeFile && outputAVIFile) {
*env << "The -i and -q (or -4) options cannot both be used!\n";
usage();
}
Boolean outputCompositeFile = outputQuickTimeFile || outputAVIFile;
if (!createReceivers && (outputCompositeFile || oneFilePerFrame || fileOutputInterval > 0)) {
*env << "The -r option cannot be used with -q, -4, -i, -m, or -P!\n";
usage();
}
if (oneFilePerFrame && fileOutputInterval > 0) {
*env << "The -m and -P options cannot both be used!\n";
usage();
}
if (outputCompositeFile && !movieWidthOptionSet) {
*env << "Warning: The -q, -4 or -i option was used, but not -w. Assuming a video width of "
<< movieWidth << " pixels\n";
}
if (outputCompositeFile && !movieHeightOptionSet) {
*env << "Warning: The -q, -4 or -i option was used, but not -h. Assuming a video height of "
<< movieHeight << " pixels\n";
}
if (outputCompositeFile && !movieFPSOptionSet) {
*env << "Warning: The -q, -4 or -i option was used, but not -f. Assuming a video frame rate of "
<< movieFPS << " frames-per-second\n";
}
if (audioOnly && videoOnly) {
*env << "The -a and -v options cannot both be used!\n";
usage();
}
if (sendOptionsRequestOnly && !sendOptionsRequest) {
*env << "The -o and -O options cannot both be used!\n";
usage();
}
if (initialAbsoluteSeekTime != NULL && initialSeekTime != 0.0f) {
*env << "The -s and -U options cannot both be used!\n";
usage();
}
if (initialAbsoluteSeekTime == NULL && initialAbsoluteSeekEndTime != NULL) {
*env << "The -E option requires the -U option!\n";
usage();
}
if (authDBForREGISTER != NULL && !createHandlerServerForREGISTERCommand) {
*env << "If \"-k \" is used, then -R (or \"-R \") must also be used!\n";
usage();
}
if (tunnelOverHTTPPortNum > 0) {
if (streamUsingTCP) {
*env << "The -t and -T options cannot both be used!\n";
usage();
} else {
streamUsingTCP = True;
}
}
if (!createReceivers && notifyOnPacketArrival) {
*env << "Warning: Because we're not receiving stream data, the -n flag has no effect\n";
}
if (durationSlop < 0) {
// This parameter wasn't set, so use a default value.
// If we're measuring QOS stats, then don't add any slop, to avoid
// having 'empty' measurement intervals at the end.
durationSlop = qosMeasurementIntervalMS > 0 ? 0.0 : 5.0;
}
streamURL = argv[1];
// Create (or arrange to create) our client object:
if (createHandlerServerForREGISTERCommand) {
handlerServerForREGISTERCommand
= HandlerServerForREGISTERCommand::createNew(*env, continueAfterClientCreation0,
handlerServerForREGISTERCommandPortNum, authDBForREGISTER,
verbosityLevel, progName);
if (handlerServerForREGISTERCommand == NULL) {
*env << "Failed to create a server for handling incoming \"REGISTER\" commands: " << env->getResultMsg() << "\n";
} else {
*env << "Awaiting an incoming \"REGISTER\" command on port " << handlerServerForREGISTERCommand->serverPortNum() << "\n";
}
} else {
ourClient = createClient(*env, streamURL, verbosityLevel, progName);
if (ourClient == NULL) {
*env << "Failed to create " << clientProtocolName << " client: " << env->getResultMsg() << "\n";
shutdown();
}
continueAfterClientCreation1();
}
// All subsequent activity takes place within the event loop:
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void continueAfterClientCreation0(RTSPClient* newRTSPClient, Boolean requestStreamingOverTCP) {
if (newRTSPClient == NULL) return;
streamUsingTCP = requestStreamingOverTCP;
assignClient(ourClient = newRTSPClient);
streamURL = newRTSPClient->url();
// Having handled one "REGISTER" command (giving us a "rtsp://" URL to stream from), we don't handle any more:
Medium::close(handlerServerForREGISTERCommand); handlerServerForREGISTERCommand = NULL;
continueAfterClientCreation1();
}
void continueAfterClientCreation1() {
setUserAgentString(userAgent);
if (sendOptionsRequest) {
// Begin by sending an "OPTIONS" command:
getOptions(continueAfterOPTIONS);
} else {
continueAfterOPTIONS(NULL, 0, NULL);
}
}
void continueAfterOPTIONS(RTSPClient*, int resultCode, char* resultString) {
if (sendOptionsRequestOnly) {
if (resultCode != 0) {
*env << clientProtocolName << " \"OPTIONS\" request failed: " << resultString << "\n";
} else {
*env << clientProtocolName << " \"OPTIONS\" request returned: " << resultString << "\n";
}
shutdown();
}
delete[] resultString;
// Next, get a SDP description for the stream:
getSDPDescription(continueAfterDESCRIBE);
}
void continueAfterDESCRIBE(RTSPClient*, int resultCode, char* resultString) {
if (resultCode != 0) {
*env << "Failed to get a SDP description for the URL \"" << streamURL << "\": " << resultString << "\n";
delete[] resultString;
shutdown();
}
char* sdpDescription = resultString;
*env << "Opened URL \"" << streamURL << "\", returning a SDP description:\n" << sdpDescription << "\n";
// Create a media session object from this SDP description:
session = MediaSession::createNew(*env, sdpDescription);
delete[] sdpDescription;
if (session == NULL) {
*env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";
shutdown();
} else if (!session->hasSubsessions()) {
*env << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
shutdown();
}
// Then, setup the "RTPSource"s for the session:
MediaSubsessionIterator iter(*session);
MediaSubsession *subsession;
Boolean madeProgress = False;
char const* singleMediumToTest = singleMedium;
while ((subsession = iter.next()) != NULL) {
// If we've asked to receive only a single medium, then check this now:
if (singleMediumToTest != NULL) {
if (strcmp(subsession->mediumName(), singleMediumToTest) != 0) {
*env << "Ignoring \"" << subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession, because we've asked to receive a single " << singleMedium
<< " session only\n";
continue;
} else {
// Receive this subsession only
singleMediumToTest = "xxxxx";
// this hack ensures that we get only 1 subsession of this type
}
}
if (desiredPortNum != 0) {
subsession->setClientPortNum(desiredPortNum);
desiredPortNum += 2;
}
if (createReceivers) {
if (!subsession->initiate(simpleRTPoffsetArg)) {
*env << "Unable to create receiver for \"" << subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession: " << env->getResultMsg() << "\n";
} else {
*env << "Created receiver for \"" << subsession->mediumName()
<< "/" << subsession->codecName() << "\" subsession (";
if (subsession->rtcpIsMuxed()) {
*env << "client port " << subsession->clientPortNum();
} else {
*env << "client ports " << subsession->clientPortNum()
<< "-" << subsession->clientPortNum()+1;
}
*env << ")\n";
madeProgress = True;
if (subsession->rtpSource() != NULL) {
// Because we're saving the incoming data, rather than playing
// it in real time, allow an especially large time threshold
// (1 second) for reordering misordered incoming packets:
unsigned const thresh = 1000000; // 1 second
subsession->rtpSource()->setPacketReorderingThresholdTime(thresh);
// Set the RTP source's OS socket buffer size as appropriate - either if we were explicitly asked (using -B),
// or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size.
// (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size,
// then the input data rate may be large enough to justify increasing the OS socket buffer size also.)
int socketNum = subsession->rtpSource()->RTPgs()->socketNum();
unsigned curBufferSize = getReceiveBufferSize(*env, socketNum);
if (socketInputBufferSize > 0 || fileSinkBufferSize > curBufferSize) {
unsigned newBufferSize = socketInputBufferSize > 0 ? socketInputBufferSize : fileSinkBufferSize;
newBufferSize = setReceiveBufferTo(*env, socketNum, newBufferSize);
if (socketInputBufferSize > 0) { // The user explicitly asked for the new socket buffer size; announce it:
*env << "Changed socket receive buffer size for the \""
<< subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession from "
<< curBufferSize << " to "
<< newBufferSize << " bytes\n";
}
}
}
}
} else {
if (subsession->clientPortNum() == 0) {
*env << "No client port was specified for the \""
<< subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession. (Try adding the \"-p \" option.)\n";
} else {
madeProgress = True;
}
}
}
if (!madeProgress) shutdown();
// Perform additional 'setup' on each subsession, before playing them:
setupStreams();
}
MediaSubsession *subsession;
Boolean madeProgress = False;
void continueAfterSETUP(RTSPClient* client, int resultCode, char* resultString) {
if (resultCode == 0) {
*env << "Setup \"" << subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession (";
if (subsession->rtcpIsMuxed()) {
*env << "client port " << subsession->clientPortNum();
} else {
*env << "client ports " << subsession->clientPortNum()
<< "-" << subsession->clientPortNum()+1;
}
*env << ")\n";
madeProgress = True;
} else {
*env << "Failed to setup \"" << subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession: " << resultString << "\n";
}
delete[] resultString;
if (client != NULL) sessionTimeoutParameter = client->sessionTimeoutParameter();
// Set up the next subsession, if any:
setupStreams();
}
void createOutputFiles(char const* periodicFilenameSuffix) {
char outFileName[1000];
if (outputQuickTimeFile || outputAVIFile) {
if (periodicFilenameSuffix[0] == '\0') {
// Normally (unless the '-P ' option was given) we output to 'stdout':
sprintf(outFileName, "stdout");
} else {
// Otherwise output to a type-specific file name, containing "periodicFilenameSuffix":
char const* prefix = fileNamePrefix[0] == '\0' ? "output" : fileNamePrefix;
snprintf(outFileName, sizeof outFileName, "%s%s.%s", prefix, periodicFilenameSuffix,
outputAVIFile ? "avi" : generateMP4Format ? "mp4" : "mov");
}
if (outputQuickTimeFile) {
qtOut = QuickTimeFileSink::createNew(*env, *session, outFileName,
fileSinkBufferSize,
movieWidth, movieHeight,
movieFPS,
packetLossCompensate,
syncStreams,
generateHintTracks,
generateMP4Format);
if (qtOut == NULL) {
*env << "Failed to create a \"QuickTimeFileSink\" for outputting to \""
<< outFileName << "\": " << env->getResultMsg() << "\n";
shutdown();
} else {
*env << "Outputting to the file: \"" << outFileName << "\"\n";
}
qtOut->startPlaying(sessionAfterPlaying, NULL);
} else { // outputAVIFile
aviOut = AVIFileSink::createNew(*env, *session, outFileName,
fileSinkBufferSize,
movieWidth, movieHeight,
movieFPS,
packetLossCompensate);
if (aviOut == NULL) {
*env << "Failed to create an \"AVIFileSink\" for outputting to \""
<< outFileName << "\": " << env->getResultMsg() << "\n";
shutdown();
} else {
*env << "Outputting to the file: \"" << outFileName << "\"\n";
}
aviOut->startPlaying(sessionAfterPlaying, NULL);
}
} else {
// Create and start "FileSink"s for each subsession:
madeProgress = False;
MediaSubsessionIterator iter(*session);
while ((subsession = iter.next()) != NULL) {
if (subsession->readSource() == NULL) continue; // was not initiated
// Create an output file for each desired stream:
if (singleMedium == NULL || periodicFilenameSuffix[0] != '\0') {
// Output file name is
// "--"
static unsigned streamCounter = 0;
snprintf(outFileName, sizeof outFileName, "%s%s-%s-%d%s",
fileNamePrefix, subsession->mediumName(),
subsession->codecName(), ++streamCounter, periodicFilenameSuffix);
} else {
// When outputting a single medium only, we output to 'stdout
// (unless the '-P ' option was given):
sprintf(outFileName, "stdout");
}
FileSink* fileSink = NULL;
Boolean createOggFileSink = False; // by default
if (strcmp(subsession->mediumName(), "video") == 0) {
if (strcmp(subsession->codecName(), "H264") == 0) {
// For H.264 video stream, we use a special sink that adds 'start codes',
// and (at the start) the SPS and PPS NAL units:
fileSink = H264VideoFileSink::createNew(*env, outFileName,
subsession->fmtp_spropparametersets(),
fileSinkBufferSize, oneFilePerFrame);
} else if (strcmp(subsession->codecName(), "H265") == 0) {
// For H.265 video stream, we use a special sink that adds 'start codes',
// and (at the start) the VPS, SPS, and PPS NAL units:
fileSink = H265VideoFileSink::createNew(*env, outFileName,
subsession->fmtp_spropvps(),
subsession->fmtp_spropsps(),
subsession->fmtp_sproppps(),
fileSinkBufferSize, oneFilePerFrame);
} else if (strcmp(subsession->codecName(), "THEORA") == 0) {
createOggFileSink = True;
}
} else if (strcmp(subsession->mediumName(), "audio") == 0) {
if (strcmp(subsession->codecName(), "AMR") == 0 ||
strcmp(subsession->codecName(), "AMR-WB") == 0) {
// For AMR audio streams, we use a special sink that inserts AMR frame hdrs:
fileSink = AMRAudioFileSink::createNew(*env, outFileName,
fileSinkBufferSize, oneFilePerFrame);
} else if (strcmp(subsession->codecName(), "VORBIS") == 0 ||
strcmp(subsession->codecName(), "OPUS") == 0) {
createOggFileSink = True;
}
}
if (createOggFileSink) {
fileSink = OggFileSink
::createNew(*env, outFileName,
subsession->rtpTimestampFrequency(), subsession->fmtp_config());
} else if (fileSink == NULL) {
// Normal case:
fileSink = FileSink::createNew(*env, outFileName,
fileSinkBufferSize, oneFilePerFrame);
}
subsession->sink = fileSink;
if (subsession->sink == NULL) {
*env << "Failed to create FileSink for \"" << outFileName
<< "\": " << env->getResultMsg() << "\n";
} else {
if (singleMedium == NULL) {
*env << "Created output file: \"" << outFileName << "\"\n";
} else {
*env << "Outputting data from the \"" << subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession to \"" << outFileName << "\"\n";
}
if (strcmp(subsession->mediumName(), "video") == 0 &&
strcmp(subsession->codecName(), "MP4V-ES") == 0 &&
subsession->fmtp_config() != NULL) {
// For MPEG-4 video RTP streams, the 'config' information
// from the SDP description contains useful VOL etc. headers.
// Insert this data at the front of the output file:
unsigned configLen;
unsigned char* configData
= parseGeneralConfigStr(subsession->fmtp_config(), configLen);
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
fileSink->addData(configData, configLen, timeNow);
delete[] configData;
}
subsession->sink->startPlaying(*(subsession->readSource()),
subsessionAfterPlaying,
subsession);
// Also set a handler to be called if a RTCP "BYE" arrives
// for this subsession:
if (subsession->rtcpInstance() != NULL) {
subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, subsession);
}
madeProgress = True;
}
}
if (!madeProgress) shutdown();
}
}
void createPeriodicOutputFiles() {
// Create a filename suffix that notes the time interval that's being recorded:
char periodicFileNameSuffix[100];
snprintf(periodicFileNameSuffix, sizeof periodicFileNameSuffix, "-%05d-%05d",
fileOutputSecondsSoFar, fileOutputSecondsSoFar + fileOutputInterval);
createOutputFiles(periodicFileNameSuffix);
// Schedule an event for writing the next output file:
periodicFileOutputTask
= env->taskScheduler().scheduleDelayedTask(fileOutputInterval*1000000,
(TaskFunc*)periodicFileOutputTimerHandler,
(void*)NULL);
}
void setupStreams() {
static MediaSubsessionIterator* setupIter = NULL;
if (setupIter == NULL) setupIter = new MediaSubsessionIterator(*session);
while ((subsession = setupIter->next()) != NULL) {
// We have another subsession left to set up:
if (subsession->clientPortNum() == 0) continue; // port # was not set
setupSubsession(subsession, streamUsingTCP, forceMulticastOnUnspecified, continueAfterSETUP);
return;
}
// We're done setting up subsessions.
delete setupIter;
if (!madeProgress) shutdown();
// Create output files:
if (createReceivers) {
if (fileOutputInterval > 0) {
createPeriodicOutputFiles();
} else {
createOutputFiles("");
}
}
// Finally, start playing each subsession, to start the data flow:
if (duration == 0) {
if (scale > 0) duration = session->playEndTime() - initialSeekTime; // use SDP end time
else if (scale < 0) duration = initialSeekTime;
}
if (duration < 0) duration = 0.0;
endTime = initialSeekTime;
if (scale > 0) {
if (duration <= 0) endTime = -1.0f;
else endTime = initialSeekTime + duration;
} else {
endTime = initialSeekTime - duration;
if (endTime < 0) endTime = 0.0f;
}
char const* absStartTime = initialAbsoluteSeekTime != NULL ? initialAbsoluteSeekTime : session->absStartTime();
char const* absEndTime = initialAbsoluteSeekEndTime != NULL ? initialAbsoluteSeekEndTime : session->absEndTime();
if (absStartTime != NULL) {
// Either we or the server have specified that seeking should be done by 'absolute' time:
startPlayingSession(session, absStartTime, absEndTime, scale, continueAfterPLAY);
} else {
// Normal case: Seek by relative time (NPT):
startPlayingSession(session, initialSeekTime, endTime, scale, continueAfterPLAY);
}
}
void continueAfterPLAY(RTSPClient*, int resultCode, char* resultString) {
if (resultCode != 0) {
*env << "Failed to start playing session: " << resultString << "\n";
delete[] resultString;
shutdown();
return;
} else {
*env << "Started playing session\n";
}
delete[] resultString;
if (qosMeasurementIntervalMS > 0) {
// Begin periodic QOS measurements:
beginQOSMeasurement();
}
// Figure out how long to delay (if at all) before shutting down, or
// repeating the playing
Boolean timerIsBeingUsed = False;
double secondsToDelay = duration;
if (duration > 0) {
// First, adjust "duration" based on any change to the play range (that was specified in the "PLAY" response):
double rangeAdjustment = (session->playEndTime() - session->playStartTime()) - (endTime - initialSeekTime);
if (duration + rangeAdjustment > 0.0) duration += rangeAdjustment;
timerIsBeingUsed = True;
double absScale = scale > 0 ? scale : -scale; // ASSERT: scale != 0
secondsToDelay = duration/absScale + durationSlop;
int64_t uSecsToDelay = (int64_t)(secondsToDelay*1000000.0);
sessionTimerTask = env->taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)sessionTimerHandler, (void*)NULL);
}
char const* actionString
= createReceivers? "Receiving streamed data":"Data is being streamed";
if (timerIsBeingUsed) {
*env << actionString
<< " (for up to " << secondsToDelay
<< " seconds)...\n";
} else {
#ifdef USE_SIGNALS
pid_t ourPid = getpid();
*env << actionString
<< " (signal with \"kill -HUP " << (int)ourPid
<< "\" or \"kill -USR1 " << (int)ourPid
<< "\" to terminate)...\n";
#else
*env << actionString << "...\n";
#endif
}
sessionTimeoutBrokenServerTask = NULL;
// Watch for incoming packets (if desired):
checkForPacketArrival(NULL);
checkInterPacketGaps(NULL);
checkSessionTimeoutBrokenServer(NULL);
}
void closeMediaSinks() {
Medium::close(qtOut); qtOut = NULL;
Medium::close(aviOut); aviOut = NULL;
if (session == NULL) return;
MediaSubsessionIterator iter(*session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
Medium::close(subsession->sink);
subsession->sink = NULL;
}
}
void subsessionAfterPlaying(void* clientData) {
// Begin by closing this media subsession's stream:
MediaSubsession* subsession = (MediaSubsession*)clientData;
Medium::close(subsession->sink);
subsession->sink = NULL;
// Next, check whether *all* subsessions' streams have now been closed:
MediaSession& session = subsession->parentSession();
MediaSubsessionIterator iter(session);
while ((subsession = iter.next()) != NULL) {
if (subsession->sink != NULL) return; // this subsession is still active
}
// All subsessions' streams have now been closed
sessionAfterPlaying();
}
void subsessionByeHandler(void* clientData) {
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
unsigned secsDiff = timeNow.tv_sec - startTime.tv_sec;
MediaSubsession* subsession = (MediaSubsession*)clientData;
*env << "Received RTCP \"BYE\" on \"" << subsession->mediumName()
<< "/" << subsession->codecName()
<< "\" subsession (after " << secsDiff
<< " seconds)\n";
// Act now as if the subsession had closed:
subsessionAfterPlaying(subsession);
}
void sessionAfterPlaying(void* /*clientData*/) {
if (!playContinuously) {
shutdown(0);
} else {
// We've been asked to play the stream(s) over again.
// First, reset state from the current session:
if (env != NULL) {
env->taskScheduler().unscheduleDelayedTask(periodicFileOutputTask);
env->taskScheduler().unscheduleDelayedTask(sessionTimerTask);
env->taskScheduler().unscheduleDelayedTask(sessionTimeoutBrokenServerTask);
env->taskScheduler().unscheduleDelayedTask(arrivalCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(interPacketGapCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(qosMeasurementTimerTask);
}
totNumPacketsReceived = ~0;
startPlayingSession(session, initialSeekTime, endTime, scale, continueAfterPLAY);
}
}
void sessionTimerHandler(void* /*clientData*/) {
sessionTimerTask = NULL;
sessionAfterPlaying();
}
void periodicFileOutputTimerHandler(void* /*clientData*/) {
fileOutputSecondsSoFar += fileOutputInterval;
// First, close the existing output files:
closeMediaSinks();
// Then, create new output files:
createPeriodicOutputFiles();
}
class qosMeasurementRecord {
public:
qosMeasurementRecord(struct timeval const& startTime, RTPSource* src)
: fSource(src), fNext(NULL),
kbits_per_second_min(1e20), kbits_per_second_max(0),
kBytesTotal(0.0),
packet_loss_fraction_min(1.0), packet_loss_fraction_max(0.0),
totNumPacketsReceived(0), totNumPacketsExpected(0) {
measurementEndTime = measurementStartTime = startTime;
RTPReceptionStatsDB::Iterator statsIter(src->receptionStatsDB());
// Assume that there's only one SSRC source (usually the case):
RTPReceptionStats* stats = statsIter.next(True);
if (stats != NULL) {
kBytesTotal = stats->totNumKBytesReceived();
totNumPacketsReceived = stats->totNumPacketsReceived();
totNumPacketsExpected = stats->totNumPacketsExpected();
}
}
virtual ~qosMeasurementRecord() { delete fNext; }
void periodicQOSMeasurement(struct timeval const& timeNow);
public:
RTPSource* fSource;
qosMeasurementRecord* fNext;
public:
struct timeval measurementStartTime, measurementEndTime;
double kbits_per_second_min, kbits_per_second_max;
double kBytesTotal;
double packet_loss_fraction_min, packet_loss_fraction_max;
unsigned totNumPacketsReceived, totNumPacketsExpected;
};
static qosMeasurementRecord* qosRecordHead = NULL;
static void periodicQOSMeasurement(void* clientData); // forward
static unsigned nextQOSMeasurementUSecs;
static void scheduleNextQOSMeasurement() {
nextQOSMeasurementUSecs += qosMeasurementIntervalMS*1000;
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
unsigned timeNowUSecs = timeNow.tv_sec*1000000 + timeNow.tv_usec;
int usecsToDelay = nextQOSMeasurementUSecs - timeNowUSecs;
qosMeasurementTimerTask = env->taskScheduler().scheduleDelayedTask(
usecsToDelay, (TaskFunc*)periodicQOSMeasurement, (void*)NULL);
}
static void periodicQOSMeasurement(void* /*clientData*/) {
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
for (qosMeasurementRecord* qosRecord = qosRecordHead;
qosRecord != NULL; qosRecord = qosRecord->fNext) {
qosRecord->periodicQOSMeasurement(timeNow);
}
// Do this again later:
scheduleNextQOSMeasurement();
}
void qosMeasurementRecord
::periodicQOSMeasurement(struct timeval const& timeNow) {
unsigned secsDiff = timeNow.tv_sec - measurementEndTime.tv_sec;
int usecsDiff = timeNow.tv_usec - measurementEndTime.tv_usec;
double timeDiff = secsDiff + usecsDiff/1000000.0;
measurementEndTime = timeNow;
RTPReceptionStatsDB::Iterator statsIter(fSource->receptionStatsDB());
// Assume that there's only one SSRC source (usually the case):
RTPReceptionStats* stats = statsIter.next(True);
if (stats != NULL) {
double kBytesTotalNow = stats->totNumKBytesReceived();
double kBytesDeltaNow = kBytesTotalNow - kBytesTotal;
kBytesTotal = kBytesTotalNow;
double kbpsNow = timeDiff == 0.0 ? 0.0 : 8*kBytesDeltaNow/timeDiff;
if (kbpsNow < 0.0) kbpsNow = 0.0; // in case of roundoff error
if (kbpsNow < kbits_per_second_min) kbits_per_second_min = kbpsNow;
if (kbpsNow > kbits_per_second_max) kbits_per_second_max = kbpsNow;
unsigned totReceivedNow = stats->totNumPacketsReceived();
unsigned totExpectedNow = stats->totNumPacketsExpected();
unsigned deltaReceivedNow = totReceivedNow - totNumPacketsReceived;
unsigned deltaExpectedNow = totExpectedNow - totNumPacketsExpected;
totNumPacketsReceived = totReceivedNow;
totNumPacketsExpected = totExpectedNow;
double lossFractionNow = deltaExpectedNow == 0 ? 0.0
: 1.0 - deltaReceivedNow/(double)deltaExpectedNow;
//if (lossFractionNow < 0.0) lossFractionNow = 0.0; //reordering can cause
if (lossFractionNow < packet_loss_fraction_min) {
packet_loss_fraction_min = lossFractionNow;
}
if (lossFractionNow > packet_loss_fraction_max) {
packet_loss_fraction_max = lossFractionNow;
}
}
}
void beginQOSMeasurement() {
// Set up a measurement record for each active subsession:
struct timeval startTime;
gettimeofday(&startTime, NULL);
nextQOSMeasurementUSecs = startTime.tv_sec*1000000 + startTime.tv_usec;
qosMeasurementRecord* qosRecordTail = NULL;
MediaSubsessionIterator iter(*session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
RTPSource* src = subsession->rtpSource();
if (src == NULL) continue;
qosMeasurementRecord* qosRecord
= new qosMeasurementRecord(startTime, src);
if (qosRecordHead == NULL) qosRecordHead = qosRecord;
if (qosRecordTail != NULL) qosRecordTail->fNext = qosRecord;
qosRecordTail = qosRecord;
}
// Then schedule the first of the periodic measurements:
scheduleNextQOSMeasurement();
}
void printQOSData(int exitCode) {
*env << "begin_QOS_statistics\n";
// Print out stats for each active subsession:
qosMeasurementRecord* curQOSRecord = qosRecordHead;
if (session != NULL) {
MediaSubsessionIterator iter(*session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
RTPSource* src = subsession->rtpSource();
if (src == NULL) continue;
*env << "subsession\t" << subsession->mediumName()
<< "/" << subsession->codecName() << "\n";
unsigned numPacketsReceived = 0, numPacketsExpected = 0;
if (curQOSRecord != NULL) {
numPacketsReceived = curQOSRecord->totNumPacketsReceived;
numPacketsExpected = curQOSRecord->totNumPacketsExpected;
}
*env << "num_packets_received\t" << numPacketsReceived << "\n";
*env << "num_packets_lost\t" << int(numPacketsExpected - numPacketsReceived) << "\n";
if (curQOSRecord != NULL) {
unsigned secsDiff = curQOSRecord->measurementEndTime.tv_sec
- curQOSRecord->measurementStartTime.tv_sec;
int usecsDiff = curQOSRecord->measurementEndTime.tv_usec
- curQOSRecord->measurementStartTime.tv_usec;
double measurementTime = secsDiff + usecsDiff/1000000.0;
*env << "elapsed_measurement_time\t" << measurementTime << "\n";
*env << "kBytes_received_total\t" << curQOSRecord->kBytesTotal << "\n";
*env << "measurement_sampling_interval_ms\t" << qosMeasurementIntervalMS << "\n";
if (curQOSRecord->kbits_per_second_max == 0) {
// special case: we didn't receive any data:
*env <<
"kbits_per_second_min\tunavailable\n"
"kbits_per_second_ave\tunavailable\n"
"kbits_per_second_max\tunavailable\n";
} else {
*env << "kbits_per_second_min\t" << curQOSRecord->kbits_per_second_min << "\n";
*env << "kbits_per_second_ave\t"
<< (measurementTime == 0.0 ? 0.0 : 8*curQOSRecord->kBytesTotal/measurementTime) << "\n";
*env << "kbits_per_second_max\t" << curQOSRecord->kbits_per_second_max << "\n";
}
*env << "packet_loss_percentage_min\t" << 100*curQOSRecord->packet_loss_fraction_min << "\n";
double packetLossFraction = numPacketsExpected == 0 ? 1.0
: 1.0 - numPacketsReceived/(double)numPacketsExpected;
if (packetLossFraction < 0.0) packetLossFraction = 0.0;
*env << "packet_loss_percentage_ave\t" << 100*packetLossFraction << "\n";
*env << "packet_loss_percentage_max\t"
<< (packetLossFraction == 1.0 ? 100.0 : 100*curQOSRecord->packet_loss_fraction_max) << "\n";
RTPReceptionStatsDB::Iterator statsIter(src->receptionStatsDB());
// Assume that there's only one SSRC source (usually the case):
RTPReceptionStats* stats = statsIter.next(True);
if (stats != NULL) {
*env << "inter_packet_gap_ms_min\t" << stats->minInterPacketGapUS()/1000.0 << "\n";
struct timeval totalGaps = stats->totalInterPacketGaps();
double totalGapsMS = totalGaps.tv_sec*1000.0 + totalGaps.tv_usec/1000.0;
unsigned totNumPacketsReceived = stats->totNumPacketsReceived();
*env << "inter_packet_gap_ms_ave\t"
<< (totNumPacketsReceived == 0 ? 0.0 : totalGapsMS/totNumPacketsReceived) << "\n";
*env << "inter_packet_gap_ms_max\t" << stats->maxInterPacketGapUS()/1000.0 << "\n";
}
curQOSRecord = curQOSRecord->fNext;
}
}
}
*env << "end_QOS_statistics\n";
delete qosRecordHead;
}
Boolean areAlreadyShuttingDown = False;
int shutdownExitCode;
void shutdown(int exitCode) {
if (areAlreadyShuttingDown) return; // in case we're called after receiving a RTCP "BYE" while in the middle of a "TEARDOWN".
areAlreadyShuttingDown = True;
shutdownExitCode = exitCode;
if (env != NULL) {
env->taskScheduler().unscheduleDelayedTask(periodicFileOutputTask);
env->taskScheduler().unscheduleDelayedTask(sessionTimerTask);
env->taskScheduler().unscheduleDelayedTask(sessionTimeoutBrokenServerTask);
env->taskScheduler().unscheduleDelayedTask(arrivalCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(interPacketGapCheckTimerTask);
env->taskScheduler().unscheduleDelayedTask(qosMeasurementTimerTask);
}
if (qosMeasurementIntervalMS > 0) {
printQOSData(exitCode);
}
// Teardown, then shutdown, any outstanding RTP/RTCP subsessions
Boolean shutdownImmediately = True; // by default
if (session != NULL) {
RTSPClient::responseHandler* responseHandlerForTEARDOWN = NULL; // unless:
if (waitForResponseToTEARDOWN) {
shutdownImmediately = False;
responseHandlerForTEARDOWN = continueAfterTEARDOWN;
}
tearDownSession(session, responseHandlerForTEARDOWN);
}
if (shutdownImmediately) continueAfterTEARDOWN(NULL, 0, NULL);
}
void continueAfterTEARDOWN(RTSPClient*, int /*resultCode*/, char* resultString) {
delete[] resultString;
// Now that we've stopped any more incoming data from arriving, close our output files:
closeMediaSinks();
Medium::close(session);
// Finally, shut down our client:
delete ourAuthenticator;
delete authDBForREGISTER;
Medium::close(ourClient);
// Adios...
exit(shutdownExitCode);
}
void signalHandlerShutdown(int /*sig*/) {
*env << "Got shutdown signal\n";
waitForResponseToTEARDOWN = False; // to ensure that we end, even if the server does not respond to our TEARDOWN
shutdown(0);
}
void checkForPacketArrival(void* /*clientData*/) {
if (!notifyOnPacketArrival) return; // we're not checking
// Check each subsession, to see whether it has received data packets:
unsigned numSubsessionsChecked = 0;
unsigned numSubsessionsWithReceivedData = 0;
unsigned numSubsessionsThatHaveBeenSynced = 0;
MediaSubsessionIterator iter(*session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
RTPSource* src = subsession->rtpSource();
if (src == NULL) continue;
++numSubsessionsChecked;
if (src->receptionStatsDB().numActiveSourcesSinceLastReset() > 0) {
// At least one data packet has arrived
++numSubsessionsWithReceivedData;
}
if (src->hasBeenSynchronizedUsingRTCP()) {
++numSubsessionsThatHaveBeenSynced;
}
}
unsigned numSubsessionsToCheck = numSubsessionsChecked;
// Special case for "QuickTimeFileSink"s and "AVIFileSink"s:
// They might not use all of the input sources:
if (qtOut != NULL) {
numSubsessionsToCheck = qtOut->numActiveSubsessions();
} else if (aviOut != NULL) {
numSubsessionsToCheck = aviOut->numActiveSubsessions();
}
Boolean notifyTheUser;
if (!syncStreams) {
notifyTheUser = numSubsessionsWithReceivedData > 0; // easy case
} else {
notifyTheUser = numSubsessionsWithReceivedData >= numSubsessionsToCheck
&& numSubsessionsThatHaveBeenSynced == numSubsessionsChecked;
// Note: A subsession with no active sources is considered to be synced
}
if (notifyTheUser) {
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
char timestampStr[100];
sprintf(timestampStr, "%ld%03ld", timeNow.tv_sec, (long)(timeNow.tv_usec/1000));
*env << (syncStreams ? "Synchronized d" : "D")
<< "ata packets have begun arriving [" << timestampStr << "]\007\n";
return;
}
// No luck, so reschedule this check again, after a delay:
int uSecsToDelay = 100000; // 100 ms
arrivalCheckTimerTask
= env->taskScheduler().scheduleDelayedTask(uSecsToDelay,
(TaskFunc*)checkForPacketArrival, NULL);
}
void checkInterPacketGaps(void* /*clientData*/) {
if (interPacketGapMaxTime == 0) return; // we're not checking
// Check each subsession, counting up how many packets have been received:
unsigned newTotNumPacketsReceived = 0;
MediaSubsessionIterator iter(*session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
RTPSource* src = subsession->rtpSource();
if (src == NULL) continue;
newTotNumPacketsReceived += src->receptionStatsDB().totNumPacketsReceived();
}
if (newTotNumPacketsReceived == totNumPacketsReceived) {
// No additional packets have been received since the last time we
// checked, so end this stream:
*env << "Closing session, because we stopped receiving packets.\n";
interPacketGapCheckTimerTask = NULL;
sessionAfterPlaying();
} else {
totNumPacketsReceived = newTotNumPacketsReceived;
// Check again, after the specified delay:
interPacketGapCheckTimerTask
= env->taskScheduler().scheduleDelayedTask(interPacketGapMaxTime*1000000,
(TaskFunc*)checkInterPacketGaps, NULL);
}
}
void checkSessionTimeoutBrokenServer(void* /*clientData*/) {
if (!sendKeepAlivesToBrokenServers) return; // we're not checking
// Send an "OPTIONS" request, starting with the second call
if (sessionTimeoutBrokenServerTask != NULL) {
getOptions(NULL);
}
unsigned sessionTimeout = sessionTimeoutParameter == 0 ? 60/*default*/ : sessionTimeoutParameter;
unsigned secondsUntilNextKeepAlive = sessionTimeout <= 5 ? 1 : sessionTimeout - 5;
// Reduce the interval a little, to be on the safe side
sessionTimeoutBrokenServerTask
= env->taskScheduler().scheduleDelayedTask(secondsUntilNextKeepAlive*1000000,
(TaskFunc*)checkSessionTimeoutBrokenServer, NULL);
}
live/testProgs/playCommon.hh 000444 001752 001752 00000004327 13242237367 016115 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A common framework, used for the "openRTSP" and "playSIP" applications
// Interfaces
#include "liveMedia.hh"
extern Medium* createClient(UsageEnvironment& env, char const* URL, int verbosityLevel, char const* applicationName);
extern void assignClient(Medium* client);
extern RTSPClient* ourRTSPClient;
extern SIPClient* ourSIPClient;
extern void getOptions(RTSPClient::responseHandler* afterFunc);
extern void getSDPDescription(RTSPClient::responseHandler* afterFunc);
extern void setupSubsession(MediaSubsession* subsession, Boolean streamUsingTCP, Boolean forceMulticastOnUnspecified, RTSPClient::responseHandler* afterFunc);
extern void startPlayingSession(MediaSession* session, double start, double end, float scale, RTSPClient::responseHandler* afterFunc);
extern void startPlayingSession(MediaSession* session, char const* absStartTime, char const* absEndTime, float scale, RTSPClient::responseHandler* afterFunc);
// For playing by 'absolute' time (using strings of the form "YYYYMMDDTHHMMSSZ" or "YYYYMMDDTHHMMSS.Z"
extern void tearDownSession(MediaSession* session, RTSPClient::responseHandler* afterFunc);
extern void setUserAgentString(char const* userAgentString);
extern Authenticator* ourAuthenticator;
extern Boolean allowProxyServers;
extern Boolean controlConnectionUsesTCP;
extern Boolean supportCodecSelection;
extern char const* clientProtocolName;
extern unsigned statusCode;
live/testProgs/playSIP.cpp 000444 001752 001752 00000015312 13242237367 015477 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A SIP client test program that opens a SIP URL argument,
// and extracts the data from each incoming RTP stream.
#include "playCommon.hh"
#include "SIPClient.hh"
static char* getLine(char* startOfLine) {
// returns the start of the next line, or NULL if none
for (char* ptr = startOfLine; *ptr != '\0'; ++ptr) {
if (*ptr == '\r' || *ptr == '\n') {
// We found the end of the line
*ptr++ = '\0';
if (*ptr == '\n') ++ptr;
return ptr;
}
}
return NULL;
}
SIPClient* ourSIPClient = NULL;
Medium* createClient(UsageEnvironment& env, char const* /*url*/, int verbosityLevel, char const* applicationName) {
// First, trim any directory prefixes from "applicationName":
char const* suffix = &applicationName[strlen(applicationName)];
while (suffix != applicationName) {
if (*suffix == '/' || *suffix == '\\') {
applicationName = ++suffix;
break;
}
--suffix;
}
extern unsigned char desiredAudioRTPPayloadFormat;
extern char* mimeSubtype;
return ourSIPClient = SIPClient::createNew(env, desiredAudioRTPPayloadFormat, mimeSubtype, verbosityLevel, applicationName);
}
// The followign function is implemented, but is not used for "playSIP":
void assignClient(Medium* /*client*/) {
}
void getOptions(RTSPClient::responseHandler* afterFunc) {
ourSIPClient->envir().setResultMsg("NOT SUPPORTED IN CLIENT");
afterFunc(NULL, -1, strDup(ourSIPClient->envir().getResultMsg()));
}
void getSDPDescription(RTSPClient::responseHandler* afterFunc) {
extern char* proxyServerName;
if (proxyServerName != NULL) {
// Tell the SIP client about the proxy:
NetAddressList addresses(proxyServerName);
if (addresses.numAddresses() == 0) {
ourSIPClient->envir() << "Failed to find network address for \"" << proxyServerName << "\"\n";
} else {
NetAddress address = *(addresses.firstAddress());
unsigned proxyServerAddress // later, allow for IPv6 #####
= *(unsigned*)(address.data());
extern unsigned short proxyServerPortNum;
if (proxyServerPortNum == 0) proxyServerPortNum = 5060; // default
ourSIPClient->setProxyServer(proxyServerAddress, proxyServerPortNum);
}
}
extern unsigned short desiredPortNum;
unsigned short clientStartPortNum = desiredPortNum;
if (clientStartPortNum == 0) clientStartPortNum = 8000; // default
ourSIPClient->setClientStartPortNum(clientStartPortNum);
extern char const* streamURL;
char const* username = ourAuthenticator == NULL ? NULL : ourAuthenticator->username();
char const* password = ourAuthenticator == NULL ? NULL : ourAuthenticator->password();
char* result;
if (username != NULL && password != NULL) {
result = ourSIPClient->inviteWithPassword(streamURL, username, password);
} else {
result = ourSIPClient->invite(streamURL);
}
int resultCode = result == NULL ? -1 : 0;
afterFunc(NULL, resultCode, strDup(result));
}
void setupSubsession(MediaSubsession* subsession, Boolean /*streamUsingTCP*/, Boolean /*forceMulticastOnUnspecified*/,RTSPClient::responseHandler* afterFunc) {
subsession->setSessionId("mumble"); // anything that's non-NULL will work
////////// BEGIN hack code that should really be implemented in SIPClient //////////
// Parse the "Transport:" header parameters:
// We do not send audio, but we need port for RTCP
char* serverAddressStr;
portNumBits serverPortNum;
unsigned char rtpChannelId, rtcpChannelId;
rtpChannelId = rtcpChannelId = 0xff;
serverPortNum = 0;
serverAddressStr = NULL;
char* sdp = strDup(ourSIPClient->getInviteSdpReply());
char* lineStart;
char* nextLineStart = sdp;
while (1) {
lineStart = nextLineStart;
if (lineStart == NULL) {
break;
}
nextLineStart = getLine(lineStart);
char* toTagStr = strDupSize(lineStart);
if (sscanf(lineStart, "m=audio %[^/\r\n]", toTagStr) == 1) {
sscanf(toTagStr, "%hu", &serverPortNum);
} else if (sscanf(lineStart, "c=IN IP4 %[^/\r\n]", toTagStr) == 1) {
serverAddressStr = strDup(toTagStr);
}
delete[] toTagStr;
}
if(sdp != NULL) {
delete[] sdp;
}
delete[] subsession->connectionEndpointName();
subsession->connectionEndpointName() = serverAddressStr;
subsession->serverPortNum = serverPortNum;
subsession->rtpChannelId = rtpChannelId;
subsession->rtcpChannelId = rtcpChannelId;
// Set the RTP and RTCP sockets' destination address and port from the information in the SETUP response (if present):
netAddressBits destAddress = subsession->connectionEndpointAddress();
if (destAddress != 0) {
subsession->setDestinations(destAddress);
}
////////// END hack code that should really be implemented in SIPClient //////////
afterFunc(NULL, 0, NULL);
}
void startPlayingSession(MediaSession* /*session*/, double /*start*/, double /*end*/, float /*scale*/, RTSPClient::responseHandler* afterFunc) {
if (ourSIPClient->sendACK()) {
//##### This isn't quite right, because we should really be allowing
//##### for the possibility of this ACK getting lost, by retransmitting
//##### it *each time* we get a 2xx response from the server.
afterFunc(NULL, 0, NULL);
} else {
afterFunc(NULL, -1, strDup(ourSIPClient->envir().getResultMsg()));
}
}
void startPlayingSession(MediaSession* /*session*/, const char* /*start*/, const char* /*end*/, float /*scale*/, RTSPClient::responseHandler* afterFunc) {
startPlayingSession(NULL,(double)0,(double)0,0,afterFunc);
}
void tearDownSession(MediaSession* /*session*/, RTSPClient::responseHandler* afterFunc) {
if (ourSIPClient == NULL || ourSIPClient->sendBYE()) {
afterFunc(NULL, 0, NULL);
} else {
afterFunc(NULL, -1, strDup(ourSIPClient->envir().getResultMsg()));
}
}
void setUserAgentString(char const* userAgentString) {
ourSIPClient->setUserAgentString(userAgentString);
}
Boolean allowProxyServers = True;
Boolean controlConnectionUsesTCP = False;
Boolean supportCodecSelection = True;
char const* clientProtocolName = "SIP";
live/testProgs/registerRTSPStream.cpp 000400 001752 001752 00000006614 13242237367 017664 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A demonstration application that uses our custom RTSP "REGISTER" command to register a stream
// (given by "rtsp://" URL) with a RTSP client or proxy server
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
char const* programName;
UsageEnvironment* env;
Boolean requestStreamingViaTCP = False;
char const* username = NULL;
char const* password = NULL;
void registerResponseHandler(RTSPClient* rtspClient, int resultCode, char* resultString) {
Medium::close(rtspClient);
// We're done:
exit(0);
}
void usage() {
*env << "usage: " << programName << " [-t] [-u ] "
" "
" [proxy-URL-suffix]\n";
exit(1);
}
int main(int argc, char const** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Parse command-line options and arguments:
// (Unfortunately we can't use getopt() here; Windoze doesn't have it)
programName = argv[0];
while (argc > 2) {
char const* const opt = argv[1];
if (opt[0] != '-') break;
switch (opt[1]) {
case 't': { // ask the remote client to access the stream via TCP instead of UDP
requestStreamingViaTCP = True;
break;
}
case 'u': { // specify a username and password
if (argc < 4) usage(); // there's no argv[3] (for the "password")
username = argv[2];
password = argv[3];
argv+=2; argc-=2;
break;
}
default: {
usage();
break;
}
}
++argv; --argc;
}
if (argc != 4 && argc != 5) usage();
char const* remoteClientNameOrAddress = argv[1];
portNumBits remoteClientPortNum;
if (sscanf(argv[2], "%hu", &remoteClientPortNum) != 1 || remoteClientPortNum == 0 || remoteClientPortNum == 0xFFFF) usage();
char const* rtspURLToRegister = argv[3];
char const* proxyURLSuffix = argc == 5 ? argv[4] : NULL;
Authenticator* ourAuthenticator = username == NULL ? NULL : new Authenticator(username, password);
// We have the command-line arguments. Send the command:
RTSPRegisterSender::createNew(*env, remoteClientNameOrAddress, remoteClientPortNum, rtspURLToRegister,
registerResponseHandler, ourAuthenticator,
requestStreamingViaTCP, proxyURLSuffix, False/*reuseConnection*/,
1/*verbosityLevel*/, programName);
// Note: This object will be deleted later, by the response handler
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
live/testProgs/sapWatch.cpp 000444 001752 001752 00000005445 13242237367 015736 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A program that receives and prints SDP/SAP announcements
// (on the default SDP/SAP directory: 224.2.127.254/9875)
#include "Groupsock.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
#include
static unsigned const maxPacketSize = 65536;
static unsigned char packet[maxPacketSize+1];
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
// Create a 'groupsock' for the input multicast group,port:
char const* sessionAddressStr = "224.2.127.254";
struct in_addr sessionAddress;
sessionAddress.s_addr = our_inet_addr(sessionAddressStr);
const Port port(9875);
const unsigned char ttl = 0; // we're only reading from this mcast group
Groupsock inputGroupsock(*env, sessionAddress, port, ttl);
// Start reading and printing incoming packets
// (Because this is the only thing we do, we can just do this
// synchronously, in a loop, so we don't need to set up an asynchronous
// event handler like we do in most of the other test programs.)
unsigned packetSize;
struct sockaddr_in fromAddress;
while (inputGroupsock.handleRead(packet, maxPacketSize,
packetSize, fromAddress)) {
printf("\n[packet from %s (%d bytes)]\n", AddressString(fromAddress).val(), packetSize);
// Ignore the first 8 bytes (SAP header).
if (packetSize < 8) {
*env << "Ignoring short packet from " << AddressString(fromAddress).val() << "%s!\n";
continue;
}
// convert "application/sdp\0" -> "application/sdp\0x20"
// or all other nonprintable characters to blank, except new line
unsigned idx = 8;
while (idx < packetSize) {
if (packet[idx] < 0x20 && packet[idx] != '\n') packet[idx] = 0x20;
idx++;
}
packet[packetSize] = '\0'; // just in case
printf("%s", (char*)(packet+8));
}
return 0; // only to prevent compiler warning
}
live/testProgs/testAMRAudioStreamer.cpp 000444 001752 001752 00000010511 13242237367 020156 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads an AMR audio file (as defined in RFC 3267)
// and streams it using RTP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
UsageEnvironment* env;
char const* inputFileName = "test.amr";
AMRAudioFileSource* audioSource;
RTPSink* audioSink;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer"
// test program - not this test program - as a model.
const unsigned short rtpPortNum = 16666;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 255;
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
rtpGroupsock.multicastSendOnly(); // we're a SSM source
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
rtcpGroupsock.multicastSendOnly(); // we're a SSM source
// Create a 'AMR Audio RTP' sink from the RTP 'groupsock':
audioSink = AMRAudioRTPSink::createNew(*env, &rtpGroupsock, 96);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 10; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
RTCPInstance* rtcp
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
audioSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
// Create and start a RTSP server to serve this stream.
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testAMRAudioStreamer\"",
True /*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*audioSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
audioSink->stopPlaying();
Medium::close(audioSource);
// Note that this also closes the input file that this source read from.
play();
}
void play() {
// Open the input file as an 'AMR audio file source':
AMRAudioFileSource* audioSource
= AMRAudioFileSource::createNew(*env, inputFileName);
if (audioSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as an AMR audio file source: "
<< env->getResultMsg() << "\n";
exit(1);
}
// Finally, start playing:
*env << "Beginning to read from file...\n";
audioSink->startPlaying(*audioSource, afterPlaying, audioSink);
}
live/testProgs/testDVVideoStreamer.cpp 000444 001752 001752 00000011076 13242237367 020064 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a DV Video Elementary Stream file,
// and streams it using RTP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
UsageEnvironment* env;
char const* inputFileName = "test.dv";
DVVideoStreamFramer* videoSource;
RTPSink* videoSink;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer"
// test program - not this test program - as a model.
const unsigned short rtpPortNum = 18888;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 255;
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
rtpGroupsock.multicastSendOnly(); // we're a SSM source
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
rtcpGroupsock.multicastSendOnly(); // we're a SSM source
// Create a 'DV Video RTP' sink from the RTP 'groupsock':
// (But first, make sure that its buffers will be large enough to handle the huge size of DV frames (as big as 288000).)
OutPacketBuffer::maxSize = 300000;
videoSink = DVVideoRTPSink::createNew(*env, &rtpGroupsock, 96);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 50000; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
RTCPInstance* rtcp
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
videoSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testDVVideoStreamer\"",
True /*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
videoSink->stopPlaying();
Medium::close(videoSource);
// Note that this also closes the input file that this source read from.
// Start playing once again:
play();
}
void play() {
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, inputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
FramedSource* videoES = fileSource;
// Create a framer for the Video Elementary Stream:
videoSource = DVVideoStreamFramer::createNew(*env, videoES);
// Finally, start playing:
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}
live/testProgs/testGSMStreamer.cpp 000400 001752 001752 00000012630 13242237367 017177 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that streams GSM audio via RTP/RTCP
// main program
// NOTE: This program assumes the existence of a (currently nonexistent)
// function called "createNewGSMAudioSource()".
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
////////// Main program //////////
// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
// To set up an internal RTSP server, uncomment the following:
//#define IMPLEMENT_RTSP_SERVER 1
// (Note that this RTSP server works for multicast only)
#ifdef IMPLEMENT_RTSP_SERVER
RTSPServer* rtspServer;
#endif
UsageEnvironment* env;
void afterPlaying(void* clientData); // forward
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
FramedSource* source;
RTPSink* sink;
RTCPInstance* rtcpInstance;
Groupsock* rtpGroupsock;
Groupsock* rtcpGroupsock;
} sessionState;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
char* destinationAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: This is a multicast address. If you wish to stream using
// unicast instead, then replace this string with the unicast address
// of the (single) destination. (You may also need to make a similar
// change to the receiver program.)
#endif
const unsigned short rtpPortNum = 6666;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 1; // low, in case routers don't admin scope
struct in_addr destinationAddress;
destinationAddress.s_addr = our_inet_addr(destinationAddressStr);
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
sessionState.rtpGroupsock
= new Groupsock(*env, destinationAddress, rtpPort, ttl);
sessionState.rtcpGroupsock
= new Groupsock(*env, destinationAddress, rtcpPort, ttl);
#ifdef USE_SSM
sessionState.rtpGroupsock->multicastSendOnly();
sessionState.rtcpGroupsock->multicastSendOnly();
#endif
// Create a 'GSM RTP' sink from the RTP 'groupsock':
sessionState.sink
= GSMAudioRTPSink::createNew(*env, sessionState.rtpGroupsock);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 160; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstance
= RTCPInstance::createNew(*env, sessionState.rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
sessionState.sink, NULL /* we're a server */,
isSSM);
// Note: This starts RTCP running automatically
#ifdef IMPLEMENT_RTSP_SERVER
rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "%s\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", "GSM input",
"Session streamed by \"testGSMStreamer\"", isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*sessionState.sink, sessionState.rtcpInstance));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
#endif
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void play() {
// Open the input source:
extern FramedSource* createNewGSMAudioSource(UsageEnvironment&);
sessionState.source = createNewGSMAudioSource(*env);
if (sessionState.source == NULL) {
*env << "Failed to create GSM source\n";
exit(1);
}
// Finally, start the streaming:
*env << "Beginning streaming...\n";
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
}
void afterPlaying(void* /*clientData*/) {
*env << "...done streaming\n";
sessionState.sink->stopPlaying();
// End this loop by closing the media:
#ifdef IMPLEMENT_RTSP_SERVER
Medium::close(rtspServer);
#endif
Medium::close(sessionState.rtcpInstance);
Medium::close(sessionState.sink);
delete sessionState.rtpGroupsock;
Medium::close(sessionState.source);
delete sessionState.rtcpGroupsock;
// And start another loop:
play();
}
live/testProgs/testH264VideoStreamer.cpp 000444 001752 001752 00000011723 13242237367 020175 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a H.264 Elementary Stream video file
// and streams it using RTP
// main program
//
// NOTE: For this application to work, the H.264 Elementary Stream video file *must* contain SPS and PPS NAL units,
// ideally at or near the start of the file. These SPS and PPS NAL units are used to specify 'configuration' information
// that is set in the output stream's SDP description (by the RTSP server that is built in to this application).
// Note also that - unlike some other "*Streamer" demo applications - the resulting stream can be received only using a
// RTSP client (such as "openRTSP")
#include
#include
#include
UsageEnvironment* env;
char const* inputFileName = "test.264";
H264VideoStreamFramer* videoSource;
RTPSink* videoSink;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer"
// test program - not this test program - as a model.
const unsigned short rtpPortNum = 18888;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 255;
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
rtpGroupsock.multicastSendOnly(); // we're a SSM source
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
rtcpGroupsock.multicastSendOnly(); // we're a SSM source
// Create a 'H264 Video RTP' sink from the RTP 'groupsock':
OutPacketBuffer::maxSize = 100000;
videoSink = H264VideoRTPSink::createNew(*env, &rtpGroupsock, 96);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 500; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
RTCPInstance* rtcp
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
videoSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testH264VideoStreamer\"",
True /*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
videoSink->stopPlaying();
Medium::close(videoSource);
// Note that this also closes the input file that this source read from.
// Start playing once again:
play();
}
void play() {
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, inputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
FramedSource* videoES = fileSource;
// Create a framer for the Video Elementary Stream:
videoSource = H264VideoStreamFramer::createNew(*env, videoES);
// Finally, start playing:
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}
live/testProgs/testH264VideoToTransportStream.cpp 000444 001752 001752 00000005270 13242237367 022066 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A program that converts a H.264 (Elementary Stream) video file into a Transport Stream file.
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
char const* inputFileName = "in.264";
char const* outputFileName = "out.ts";
void afterPlaying(void* clientData); // forward
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Open the input file as a 'byte-stream file source':
FramedSource* inputSource = ByteStreamFileSource::createNew(*env, inputFileName);
if (inputSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
// Create a 'framer' filter for this file source, to generate presentation times for each NAL unit:
H264VideoStreamFramer* framer = H264VideoStreamFramer::createNew(*env, inputSource, True/*includeStartCodeInOutput*/);
// Then create a filter that packs the H.264 video data into a Transport Stream:
MPEG2TransportStreamFromESSource* tsFrames = MPEG2TransportStreamFromESSource::createNew(*env);
tsFrames->addNewVideoSource(framer, 5/*mpegVersion: H.264*/);
// Open the output file as a 'file sink':
MediaSink* outputSink = FileSink::createNew(*env, outputFileName);
if (outputSink == NULL) {
*env << "Unable to open file \"" << outputFileName << "\" as a file sink\n";
exit(1);
}
// Finally, start playing:
*env << "Beginning to read...\n";
outputSink->startPlaying(*tsFrames, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "Done reading.\n";
*env << "Wrote output file: \"" << outputFileName << "\"\n";
exit(0);
}
live/testProgs/COPYING.LESSER 000755 001752 001752 00000000000 13242237367 017734 2../COPYING.LESSER ustar 00rsf rsf 000000 000000 live/testProgs/testMKVStreamer.cpp 000444 001752 001752 00000014730 13242237367 017221 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a ".mkv" (i.e., Matroska) file, demultiplexes each track
// (video, audio, subtitles), and streams each track using RTP multicast.
// main program
#include
#include
#include
UsageEnvironment* env;
char const* inputFileName = "test.mkv";
struct in_addr destinationAddress;
RTSPServer* rtspServer;
ServerMediaSession* sms;
MatroskaFile* matroskaFile;
MatroskaDemux* matroskaDemux;
// An array of structures representing the state of the video, audio, and subtitle tracks:
static struct {
unsigned trackNumber;
FramedSource* source;
RTPSink* sink;
RTCPInstance* rtcp;
} trackState[3];
void onMatroskaFileCreation(MatroskaFile* newFile, void* clientData); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Define our destination (multicast) IP address:
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer"
// test program - not this test program - as a model.
// Create our RTSP server. (Receivers will need to use RTSP to access the stream.)
rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
sms = ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testMKVStreamer\"",
True /*SSM*/);
// Arrange to create a "MatroskaFile" object for the specified file.
// (Note that this object is not created immediately, but instead via a callback.)
MatroskaFile::createNew(*env, inputFileName, onMatroskaFileCreation, NULL, "jpn");
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void play(); // forward
void onMatroskaFileCreation(MatroskaFile* newFile, void* /*clientData*/) {
matroskaFile = newFile;
// Create a new demultiplexor for the file:
matroskaDemux = matroskaFile->newDemux();
// Create source streams, "RTPSink"s, and "RTCPInstance"s for each preferred track;
unsigned short rtpPortNum = 44444;
const unsigned char ttl = 255;
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
for (unsigned i = 0; i < 3; ++i) {
unsigned trackNumber;
FramedSource* baseSource = matroskaDemux->newDemuxedTrack(trackNumber);
trackState[i].trackNumber = trackNumber;
unsigned estBitrate, numFiltersInFrontOfTrack;
trackState[i].source = matroskaFile
->createSourceForStreaming(baseSource, trackNumber, estBitrate, numFiltersInFrontOfTrack);
trackState[i].sink = NULL; // by default; may get changed below
trackState[i].rtcp = NULL; // ditto
if (trackState[i].source != NULL) {
Groupsock* rtpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum, ttl);
Groupsock* rtcpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum+1, ttl);
rtpPortNum += 2;
trackState[i].sink
= matroskaFile->createRTPSinkForTrackNumber(trackNumber, rtpGroupsock, 96+i);
if (trackState[i].sink != NULL) {
if (trackState[i].sink->estimatedBitrate() > 0) {
estBitrate = trackState[i].sink->estimatedBitrate(); // hack
}
trackState[i].rtcp
= RTCPInstance::createNew(*env, rtcpGroupsock, estBitrate, CNAME,
trackState[i].sink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
// Having set up a track for streaming, add it to our RTSP server's "ServerMediaSession":
sms->addSubsession(PassiveServerMediaSubsession::createNew(*trackState[i].sink, trackState[i].rtcp));
}
}
}
if (sms->numSubsessions() == 0) {
*env << "Error: The Matroska file \"" << inputFileName << "\" has no streamable tracks\n";
*env << "(Perhaps the file does not exist, or is not a 'Matroska' file.)\n";
exit(1);
}
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
play();
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
// Stop playing all "RTPSink"s, then close the source streams
// (which will also close the demultiplexor itself):
unsigned i;
for (i = 0; i < 3; ++i) {
if (trackState[i].sink != NULL) trackState[i].sink->stopPlaying();
Medium::close(trackState[i].source); trackState[i].source = NULL;
}
// Create a new demultiplexor from our Matroska file, then new data sources for each track:
matroskaDemux = matroskaFile->newDemux();
for (i = 0; i < 3; ++i) {
if (trackState[i].trackNumber != 0) {
FramedSource* baseSource
= matroskaDemux->newDemuxedTrackByTrackNumber(trackState[i].trackNumber);
unsigned estBitrate, numFiltersInFrontOfTrack;
trackState[i].source = matroskaFile
->createSourceForStreaming(baseSource, trackState[i].trackNumber,
estBitrate, numFiltersInFrontOfTrack);
}
}
// Start playing once again:
play();
}
void play() {
*env << "Beginning to read from file...\n";
// Start playing each track's RTP sink from its corresponding source:
for (unsigned i = 0; i < 3; ++i) {
if (trackState[i].sink != NULL && trackState[i].source != NULL) {
trackState[i].sink->startPlaying(*trackState[i].source, afterPlaying, NULL);
}
}
}
live/testProgs/testH265VideoToTransportStream.cpp 000444 001752 001752 00000005270 13242237367 022067 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A program that converts a H.265 (Elementary Stream) video file into a Transport Stream file.
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
char const* inputFileName = "in.265";
char const* outputFileName = "out.ts";
void afterPlaying(void* clientData); // forward
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Open the input file as a 'byte-stream file source':
FramedSource* inputSource = ByteStreamFileSource::createNew(*env, inputFileName);
if (inputSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
// Create a 'framer' filter for this file source, to generate presentation times for each NAL unit:
H265VideoStreamFramer* framer = H265VideoStreamFramer::createNew(*env, inputSource, True/*includeStartCodeInOutput*/);
// Then create a filter that packs the H.265 video data into a Transport Stream:
MPEG2TransportStreamFromESSource* tsFrames = MPEG2TransportStreamFromESSource::createNew(*env);
tsFrames->addNewVideoSource(framer, 6/*mpegVersion: H.265*/);
// Open the output file as a 'file sink':
MediaSink* outputSink = FileSink::createNew(*env, outputFileName);
if (outputSink == NULL) {
*env << "Unable to open file \"" << outputFileName << "\" as a file sink\n";
exit(1);
}
// Finally, start playing:
*env << "Beginning to read...\n";
outputSink->startPlaying(*tsFrames, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "Done reading.\n";
*env << "Wrote output file: \"" << outputFileName << "\"\n";
exit(0);
}
live/testProgs/testOggStreamer.cpp 000444 001752 001752 00000015035 13242237367 017277 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a ".ogg" (i.e., Ogg) file, demultiplexes each track
// (audio and/or video), and streams each track using RTP multicast.
// main program
#include
#include
#include
UsageEnvironment* env;
char const* inputFileName = "test.ogg";
struct in_addr destinationAddress;
RTSPServer* rtspServer;
ServerMediaSession* sms;
OggFile* oggFile;
OggDemux* oggDemux;
unsigned numTracks;
// A structure representing the state of a track:
struct TrackState {
u_int32_t trackNumber;
FramedSource* source;
RTPSink* sink;
RTCPInstance* rtcp;
};
TrackState* trackState;
void onOggFileCreation(OggFile* newFile, void* clientData); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Define our destination (multicast) IP address:
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer"
// test program - not this test program - as a model.
// Create our RTSP server. (Receivers will need to use RTSP to access the stream.)
rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
sms = ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testMKVStreamer\"",
True /*SSM*/);
// Arrange to create an "OggFile" object for the specified file.
// (Note that this object is not created immediately, but instead via a callback.)
OggFile::createNew(*env, inputFileName, onOggFileCreation, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void play(); // forward
void onOggFileCreation(OggFile* newFile, void* clientData) {
oggFile = newFile;
// Create a new demultiplexor for the file:
oggDemux = oggFile->newDemux();
// Create source streams, "RTPSink"s, and "RTCPInstance"s for each preferred track;
unsigned short rtpPortNum = 22222;
const unsigned char ttl = 255;
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
numTracks = oggFile->numTracks();
trackState = new TrackState[numTracks];
for (unsigned i = 0; i < numTracks; ++i) {
u_int32_t trackNumber;
FramedSource* baseSource = oggDemux->newDemuxedTrack(trackNumber);
trackState[i].trackNumber = trackNumber;
unsigned estBitrate, numFiltersInFrontOfTrack;
trackState[i].source = oggFile
->createSourceForStreaming(baseSource, trackNumber, estBitrate, numFiltersInFrontOfTrack);
trackState[i].sink = NULL; // by default; may get changed below
trackState[i].rtcp = NULL; // ditto
if (trackState[i].source != NULL) {
Groupsock* rtpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum, ttl);
Groupsock* rtcpGroupsock = new Groupsock(*env, destinationAddress, rtpPortNum+1, ttl);
rtpPortNum += 2;
trackState[i].sink
= oggFile->createRTPSinkForTrackNumber(trackNumber, rtpGroupsock, 96+i);
if (trackState[i].sink != NULL) {
if (trackState[i].sink->estimatedBitrate() > 0) {
estBitrate = trackState[i].sink->estimatedBitrate(); // hack
}
trackState[i].rtcp
= RTCPInstance::createNew(*env, rtcpGroupsock, estBitrate, CNAME,
trackState[i].sink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
// Having set up a track for streaming, add it to our RTSP server's "ServerMediaSession":
sms->addSubsession(PassiveServerMediaSubsession::createNew(*trackState[i].sink, trackState[i].rtcp));
}
}
}
if (sms->numSubsessions() == 0) {
*env << "Error: The Ogg file \"" << inputFileName << "\" has no streamable tracks\n";
*env << "(Perhaps the file does not exist, is not an 'Ogg' file, or has no tracks that we know how to stream.)\n";
exit(1);
}
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
play();
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
// Stop playing all "RTPSink"s, then close the source streams
// (which will also close the demultiplexor itself):
unsigned i;
for (i = 0; i < numTracks; ++i) {
if (trackState[i].sink != NULL) trackState[i].sink->stopPlaying();
Medium::close(trackState[i].source); trackState[i].source = NULL;
}
// Create a new demultiplexor from our Ogg file, then new data sources for each track:
oggDemux = oggFile->newDemux();
for (i = 0; i < numTracks; ++i) {
if (trackState[i].trackNumber != 0) {
FramedSource* baseSource
= oggDemux->newDemuxedTrack(trackState[i].trackNumber);
unsigned estBitrate, numFiltersInFrontOfTrack;
trackState[i].source
= oggFile->createSourceForStreaming(baseSource, trackState[i].trackNumber,
estBitrate, numFiltersInFrontOfTrack);
}
}
// Start playing once again:
play();
}
void play() {
*env << "Beginning to read from file...\n";
// Start playing each track's RTP sink from its corresponding source:
for (unsigned i = 0; i < numTracks; ++i) {
if (trackState[i].sink != NULL && trackState[i].source != NULL) {
trackState[i].sink->startPlaying(*trackState[i].source, afterPlaying, NULL);
}
}
}
live/testProgs/testMP3-using-ADUs.sdp 000444 001752 001752 00000000355 13242237367 017377 0 ustar 00rsf rsf 000000 000000 v=0
o=- 49452 4 IN IP4 127.0.0.1
s=Test MP3 session
i=Parameters for the session streamed by "testMP3Streamer"
t=0 0
a=tool:testMP3Streamer
a=type:broadcast
m=audio 6666 RTP/AVP 96
c=IN IP4 239.255.42.42/127
a=rtpmap:96 mpa-robust/90000
live/testProgs/testMP3.sdp 000444 001752 001752 00000000320 13242237367 015452 0 ustar 00rsf rsf 000000 000000 v=0
o=- 49452 4 IN IP4 127.0.0.1
s=Test MP3 session
i=Parameters for the session streamed by "testMP3Streamer"
t=0 0
a=tool:testMP3Streamer
a=type:broadcast
m=audio 6666 RTP/AVP 14
c=IN IP4 239.255.42.42/127
live/testProgs/testMP3Receiver.cpp 000444 001752 001752 00000012521 13242237367 017141 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that receives a RTP/RTCP multicast MP3 stream,
// and outputs the resulting MP3 file stream to 'stdout'
// main program
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
// To receive a stream of 'ADUs' rather than raw MP3 frames, uncomment this:
//#define STREAM_USING_ADUS 1
// (For more information about ADUs and interleaving,
// see )
// To receive a "source-specific multicast" (SSM) stream, uncomment this:
//#define USE_SSM 1
void afterPlaying(void* clientData); // forward
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
FramedSource* source;
FileSink* sink;
RTCPInstance* rtcpInstance;
} sessionState;
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create the data sink for 'stdout':
sessionState.sink = FileSink::createNew(*env, "stdout");
// Note: The string "stdout" is handled as a special case.
// A real file name could have been used instead.
// Create 'groupsocks' for RTP and RTCP:
char const* sessionAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: If the session is unicast rather than multicast,
// then replace this string with "0.0.0.0"
#endif
const unsigned short rtpPortNum = 6666;
const unsigned short rtcpPortNum = rtpPortNum+1;
#ifndef USE_SSM
const unsigned char ttl = 1; // low, in case routers don't admin scope
#endif
struct in_addr sessionAddress;
sessionAddress.s_addr = our_inet_addr(sessionAddressStr);
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
#ifdef USE_SSM
char* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
struct in_addr sourceFilterAddress;
sourceFilterAddress.s_addr = our_inet_addr(sourceAddressStr);
Groupsock rtpGroupsock(*env, sessionAddress, sourceFilterAddress, rtpPort);
Groupsock rtcpGroupsock(*env, sessionAddress, sourceFilterAddress, rtcpPort);
rtcpGroupsock.changeDestinationParameters(sourceFilterAddress,0,~0);
// our RTCP "RR"s are sent back using unicast
#else
Groupsock rtpGroupsock(*env, sessionAddress, rtpPort, ttl);
Groupsock rtcpGroupsock(*env, sessionAddress, rtcpPort, ttl);
#endif
RTPSource* rtpSource;
#ifndef STREAM_USING_ADUS
// Create the data source: a "MPEG Audio RTP source"
rtpSource = MPEG1or2AudioRTPSource::createNew(*env, &rtpGroupsock);
#else
// Create the data source: a "MP3 *ADU* RTP source"
unsigned char rtpPayloadFormat = 96; // a dynamic payload type
rtpSource
= MP3ADURTPSource::createNew(*env, &rtpGroupsock, rtpPayloadFormat);
#endif
// Create (and start) a 'RTCP instance' for the RTP source:
const unsigned estimatedSessionBandwidth = 160; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstance
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
NULL /* we're a client */, rtpSource);
// Note: This starts RTCP running automatically
sessionState.source = rtpSource;
#ifdef STREAM_USING_ADUS
// Add a filter that deinterleaves the ADUs after depacketizing them:
sessionState.source
= MP3ADUdeinterleaver::createNew(*env, sessionState.source);
if (sessionState.source == NULL) {
*env << "Unable to create an ADU deinterleaving filter for the source\n";
exit(1);
}
// Add another filter that converts these ADUs to MP3s:
sessionState.source
= MP3FromADUSource::createNew(*env, sessionState.source);
if (sessionState.source == NULL) {
*env << "Unable to create an ADU->MP3 filter for the source\n";
exit(1);
}
#endif
// Finally, start receiving the multicast stream:
*env << "Beginning receiving multicast stream...\n";
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done receiving\n";
// End by closing the media:
Medium::close(sessionState.rtcpInstance); // Note: Sends a RTCP BYE
Medium::close(sessionState.sink);
Medium::close(sessionState.source);
}
live/testProgs/testMP3Streamer.cpp 000400 001752 001752 00000015250 13242237367 017151 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that streams a MP3 file via RTP/RTCP
// main program
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
// To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
//#define STREAM_USING_ADUS 1
// To also reorder ADUs before streaming, uncomment the following:
//#define INTERLEAVE_ADUS 1
// (For more information about ADUs and interleaving,
// see )
// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
// To set up an internal RTSP server, uncomment the following:
//#define IMPLEMENT_RTSP_SERVER 1
// (Note that this RTSP server works for multicast only)
#ifdef IMPLEMENT_RTSP_SERVER
RTSPServer* rtspServer;
#endif
UsageEnvironment* env;
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
FramedSource* source;
RTPSink* sink;
RTCPInstance* rtcpInstance;
Groupsock* rtpGroupsock;
Groupsock* rtcpGroupsock;
} sessionState;
char const* inputFileName = "test.mp3";
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
char const* destinationAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: This is a multicast address. If you wish to stream using
// unicast instead, then replace this string with the unicast address
// of the (single) destination. (You may also need to make a similar
// change to the receiver program.)
#endif
const unsigned short rtpPortNum = 6666;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 1; // low, in case routers don't admin scope
struct in_addr destinationAddress;
destinationAddress.s_addr = our_inet_addr(destinationAddressStr);
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
sessionState.rtpGroupsock
= new Groupsock(*env, destinationAddress, rtpPort, ttl);
sessionState.rtcpGroupsock
= new Groupsock(*env, destinationAddress, rtcpPort, ttl);
#ifdef USE_SSM
sessionState.rtpGroupsock->multicastSendOnly();
sessionState.rtcpGroupsock->multicastSendOnly();
#endif
// Create a 'MP3 RTP' sink from the RTP 'groupsock':
#ifdef STREAM_USING_ADUS
unsigned char rtpPayloadFormat = 96; // A dynamic payload format code
sessionState.sink
= MP3ADURTPSink::createNew(*env, sessionState.rtpGroupsock,
rtpPayloadFormat);
#else
sessionState.sink
= MPEG1or2AudioRTPSink::createNew(*env, sessionState.rtpGroupsock);
#endif
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 160; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstance
= RTCPInstance::createNew(*env, sessionState.rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
sessionState.sink, NULL /* we're a server */,
isSSM);
// Note: This starts RTCP running automatically
#ifdef IMPLEMENT_RTSP_SERVER
rtspServer = RTSPServer::createNew(*env);
// Note that this (attempts to) start a server on the default RTSP server
// port: 554. To use a different port number, add it as an extra
// (optional) parameter to the "RTSPServer::createNew()" call above.
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testMP3Streamer\"", isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*sessionState.sink, sessionState.rtcpInstance));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
#endif
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* clientData); // forward
void play() {
// Open the file as a 'MP3 file source':
sessionState.source = MP3FileSource::createNew(*env, inputFileName);
if (sessionState.source == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a MP3 file source\n";
exit(1);
}
#ifdef STREAM_USING_ADUS
// Add a filter that converts the source MP3s to ADUs:
sessionState.source
= ADUFromMP3Source::createNew(*env, sessionState.source);
if (sessionState.source == NULL) {
*env << "Unable to create a MP3->ADU filter for the source\n";
exit(1);
}
#ifdef INTERLEAVE_ADUS
// Add another filter that interleaves the ADUs before packetizing them:
unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own order...
unsigned const interleaveCycleSize
= (sizeof interleaveCycle)/(sizeof (unsigned char));
Interleaving interleaving(interleaveCycleSize, interleaveCycle);
sessionState.source
= MP3ADUinterleaver::createNew(*env, interleaving, sessionState.source);
if (sessionState.source == NULL) {
*env << "Unable to create an ADU interleaving filter for the source\n";
exit(1);
}
#endif
#endif
// Finally, start the streaming:
*env << "Beginning streaming...\n";
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
}
void afterPlaying(void* /*clientData*/) {
*env << "...done streaming\n";
sessionState.sink->stopPlaying();
// End this loop by closing the current source:
Medium::close(sessionState.source);
// And start another loop:
play();
}
live/testProgs/testMPEG1or2AudioVideo.sdp 000444 001752 001752 00000000456 13242237367 020272 0 ustar 00rsf rsf 000000 000000 v=0
o=- 49451 3 IN IP4 127.0.0.1
s=Test MPEG Audio+Video session
i=Parameters for the session streamed by "testMPEG1or2AudioVideoStreamer"
t=0 0
a=tool:testMPEG1or2AudioVideoStreamer
a=type:broadcast
m=audio 6666 RTP/AVP 14
c=IN IP4 239.255.42.42/127
m=video 8888 RTP/AVP 32
c=IN IP4 239.255.42.42/127
live/testProgs/testMPEG1or2AudioVideoStreamer.cpp 000400 001752 001752 00000016654 13242237367 021770 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-1 or 2 Program Stream file,
// splits it into Audio and Video Elementary Streams,
// and streams both using RTP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
UsageEnvironment* env;
char const* inputFileName = "test.mpg";
MPEG1or2Demux* mpegDemux;
FramedSource* audioSource;
FramedSource* videoSource;
RTPSink* audioSink;
RTPSink* videoSink;
void play(); // forward
// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
// To set up an internal RTSP server, uncomment the following:
//#define IMPLEMENT_RTSP_SERVER 1
// (Note that this RTSP server works for multicast only)
// To stream *only* MPEG "I" frames (e.g., to reduce network bandwidth),
// change the following "False" to "True":
Boolean iFramesOnly = False;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
char const* destinationAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: This is a multicast address. If you wish to stream using
// unicast instead, then replace this string with the unicast address
// of the (single) destination. (You may also need to make a similar
// change to the receiver program.)
#endif
const unsigned short rtpPortNumAudio = 6666;
const unsigned short rtcpPortNumAudio = rtpPortNumAudio+1;
const unsigned short rtpPortNumVideo = 8888;
const unsigned short rtcpPortNumVideo = rtpPortNumVideo+1;
const unsigned char ttl = 7; // low, in case routers don't admin scope
struct in_addr destinationAddress;
destinationAddress.s_addr = our_inet_addr(destinationAddressStr);
const Port rtpPortAudio(rtpPortNumAudio);
const Port rtcpPortAudio(rtcpPortNumAudio);
const Port rtpPortVideo(rtpPortNumVideo);
const Port rtcpPortVideo(rtcpPortNumVideo);
Groupsock rtpGroupsockAudio(*env, destinationAddress, rtpPortAudio, ttl);
Groupsock rtcpGroupsockAudio(*env, destinationAddress, rtcpPortAudio, ttl);
Groupsock rtpGroupsockVideo(*env, destinationAddress, rtpPortVideo, ttl);
Groupsock rtcpGroupsockVideo(*env, destinationAddress, rtcpPortVideo, ttl);
#ifdef USE_SSM
rtpGroupsockAudio.multicastSendOnly();
rtcpGroupsockAudio.multicastSendOnly();
rtpGroupsockVideo.multicastSendOnly();
rtcpGroupsockVideo.multicastSendOnly();
#endif
// Create a 'MPEG Audio RTP' sink from the RTP 'groupsock':
audioSink = MPEG1or2AudioRTPSink::createNew(*env, &rtpGroupsockAudio);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidthAudio = 160; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
#ifdef IMPLEMENT_RTSP_SERVER
RTCPInstance* audioRTCP =
#endif
RTCPInstance::createNew(*env, &rtcpGroupsockAudio,
estimatedSessionBandwidthAudio, CNAME,
audioSink, NULL /* we're a server */, isSSM);
// Note: This starts RTCP running automatically
// Create a 'MPEG Video RTP' sink from the RTP 'groupsock':
videoSink = MPEG1or2VideoRTPSink::createNew(*env, &rtpGroupsockVideo);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidthVideo = 4500; // in kbps; for RTCP b/w share
#ifdef IMPLEMENT_RTSP_SERVER
RTCPInstance* videoRTCP =
#endif
RTCPInstance::createNew(*env, &rtcpGroupsockVideo,
estimatedSessionBandwidthVideo, CNAME,
videoSink, NULL /* we're a server */, isSSM);
// Note: This starts RTCP running automatically
#ifdef IMPLEMENT_RTSP_SERVER
RTSPServer* rtspServer = RTSPServer::createNew(*env);
// Note that this (attempts to) start a server on the default RTSP server
// port: 554. To use a different port number, add it as an extra
// (optional) parameter to the "RTSPServer::createNew()" call above.
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testMPEG1or2AudioVideoStreamer\"",
isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*audioSink, audioRTCP));
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, videoRTCP));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
#endif
// Finally, start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* clientData) {
// One of the sinks has ended playing.
// Check whether any of the sources have a pending read. If so,
// wait until its sink ends playing also:
if (audioSource->isCurrentlyAwaitingData()
|| videoSource->isCurrentlyAwaitingData()) return;
// Now that both sinks have ended, close both input sources,
// and start playing again:
*env << "...done reading from file\n";
audioSink->stopPlaying();
videoSink->stopPlaying();
// ensures that both are shut down
Medium::close(audioSource);
Medium::close(videoSource);
Medium::close(mpegDemux);
// Note: This also closes the input file that this source read from.
// Start playing once again:
play();
}
void play() {
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, inputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
// We must demultiplex Audio and Video Elementary Streams
// from the input source:
mpegDemux = MPEG1or2Demux::createNew(*env, fileSource);
FramedSource* audioES = mpegDemux->newAudioStream();
FramedSource* videoES = mpegDemux->newVideoStream();
// Create a framer for each Elementary Stream:
audioSource
= MPEG1or2AudioStreamFramer::createNew(*env, audioES);
videoSource
= MPEG1or2VideoStreamFramer::createNew(*env, videoES, iFramesOnly);
// Finally, start playing each sink.
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
audioSink->startPlaying(*audioSource, afterPlaying, audioSink);
}
live/testProgs/testMPEG1or2ProgramToTransportStream.cpp 000444 001752 001752 00000005262 13242237367 023241 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A program that converts a MPEG-1 or 2 Program Stream file into
// a Transport Stream file.
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
char const* inputFileName = "in.mpg";
char const* outputFileName = "out.ts";
void afterPlaying(void* clientData); // forward
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Open the input file as a 'byte-stream file source':
FramedSource* inputSource = ByteStreamFileSource::createNew(*env, inputFileName);
if (inputSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
// Create a MPEG demultiplexor that reads from that source.
MPEG1or2Demux* baseDemultiplexor = MPEG1or2Demux::createNew(*env, inputSource);
// Create, from this, a source that returns raw PES packets:
MPEG1or2DemuxedElementaryStream* pesSource = baseDemultiplexor->newRawPESStream();
// And, from this, a filter that converts to MPEG-2 Transport Stream frames:
FramedSource* tsFrames
= MPEG2TransportStreamFromPESSource::createNew(*env, pesSource);
// Open the output file as a 'file sink':
MediaSink* outputSink = FileSink::createNew(*env, outputFileName);
if (outputSink == NULL) {
*env << "Unable to open file \"" << outputFileName << "\" as a file sink\n";
exit(1);
}
// Finally, start playing:
*env << "Beginning to read...\n";
outputSink->startPlaying(*tsFrames, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "Done reading.\n";
*env << "Wrote output file: \"" << outputFileName << "\"\n";
exit(0);
}
live/testProgs/testMPEG1or2Splitter.cpp 000444 001752 001752 00000007210 13242237367 020037 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that splits a MPEG-1 or 2 Program Stream file into
// video and audio output files.
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include
char const* inputFileName = "in.mpg";
char const* outputFileName_video = "out_video.mpg";
char const* outputFileName_audio = "out_audio.mpg";
void afterPlaying(void* clientData); // forward
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
MPEG1or2Demux* baseDemultiplexor;
MediaSource* videoSource;
MediaSource* audioSource;
FileSink* videoSink;
FileSink* audioSink;
} sessionState;
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* inputSource
= ByteStreamFileSource::createNew(*env, inputFileName);
if (inputSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
// Create a MPEG demultiplexor that reads from that source.
sessionState.baseDemultiplexor = MPEG1or2Demux::createNew(*env, inputSource);
// Create, from this, our own sources (video and audio):
sessionState.videoSource = sessionState.baseDemultiplexor->newVideoStream();
sessionState.audioSource = sessionState.baseDemultiplexor->newAudioStream();
// Create the data sinks (output files):
sessionState.videoSink = FileSink::createNew(*env, outputFileName_video);
sessionState.audioSink = FileSink::createNew(*env, outputFileName_audio);
// Finally, start playing each sink.
*env << "Beginning to read...\n";
sessionState.videoSink->startPlaying(*sessionState.videoSource,
afterPlaying, sessionState.videoSink);
sessionState.audioSink->startPlaying(*sessionState.audioSource,
afterPlaying, sessionState.audioSink);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* clientData) {
Medium* finishedSink = (Medium*)clientData;
if (finishedSink == sessionState.videoSink) {
*env << "No more video\n";
Medium::close(sessionState.videoSink);
Medium::close(sessionState.videoSource);
sessionState.videoSink = NULL;
} else if (finishedSink == sessionState.audioSink) {
*env << "No more audio\n";
Medium::close(sessionState.audioSink);
Medium::close(sessionState.audioSource);
sessionState.audioSink = NULL;
}
if (sessionState.videoSink == NULL && sessionState.audioSink == NULL) {
*env << "...finished reading\n";
Medium::close(sessionState.baseDemultiplexor);
exit(0);
}
}
live/testProgs/testMPEG1or2Video.sdp 000444 001752 001752 00000000353 13242237367 017304 0 ustar 00rsf rsf 000000 000000 v=0
o=- 49451 3 IN IP4 127.0.0.1
s=Test MPEG Video session
i=Parameters for the session streamed by "testMPEG1or2VideoStreamer"
t=0 0
a=tool:testMPEG1or2VideoStreamer
a=type:broadcast
m=video 8888 RTP/AVP 32
c=IN IP4 239.255.42.42/127
live/testProgs/testMPEG1or2VideoReceiver.cpp 000444 001752 001752 00000010444 13242237367 020767 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that receives a RTP/RTCP multicast MPEG video stream,
// and outputs the resulting MPEG file stream to 'stdout'
// main program
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
// To receive a "source-specific multicast" (SSM) stream, uncomment this:
//#define USE_SSM 1
void afterPlaying(void* clientData); // forward
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
RTPSource* source;
MediaSink* sink;
RTCPInstance* rtcpInstance;
} sessionState;
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create the data sink for 'stdout':
sessionState.sink = FileSink::createNew(*env, "stdout");
// Note: The string "stdout" is handled as a special case.
// A real file name could have been used instead.
// Create 'groupsocks' for RTP and RTCP:
char const* sessionAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: If the session is unicast rather than multicast,
// then replace this string with "0.0.0.0"
#endif
const unsigned short rtpPortNum = 8888;
const unsigned short rtcpPortNum = rtpPortNum+1;
#ifndef USE_SSM
const unsigned char ttl = 1; // low, in case routers don't admin scope
#endif
struct in_addr sessionAddress;
sessionAddress.s_addr = our_inet_addr(sessionAddressStr);
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
#ifdef USE_SSM
char* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
struct in_addr sourceFilterAddress;
sourceFilterAddress.s_addr = our_inet_addr(sourceAddressStr);
Groupsock rtpGroupsock(*env, sessionAddress, sourceFilterAddress, rtpPort);
Groupsock rtcpGroupsock(*env, sessionAddress, sourceFilterAddress, rtcpPort);
rtcpGroupsock.changeDestinationParameters(sourceFilterAddress,0,~0);
// our RTCP "RR"s are sent back using unicast
#else
Groupsock rtpGroupsock(*env, sessionAddress, rtpPort, ttl);
Groupsock rtcpGroupsock(*env, sessionAddress, rtcpPort, ttl);
#endif
// Create the data source: a "MPEG Video RTP source"
sessionState.source = MPEG1or2VideoRTPSource::createNew(*env, &rtpGroupsock);
// Create (and start) a 'RTCP instance' for the RTP source:
const unsigned estimatedSessionBandwidth = 4500; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstance
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
NULL /* we're a client */, sessionState.source);
// Note: This starts RTCP running automatically
// Finally, start receiving the multicast stream:
*env << "Beginning receiving multicast stream...\n";
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done receiving\n";
// End by closing the media:
Medium::close(sessionState.rtcpInstance); // Note: Sends a RTCP BYE
Medium::close(sessionState.sink);
Medium::close(sessionState.source);
}
live/testProgs/testMPEG1or2VideoStreamer.cpp 000444 001752 001752 00000013617 13242237367 021012 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-1 or 2 Video Elementary Stream file,
// and streams it using RTP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
// Uncomment the following if the input file is a MPEG Program Stream
// rather than a MPEG Video Elementary Stream
//#define SOURCE_IS_PROGRAM_STREAM 1
// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
// To set up an internal RTSP server, uncomment the following:
//#define IMPLEMENT_RTSP_SERVER 1
// (Note that this RTSP server works for multicast only)
// To stream *only* MPEG "I" frames (e.g., to reduce network bandwidth),
// change the following "False" to "True":
Boolean iFramesOnly = False;
UsageEnvironment* env;
char const* inputFileName = "test.mpg";
#ifdef SOURCE_IS_PROGRAM_STREAM
MPEG1or2Demux* mpegDemux;
#endif
MediaSource* videoSource;
RTPSink* videoSink;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
char const* destinationAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: This is a multicast address. If you wish to stream using
// unicast instead, then replace this string with the unicast address
// of the (single) destination. (You may also need to make a similar
// change to the receiver program.)
#endif
const unsigned short rtpPortNum = 8888;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 7; // low, in case routers don't admin scope
struct in_addr destinationAddress;
destinationAddress.s_addr = our_inet_addr(destinationAddressStr);
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
#ifdef USE_SSM
rtpGroupsock.multicastSendOnly();
rtcpGroupsock.multicastSendOnly();
#endif
// Create a 'MPEG Video RTP' sink from the RTP 'groupsock':
videoSink = MPEG1or2VideoRTPSink::createNew(*env, &rtpGroupsock);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 4500; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
#ifdef IMPLEMENT_RTSP_SERVER
RTCPInstance* rtcp =
#endif
RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
videoSink, NULL /* we're a server */, isSSM);
// Note: This starts RTCP running automatically
#ifdef IMPLEMENT_RTSP_SERVER
RTSPServer* rtspServer = RTSPServer::createNew(*env);
// Note that this (attempts to) start a server on the default RTSP server
// port: 554. To use a different port number, add it as an extra
// (optional) parameter to the "RTSPServer::createNew()" call above.
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testMPEG1or2VideoStreamer\"",
isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
#endif
// Finally, start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
videoSink->stopPlaying();
Medium::close(videoSource);
#ifdef SOURCE_IS_PROGRAM_STREAM
Medium::close(mpegDemux);
#endif
// Note that this also closes the input file that this source read from.
play();
}
void play() {
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, inputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
FramedSource* videoES;
#ifdef SOURCE_IS_PROGRAM_STREAM
// We must demultiplex a Video Elementary Stream from the input source:
mpegDemux = MPEG1or2Demux::createNew(*env, fileSource);
videoES = mpegDemux->newVideoStream();
#else
// The input source is assumed to already be a Video Elementary Stream:
videoES = fileSource;
#endif
// Create a framer for the Video Elementary Stream:
videoSource
= MPEG1or2VideoStreamFramer::createNew(*env, videoES, iFramesOnly);
// Finally, start playing:
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}
live/testProgs/testMPEG2Transport.sdp 000444 001752 001752 00000000372 13242237367 017611 0 ustar 00rsf rsf 000000 000000 v=0
o=- 49451 3 IN IP4 127.0.0.1
s=Test MPEG-2 Transport Stream session
i=Parameters for the session streamed by "testMPEG2TransportStreamer"
t=0 0
a=tool:testMPEG2TransportStreamer
a=type:broadcast
m=video 1234 RTP/AVP 33
c=IN IP4 239.255.42.42/127
live/testProgs/testMPEG2TransportReceiver.cpp 000444 001752 001752 00000010622 13242237367 021271 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that receives a RTP/RTCP multicast MPEG-2 Transport Stream,
// and outputs the resulting Transport Stream data to 'stdout'
// main program
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
// To receive a "source-specific multicast" (SSM) stream, uncomment this:
//#define USE_SSM 1
void afterPlaying(void* clientData); // forward
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
RTPSource* source;
MediaSink* sink;
RTCPInstance* rtcpInstance;
} sessionState;
UsageEnvironment* env;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create the data sink for 'stdout':
sessionState.sink = FileSink::createNew(*env, "stdout");
// Note: The string "stdout" is handled as a special case.
// A real file name could have been used instead.
// Create 'groupsocks' for RTP and RTCP:
char const* sessionAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: If the session is unicast rather than multicast,
// then replace this string with "0.0.0.0"
#endif
const unsigned short rtpPortNum = 1234;
const unsigned short rtcpPortNum = rtpPortNum+1;
#ifndef USE_SSM
const unsigned char ttl = 1; // low, in case routers don't admin scope
#endif
struct in_addr sessionAddress;
sessionAddress.s_addr = our_inet_addr(sessionAddressStr);
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
#ifdef USE_SSM
char* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
struct in_addr sourceFilterAddress;
sourceFilterAddress.s_addr = our_inet_addr(sourceAddressStr);
Groupsock rtpGroupsock(*env, sessionAddress, sourceFilterAddress, rtpPort);
Groupsock rtcpGroupsock(*env, sessionAddress, sourceFilterAddress, rtcpPort);
rtcpGroupsock.changeDestinationParameters(sourceFilterAddress,0,~0);
// our RTCP "RR"s are sent back using unicast
#else
Groupsock rtpGroupsock(*env, sessionAddress, rtpPort, ttl);
Groupsock rtcpGroupsock(*env, sessionAddress, rtcpPort, ttl);
#endif
// Create the data source: a "MPEG-2 TransportStream RTP source" (which uses a 'simple' RTP payload format):
sessionState.source = SimpleRTPSource::createNew(*env, &rtpGroupsock, 33, 90000, "video/MP2T", 0, False /*no 'M' bit*/);
// Create (and start) a 'RTCP instance' for the RTP source:
const unsigned estimatedSessionBandwidth = 5000; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstance
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
NULL /* we're a client */, sessionState.source);
// Note: This starts RTCP running automatically
// Finally, start receiving the multicast stream:
*env << "Beginning receiving multicast stream...\n";
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done receiving\n";
// End by closing the media:
Medium::close(sessionState.rtcpInstance); // Note: Sends a RTCP BYE
Medium::close(sessionState.sink);
Medium::close(sessionState.source);
}
live/testProgs/testMPEG2TransportStreamer.cpp 000444 001752 001752 00000012751 13242237367 021314 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-2 Transport Stream file,
// and streams it using RTP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
// To set up an internal RTSP server, uncomment the following:
//#define IMPLEMENT_RTSP_SERVER 1
// (Note that this RTSP server works for multicast only)
#define TRANSPORT_PACKET_SIZE 188
#define TRANSPORT_PACKETS_PER_NETWORK_PACKET 7
// The product of these two numbers must be enough to fit within a network packet
UsageEnvironment* env;
char const* inputFileName = "test.ts";
FramedSource* videoSource;
RTPSink* videoSink;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
char const* destinationAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
// Note: This is a multicast address. If you wish to stream using
// unicast instead, then replace this string with the unicast address
// of the (single) destination. (You may also need to make a similar
// change to the receiver program.)
#endif
const unsigned short rtpPortNum = 1234;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 7; // low, in case routers don't admin scope
struct in_addr destinationAddress;
destinationAddress.s_addr = our_inet_addr(destinationAddressStr);
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
#ifdef USE_SSM
rtpGroupsock.multicastSendOnly();
rtcpGroupsock.multicastSendOnly();
#endif
// Create an appropriate 'RTP sink' from the RTP 'groupsock':
videoSink =
SimpleRTPSink::createNew(*env, &rtpGroupsock, 33, 90000, "video", "MP2T",
1, True, False /*no 'M' bit*/);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 5000; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
#ifdef IMPLEMENT_RTSP_SERVER
RTCPInstance* rtcp =
#endif
RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
videoSink, NULL /* we're a server */, isSSM);
// Note: This starts RTCP running automatically
#ifdef IMPLEMENT_RTSP_SERVER
RTSPServer* rtspServer = RTSPServer::createNew(*env);
// Note that this (attempts to) start a server on the default RTSP server
// port: 554. To use a different port number, add it as an extra
// (optional) parameter to the "RTSPServer::createNew()" call above.
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testMPEG2TransportStreamer\"",
isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
#endif
// Finally, start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
videoSink->stopPlaying();
Medium::close(videoSource);
// Note that this also closes the input file that this source read from.
play();
}
void play() {
unsigned const inputDataChunkSize
= TRANSPORT_PACKETS_PER_NETWORK_PACKET*TRANSPORT_PACKET_SIZE;
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, inputFileName, inputDataChunkSize);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
// Create a 'framer' for the input source (to give us proper inter-packet gaps):
videoSource = MPEG2TransportStreamFramer::createNew(*env, fileSource);
// Finally, start playing:
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}
live/testProgs/testMPEG2TransportStreamTrickPlay.cpp 000400 001752 001752 00000012307 13242237367 022575 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A program that tests 'trick mode' operations on a MPEG-2 Transport Stream file,
// by generating a new Transport Stream file that represents the result of the
// 'trick mode' operation (seeking and/or fast forward/reverse play).
// For this to work, there must also be an index file present, in the same directory
// as the Transport Stream file, and with the same name prefix. (The Transport
// Stream file has name suffix ".ts"; the index file has name suffix ".tsx".)
// main program
#include
#include
void afterPlaying(void* clientData); // forward
UsageEnvironment* env;
char const* programName;
void usage() {
*env << "usage: " << programName << " \n";
*env << "\twhere\t ends with \".ts\"\n";
*env << "\t\t is the starting play time in seconds (0 for the start)\n";
*env << "\t\t is a non-zero integer, representing the playing speed (use 1 for normal play; use a negative number for reverse play)\n";
exit(1);
}
int main(int argc, char const** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Parse the command line:
programName = argv[0];
if (argc != 5) usage();
char const* inputFileName = argv[1];
// Check whether the input file name ends with ".ts":
int len = strlen(inputFileName);
if (len < 4 || strcmp(&inputFileName[len-3], ".ts") != 0) {
*env << "ERROR: input file name \"" << inputFileName
<< "\" does not end with \".ts\"\n";
usage();
}
// Parse the and parameters:
float startTime;
if (sscanf(argv[2], "%f", &startTime) != 1 || startTime < 0.0f) usage();
int scale;
if (sscanf(argv[3], "%d", &scale) != 1 || scale == 0) usage();
// Open the input file (as a 'byte stream file source'):
FramedSource* input
= ByteStreamFileSource::createNew(*env, inputFileName, TRANSPORT_PACKET_SIZE);
if (input == NULL) {
*env << "Failed to open input file \"" << inputFileName << "\" (does it exist?)\n";
exit(1);
}
// Check whether the corresponding index file exists.
// The index file name is the same as the input file name, except with suffix ".tsx":
char* indexFileName = new char[len+2]; // allow for trailing x\0
sprintf(indexFileName, "%sx", inputFileName);
MPEG2TransportStreamIndexFile* indexFile
= MPEG2TransportStreamIndexFile::createNew(*env, indexFileName);
if (indexFile == NULL) {
*env << "Failed to open index file \"" << indexFileName << "\" (does it exist?)\n";
exit(1);
}
// Create a filter that generates trick mode data from the input and index files:
MPEG2TransportStreamTrickModeFilter* trickModeFilter
= MPEG2TransportStreamTrickModeFilter::createNew(*env, input, indexFile, scale);
if (startTime > 0.0f) {
// Seek the input Transport Stream and Index files to the specified start time:
unsigned long tsRecordNumber, indexRecordNumber;
indexFile->lookupTSPacketNumFromNPT(startTime, tsRecordNumber, indexRecordNumber);
if (!trickModeFilter->seekTo(tsRecordNumber, indexRecordNumber)) { // TARFU!
*env << "Failed to seek trick mode filter to ts #" << (unsigned)tsRecordNumber
<< ", ix #" << (unsigned)indexRecordNumber
<< "(for time " << startTime << ")\n";
exit(1);
}
}
// Generate a new Transport Stream from the Trick Mode filter:
MPEG2TransportStreamFromESSource* newTransportStream
= MPEG2TransportStreamFromESSource::createNew(*env);
newTransportStream->addNewVideoSource(trickModeFilter, indexFile->mpegVersion());
// Open the output file (for writing), as a 'file sink':
char const* outputFileName = argv[4];
MediaSink* output = FileSink::createNew(*env, outputFileName);
if (output == NULL) {
*env << "Failed to open output file \"" << outputFileName << "\"\n";
exit(1);
}
// Start playing, to generate the output file:
*env << "Writing output file \"" << outputFileName
<< "\" (start time " << startTime
<< ", scale " << scale
<< ")...";
output->startPlaying(*newTransportStream, afterPlaying, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done\n";
exit(0);
}
live/testProgs/testMPEG4VideoStreamer.cpp 000444 001752 001752 00000010663 13242237367 020370 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a MPEG-4 Video Elementary Stream file,
// and streams it using RTP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
UsageEnvironment* env;
char const* inputFileName = "test.m4e";
MPEG4VideoStreamFramer* videoSource;
RTPSink* videoSink;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer"
// test program - not this test program - as a model.
const unsigned short rtpPortNum = 18888;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 255;
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
rtpGroupsock.multicastSendOnly(); // we're a SSM source
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
rtcpGroupsock.multicastSendOnly(); // we're a SSM source
// Create a 'MPEG-4 Video RTP' sink from the RTP 'groupsock':
videoSink = MPEG4ESVideoRTPSink::createNew(*env, &rtpGroupsock, 96);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 500; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
RTCPInstance* rtcp
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
videoSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testMPEG4VideoStreamer\"",
True /*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
videoSink->stopPlaying();
Medium::close(videoSource);
// Note that this also closes the input file that this source read from.
// Start playing once again:
play();
}
void play() {
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, inputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
FramedSource* videoES = fileSource;
// Create a framer for the Video Elementary Stream:
videoSource = MPEG4VideoStreamFramer::createNew(*env, videoES);
// Finally, start playing:
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}
live/testProgs/testOnDemandRTSPServer.cpp 000444 001752 001752 00000042336 13242237367 020451 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that demonstrates how to stream - via unicast RTP
// - various kinds of file on demand, using a built-in RTSP server.
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
UsageEnvironment* env;
// To make the second and subsequent client for each stream reuse the same
// input stream as the first client (rather than playing the file from the
// start for each client), change the following "False" to "True":
Boolean reuseFirstSource = False;
// To stream *only* MPEG-1 or 2 video "I" frames
// (e.g., to reduce network bandwidth),
// change the following "False" to "True":
Boolean iFramesOnly = False;
static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
char const* streamName, char const* inputFileName); // fwd
static char newDemuxWatchVariable;
static MatroskaFileServerDemux* matroskaDemux;
static void onMatroskaDemuxCreation(MatroskaFileServerDemux* newDemux, void* /*clientData*/) {
matroskaDemux = newDemux;
newDemuxWatchVariable = 1;
}
static OggFileServerDemux* oggDemux;
static void onOggDemuxCreation(OggFileServerDemux* newDemux, void* /*clientData*/) {
oggDemux = newDemux;
newDemuxWatchVariable = 1;
}
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
// To implement client access control to the RTSP server, do the following:
authDB = new UserAuthenticationDatabase;
authDB->addUserRecord("username1", "password1"); // replace these with real strings
// Repeat the above with each , that you wish to allow
// access to the server.
#endif
// Create the RTSP server:
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
char const* descriptionString
= "Session streamed by \"testOnDemandRTSPServer\"";
// Set up each of the possible streams that can be served by the
// RTSP server. Each such stream is implemented using a
// "ServerMediaSession" object, plus one or more
// "ServerMediaSubsession" objects for each audio/video substream.
// A MPEG-4 video elementary stream:
{
char const* streamName = "mpeg4ESVideoTest";
char const* inputFileName = "test.m4e";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(MPEG4VideoFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A H.264 video elementary stream:
{
char const* streamName = "h264ESVideoTest";
char const* inputFileName = "test.264";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(H264VideoFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A H.265 video elementary stream:
{
char const* streamName = "h265ESVideoTest";
char const* inputFileName = "test.265";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(H265VideoFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A MPEG-1 or 2 audio+video program stream:
{
char const* streamName = "mpeg1or2AudioVideoTest";
char const* inputFileName = "test.mpg";
// NOTE: This *must* be a Program Stream; not an Elementary Stream
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
MPEG1or2FileServerDemux* demux
= MPEG1or2FileServerDemux::createNew(*env, inputFileName, reuseFirstSource);
sms->addSubsession(demux->newVideoServerMediaSubsession(iFramesOnly));
sms->addSubsession(demux->newAudioServerMediaSubsession());
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A MPEG-1 or 2 video elementary stream:
{
char const* streamName = "mpeg1or2ESVideoTest";
char const* inputFileName = "testv.mpg";
// NOTE: This *must* be a Video Elementary Stream; not a Program Stream
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(MPEG1or2VideoFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource, iFramesOnly));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A MP3 audio stream (actually, any MPEG-1 or 2 audio file will work):
// To stream using 'ADUs' rather than raw MP3 frames, uncomment the following:
//#define STREAM_USING_ADUS 1
// To also reorder ADUs before streaming, uncomment the following:
//#define INTERLEAVE_ADUS 1
// (For more information about ADUs and interleaving,
// see )
{
char const* streamName = "mp3AudioTest";
char const* inputFileName = "test.mp3";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
Boolean useADUs = False;
Interleaving* interleaving = NULL;
#ifdef STREAM_USING_ADUS
useADUs = True;
#ifdef INTERLEAVE_ADUS
unsigned char interleaveCycle[] = {0,2,1,3}; // or choose your own...
unsigned const interleaveCycleSize
= (sizeof interleaveCycle)/(sizeof (unsigned char));
interleaving = new Interleaving(interleaveCycleSize, interleaveCycle);
#endif
#endif
sms->addSubsession(MP3AudioFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource,
useADUs, interleaving));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A WAV audio stream:
{
char const* streamName = "wavAudioTest";
char const* inputFileName = "test.wav";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
// To convert 16-bit PCM data to 8-bit u-law, prior to streaming,
// change the following to True:
Boolean convertToULaw = False;
sms->addSubsession(WAVAudioFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource, convertToULaw));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// An AMR audio stream:
{
char const* streamName = "amrAudioTest";
char const* inputFileName = "test.amr";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(AMRAudioFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A 'VOB' file (e.g., from an unencrypted DVD):
{
char const* streamName = "vobTest";
char const* inputFileName = "test.vob";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
// Note: VOB files are MPEG-2 Program Stream files, but using AC-3 audio
MPEG1or2FileServerDemux* demux
= MPEG1or2FileServerDemux::createNew(*env, inputFileName, reuseFirstSource);
sms->addSubsession(demux->newVideoServerMediaSubsession(iFramesOnly));
sms->addSubsession(demux->newAC3AudioServerMediaSubsession());
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A MPEG-2 Transport Stream:
{
char const* streamName = "mpeg2TransportStreamTest";
char const* inputFileName = "test.ts";
char const* indexFileName = "test.tsx";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(MPEG2TransportFileServerMediaSubsession
::createNew(*env, inputFileName, indexFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// An AAC audio stream (ADTS-format file):
{
char const* streamName = "aacAudioTest";
char const* inputFileName = "test.aac";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(ADTSAudioFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A DV video stream:
{
// First, make sure that the RTPSinks' buffers will be large enough to handle the huge size of DV frames (as big as 288000).
OutPacketBuffer::maxSize = 300000;
char const* streamName = "dvVideoTest";
char const* inputFileName = "test.dv";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(DVVideoFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A AC3 video elementary stream:
{
char const* streamName = "ac3AudioTest";
char const* inputFileName = "test.ac3";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(AC3AudioFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
rtspServer->addServerMediaSession(sms);
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A Matroska ('.mkv') file, with video+audio+subtitle streams:
{
char const* streamName = "matroskaFileTest";
char const* inputFileName = "test.mkv";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
newDemuxWatchVariable = 0;
MatroskaFileServerDemux::createNew(*env, inputFileName, onMatroskaDemuxCreation, NULL);
env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
Boolean sessionHasTracks = False;
ServerMediaSubsession* smss;
while ((smss = matroskaDemux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
sessionHasTracks = True;
}
if (sessionHasTracks) {
rtspServer->addServerMediaSession(sms);
}
// otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A WebM ('.webm') file, with video(VP8)+audio(Vorbis) streams:
// (Note: ".webm' files are special types of Matroska files, so we use the same code as the Matroska ('.mkv') file code above.)
{
char const* streamName = "webmFileTest";
char const* inputFileName = "test.webm";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
newDemuxWatchVariable = 0;
MatroskaFileServerDemux::createNew(*env, inputFileName, onMatroskaDemuxCreation, NULL);
env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
Boolean sessionHasTracks = False;
ServerMediaSubsession* smss;
while ((smss = matroskaDemux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
sessionHasTracks = True;
}
if (sessionHasTracks) {
rtspServer->addServerMediaSession(sms);
}
// otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
announceStream(rtspServer, sms, streamName, inputFileName);
}
// An Ogg ('.ogg') file, with video and/or audio streams:
{
char const* streamName = "oggFileTest";
char const* inputFileName = "test.ogg";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
newDemuxWatchVariable = 0;
OggFileServerDemux::createNew(*env, inputFileName, onOggDemuxCreation, NULL);
env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
Boolean sessionHasTracks = False;
ServerMediaSubsession* smss;
while ((smss = oggDemux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
sessionHasTracks = True;
}
if (sessionHasTracks) {
rtspServer->addServerMediaSession(sms);
}
// otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
announceStream(rtspServer, sms, streamName, inputFileName);
}
// An Opus ('.opus') audio file:
// (Note: ".opus' files are special types of Ogg files, so we use the same code as the Ogg ('.ogg') file code above.)
{
char const* streamName = "opusFileTest";
char const* inputFileName = "test.opus";
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
newDemuxWatchVariable = 0;
OggFileServerDemux::createNew(*env, inputFileName, onOggDemuxCreation, NULL);
env->taskScheduler().doEventLoop(&newDemuxWatchVariable);
Boolean sessionHasTracks = False;
ServerMediaSubsession* smss;
while ((smss = oggDemux->newServerMediaSubsession()) != NULL) {
sms->addSubsession(smss);
sessionHasTracks = True;
}
if (sessionHasTracks) {
rtspServer->addServerMediaSession(sms);
}
// otherwise, because the stream has no tracks, we don't add a ServerMediaSession to the server.
announceStream(rtspServer, sms, streamName, inputFileName);
}
// A MPEG-2 Transport Stream, coming from a live UDP (raw-UDP or RTP/UDP) source:
{
char const* streamName = "mpeg2TransportStreamFromUDPSourceTest";
char const* inputAddressStr = "239.255.42.42";
// This causes the server to take its input from the stream sent by the "testMPEG2TransportStreamer" demo application.
// (Note: If the input UDP source is unicast rather than multicast, then change this to NULL.)
portNumBits const inputPortNum = 1234;
// This causes the server to take its input from the stream sent by the "testMPEG2TransportStreamer" demo application.
Boolean const inputStreamIsRawUDP = False;
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, streamName, streamName,
descriptionString);
sms->addSubsession(MPEG2TransportUDPServerMediaSubsession
::createNew(*env, inputAddressStr, inputPortNum, inputStreamIsRawUDP));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "\n\"" << streamName << "\" stream, from a UDP Transport Stream input source \n\t(";
if (inputAddressStr != NULL) {
*env << "IP multicast address " << inputAddressStr << ",";
} else {
*env << "unicast;";
}
*env << " port " << inputPortNum << ")\n";
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
}
// Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
// Try first with the default HTTP port (80), and then with the alternative HTTP
// port numbers (8000 and 8080).
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
*env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling.)\n";
} else {
*env << "\n(RTSP-over-HTTP tunneling is not available.)\n";
}
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,
char const* streamName, char const* inputFileName) {
char* url = rtspServer->rtspURL(sms);
UsageEnvironment& env = rtspServer->envir();
env << "\n\"" << streamName << "\" stream, from the file \""
<< inputFileName << "\"\n";
env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
}
live/testProgs/testRelay.cpp 000444 001752 001752 00000006404 13242237367 016134 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that receives a UDP multicast stream
// and retransmits it to another (multicast or unicast) address & port
// main program
#include
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
UsageEnvironment* env;
// To receive a "source-specific multicast" (SSM) stream, uncomment this:
//#define USE_SSM 1
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create a 'groupsock' for the input multicast group,port:
char const* inputAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
#endif
struct in_addr inputAddress;
inputAddress.s_addr = our_inet_addr(inputAddressStr);
Port const inputPort(8888);
unsigned char const inputTTL = 0; // we're only reading from this mcast group
#ifdef USE_SSM
char* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
struct in_addr sourceFilterAddress;
sourceFilterAddress.s_addr = our_inet_addr(sourceAddressStr);
Groupsock inputGroupsock(*env, inputAddress, sourceFilterAddress, inputPort);
#else
Groupsock inputGroupsock(*env, inputAddress, inputPort, inputTTL);
#endif
// Then create a liveMedia 'source' object, encapsulating this groupsock:
FramedSource* source = BasicUDPSource::createNew(*env, &inputGroupsock);
// Create a 'groupsock' for the destination address and port:
char const* outputAddressStr = "239.255.43.43"; // this could also be unicast
// Note: You may change "outputAddressStr" to use a different multicast
// (or unicast address), but do *not* change it to use the same multicast
// address as "inputAddressStr".
struct in_addr outputAddress;
outputAddress.s_addr = our_inet_addr(outputAddressStr);
Port const outputPort(4444);
unsigned char const outputTTL = 255;
Groupsock outputGroupsock(*env, outputAddress, outputPort, outputTTL);
// Then create a liveMedia 'sink' object, encapsulating this groupsock:
unsigned const maxPacketSize = 65536; // allow for large UDP packets
MediaSink* sink = BasicUDPSink::createNew(*env, &outputGroupsock, maxPacketSize);
// Now, start playing, feeding the sink object from the source:
sink->startPlaying(*source, NULL, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
live/testProgs/testReplicator.cpp 000444 001752 001752 00000011461 13242237367 017163 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A demo application that receives a UDP multicast stream, replicates it (using the "StreamReplicator" class),
// and retransmits one replica stream to another (multicast or unicast) address & port,
// and writes the other replica stream to a file.
//
// main program
#include
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
UsageEnvironment* env;
// To receive a "source-specific multicast" (SSM) stream, uncomment this:
//#define USE_SSM 1
void startReplicaUDPSink(StreamReplicator* replicator, char const* outputAddressStr, portNumBits outputPortNum); // forward
void startReplicaFileSink(StreamReplicator* replicator, char const* outputFileName); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create a 'groupsock' for the input multicast group,port:
char const* inputAddressStr
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
#endif
struct in_addr inputAddress;
inputAddress.s_addr = our_inet_addr(inputAddressStr);
Port const inputPort(8888);
unsigned char const inputTTL = 0; // we're only reading from this mcast group
#ifdef USE_SSM
char* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
struct in_addr sourceFilterAddress;
sourceFilterAddress.s_addr = our_inet_addr(sourceAddressStr);
Groupsock inputGroupsock(*env, inputAddress, sourceFilterAddress, inputPort);
#else
Groupsock inputGroupsock(*env, inputAddress, inputPort, inputTTL);
#endif
// Then create a liveMedia 'source' object, encapsulating this groupsock:
FramedSource* source = BasicUDPSource::createNew(*env, &inputGroupsock);
// And feed this into a 'stream replicator':
StreamReplicator* replicator = StreamReplicator::createNew(*env, source);
// Then create a network (UDP) 'sink' object to receive a replica of the input stream, and start it.
// If you wish, you can duplicate this line - with different network addresses and ports - to create multiple output UDP streams:
startReplicaUDPSink(replicator, "239.255.43.43", 4444);
// Then create a file 'sink' object to receive a replica of the input stream, and start it.
// If you wish, you can duplicate this line - with a different file name - to create multiple output files:
startReplicaFileSink(replicator, "test.out");
// Finally, enter the 'event loop' (which is where most of the 'real work' in a LIVE555-based application gets done):
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void startReplicaUDPSink(StreamReplicator* replicator, char const* outputAddressStr, portNumBits outputPortNum) {
// Begin by creating an input stream from our replicator:
FramedSource* source = replicator->createStreamReplica();
// Create a 'groupsock' for the destination address and port:
struct in_addr outputAddress;
outputAddress.s_addr = our_inet_addr(outputAddressStr);
Port const outputPort(outputPortNum);
unsigned char const outputTTL = 255;
Groupsock* outputGroupsock = new Groupsock(*env, outputAddress, outputPort, outputTTL);
// Then create a liveMedia 'sink' object, encapsulating this groupsock:
unsigned const maxPacketSize = 65536; // allow for large UDP packets
MediaSink* sink = BasicUDPSink::createNew(*env, outputGroupsock, maxPacketSize);
// Now, start playing, feeding the sink object from the source:
sink->startPlaying(*source, NULL, NULL);
}
void startReplicaFileSink(StreamReplicator* replicator, char const* outputFileName) {
// Begin by creating an input stream from our replicator:
FramedSource* source = replicator->createStreamReplica();
// Then create a 'file sink' object to receive thie replica stream:
MediaSink* sink = FileSink::createNew(*env, outputFileName);
// Now, start playing, feeding the sink object from the source:
sink->startPlaying(*source, NULL, NULL);
}
live/testProgs/testRTSPClient.cpp 000444 001752 001752 00000053707 13242237367 017017 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A demo application, showing how to create and run a RTSP client (that can potentially receive multiple streams concurrently).
//
// NOTE: This code - although it builds a running application - is intended only to illustrate how to develop your own RTSP
// client application. For a full-featured RTSP client application - with much more functionality, and many options - see
// "openRTSP": http://www.live555.com/openRTSP/
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
// Forward function definitions:
// RTSP 'response handlers':
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString);
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString);
void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString);
// Other event handler functions:
void subsessionAfterPlaying(void* clientData); // called when a stream's subsession (e.g., audio or video substream) ends
void subsessionByeHandler(void* clientData); // called when a RTCP "BYE" is received for a subsession
void streamTimerHandler(void* clientData);
// called at the end of a stream's expected duration (if the stream has not already signaled its end using a RTCP "BYE")
// The main streaming routine (for each "rtsp://" URL):
void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL);
// Used to iterate through each stream's 'subsessions', setting up each one:
void setupNextSubsession(RTSPClient* rtspClient);
// Used to shut down and close a stream (including its "RTSPClient" object):
void shutdownStream(RTSPClient* rtspClient, int exitCode = 1);
// A function that outputs a string that identifies each stream (for debugging output). Modify this if you wish:
UsageEnvironment& operator<<(UsageEnvironment& env, const RTSPClient& rtspClient) {
return env << "[URL:\"" << rtspClient.url() << "\"]: ";
}
// A function that outputs a string that identifies each subsession (for debugging output). Modify this if you wish:
UsageEnvironment& operator<<(UsageEnvironment& env, const MediaSubsession& subsession) {
return env << subsession.mediumName() << "/" << subsession.codecName();
}
void usage(UsageEnvironment& env, char const* progName) {
env << "Usage: " << progName << " ... \n";
env << "\t(where each is a \"rtsp://\" URL)\n";
}
char eventLoopWatchVariable = 0;
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
// We need at least one "rtsp://" URL argument:
if (argc < 2) {
usage(*env, argv[0]);
return 1;
}
// There are argc-1 URLs: argv[1] through argv[argc-1]. Open and start streaming each one:
for (int i = 1; i <= argc-1; ++i) {
openURL(*env, argv[0], argv[i]);
}
// All subsequent activity takes place within the event loop:
env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
// This function call does not return, unless, at some point in time, "eventLoopWatchVariable" gets set to something non-zero.
return 0;
// If you choose to continue the application past this point (i.e., if you comment out the "return 0;" statement above),
// and if you don't intend to do anything more with the "TaskScheduler" and "UsageEnvironment" objects,
// then you can also reclaim the (small) memory used by these objects by uncommenting the following code:
/*
env->reclaim(); env = NULL;
delete scheduler; scheduler = NULL;
*/
}
// Define a class to hold per-stream state that we maintain throughout each stream's lifetime:
class StreamClientState {
public:
StreamClientState();
virtual ~StreamClientState();
public:
MediaSubsessionIterator* iter;
MediaSession* session;
MediaSubsession* subsession;
TaskToken streamTimerTask;
double duration;
};
// If you're streaming just a single stream (i.e., just from a single URL, once), then you can define and use just a single
// "StreamClientState" structure, as a global variable in your application. However, because - in this demo application - we're
// showing how to play multiple streams, concurrently, we can't do that. Instead, we have to have a separate "StreamClientState"
// structure for each "RTSPClient". To do this, we subclass "RTSPClient", and add a "StreamClientState" field to the subclass:
class ourRTSPClient: public RTSPClient {
public:
static ourRTSPClient* createNew(UsageEnvironment& env, char const* rtspURL,
int verbosityLevel = 0,
char const* applicationName = NULL,
portNumBits tunnelOverHTTPPortNum = 0);
protected:
ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum);
// called only by createNew();
virtual ~ourRTSPClient();
public:
StreamClientState scs;
};
// Define a data sink (a subclass of "MediaSink") to receive the data for each subsession (i.e., each audio or video 'substream').
// In practice, this might be a class (or a chain of classes) that decodes and then renders the incoming audio or video.
// Or it might be a "FileSink", for outputting the received data into a file (as is done by the "openRTSP" application).
// In this example code, however, we define a simple 'dummy' sink that receives incoming data, but does nothing with it.
class DummySink: public MediaSink {
public:
static DummySink* createNew(UsageEnvironment& env,
MediaSubsession& subsession, // identifies the kind of data that's being received
char const* streamId = NULL); // identifies the stream itself (optional)
private:
DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId);
// called only by "createNew()"
virtual ~DummySink();
static void afterGettingFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
struct timeval presentationTime,
unsigned durationInMicroseconds);
void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned durationInMicroseconds);
private:
// redefined virtual functions:
virtual Boolean continuePlaying();
private:
u_int8_t* fReceiveBuffer;
MediaSubsession& fSubsession;
char* fStreamId;
};
#define RTSP_CLIENT_VERBOSITY_LEVEL 1 // by default, print verbose output from each "RTSPClient"
static unsigned rtspClientCount = 0; // Counts how many streams (i.e., "RTSPClient"s) are currently in use.
void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL) {
// Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish
// to receive (even if more than stream uses the same "rtsp://" URL).
RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
if (rtspClient == NULL) {
env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg() << "\n";
return;
}
++rtspClientCount;
// Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream.
// Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response.
// Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop:
rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
}
// Implementation of the RTSP 'response handlers':
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n";
delete[] resultString;
break;
}
char* const sdpDescription = resultString;
env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n";
// Create a media session object from this SDP description:
scs.session = MediaSession::createNew(env, sdpDescription);
delete[] sdpDescription; // because we don't need it anymore
if (scs.session == NULL) {
env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
break;
} else if (!scs.session->hasSubsessions()) {
env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
break;
}
// Then, create and set up our data source objects for the session. We do this by iterating over the session's 'subsessions',
// calling "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, on each one.
// (Each 'subsession' will have its own data source.)
scs.iter = new MediaSubsessionIterator(*scs.session);
setupNextSubsession(rtspClient);
return;
} while (0);
// An unrecoverable error occurred with this stream.
shutdownStream(rtspClient);
}
// By default, we request that the server stream its data using RTP/UDP.
// If, instead, you want to request that the server stream via RTP-over-TCP, change the following to True:
#define REQUEST_STREAMING_OVER_TCP False
void setupNextSubsession(RTSPClient* rtspClient) {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
scs.subsession = scs.iter->next();
if (scs.subsession != NULL) {
if (!scs.subsession->initiate()) {
env << *rtspClient << "Failed to initiate the \"" << *scs.subsession << "\" subsession: " << env.getResultMsg() << "\n";
setupNextSubsession(rtspClient); // give up on this subsession; go to the next one
} else {
env << *rtspClient << "Initiated the \"" << *scs.subsession << "\" subsession (";
if (scs.subsession->rtcpIsMuxed()) {
env << "client port " << scs.subsession->clientPortNum();
} else {
env << "client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1;
}
env << ")\n";
// Continue setting up this subsession, by sending a RTSP "SETUP" command:
rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP, False, REQUEST_STREAMING_OVER_TCP);
}
return;
}
// We've finished setting up all of the subsessions. Now, send a RTSP "PLAY" command to start the streaming:
if (scs.session->absStartTime() != NULL) {
// Special case: The stream is indexed by 'absolute' time, so send an appropriate "PLAY" command:
rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY, scs.session->absStartTime(), scs.session->absEndTime());
} else {
scs.duration = scs.session->playEndTime() - scs.session->playStartTime();
rtspClient->sendPlayCommand(*scs.session, continueAfterPLAY);
}
}
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to set up the \"" << *scs.subsession << "\" subsession: " << resultString << "\n";
break;
}
env << *rtspClient << "Set up the \"" << *scs.subsession << "\" subsession (";
if (scs.subsession->rtcpIsMuxed()) {
env << "client port " << scs.subsession->clientPortNum();
} else {
env << "client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1;
}
env << ")\n";
// Having successfully setup the subsession, create a data sink for it, and call "startPlaying()" on it.
// (This will prepare the data sink to receive data; the actual flow of data from the client won't start happening until later,
// after we've sent a RTSP "PLAY" command.)
scs.subsession->sink = DummySink::createNew(env, *scs.subsession, rtspClient->url());
// perhaps use your own custom "MediaSink" subclass instead
if (scs.subsession->sink == NULL) {
env << *rtspClient << "Failed to create a data sink for the \"" << *scs.subsession
<< "\" subsession: " << env.getResultMsg() << "\n";
break;
}
env << *rtspClient << "Created a data sink for the \"" << *scs.subsession << "\" subsession\n";
scs.subsession->miscPtr = rtspClient; // a hack to let subsession handler functions get the "RTSPClient" from the subsession
scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
subsessionAfterPlaying, scs.subsession);
// Also set a handler to be called if a RTCP "BYE" arrives for this subsession:
if (scs.subsession->rtcpInstance() != NULL) {
scs.subsession->rtcpInstance()->setByeHandler(subsessionByeHandler, scs.subsession);
}
} while (0);
delete[] resultString;
// Set up the next subsession, if any:
setupNextSubsession(rtspClient);
}
void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString) {
Boolean success = False;
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to start playing session: " << resultString << "\n";
break;
}
// Set a timer to be handled at the end of the stream's expected duration (if the stream does not already signal its end
// using a RTCP "BYE"). This is optional. If, instead, you want to keep the stream active - e.g., so you can later
// 'seek' back within it and do another RTSP "PLAY" - then you can omit this code.
// (Alternatively, if you don't want to receive the entire stream, you could set this timer for some shorter value.)
if (scs.duration > 0) {
unsigned const delaySlop = 2; // number of seconds extra to delay, after the stream's expected duration. (This is optional.)
scs.duration += delaySlop;
unsigned uSecsToDelay = (unsigned)(scs.duration*1000000);
scs.streamTimerTask = env.taskScheduler().scheduleDelayedTask(uSecsToDelay, (TaskFunc*)streamTimerHandler, rtspClient);
}
env << *rtspClient << "Started playing session";
if (scs.duration > 0) {
env << " (for up to " << scs.duration << " seconds)";
}
env << "...\n";
success = True;
} while (0);
delete[] resultString;
if (!success) {
// An unrecoverable error occurred with this stream.
shutdownStream(rtspClient);
}
}
// Implementation of the other event handlers:
void subsessionAfterPlaying(void* clientData) {
MediaSubsession* subsession = (MediaSubsession*)clientData;
RTSPClient* rtspClient = (RTSPClient*)(subsession->miscPtr);
// Begin by closing this subsession's stream:
Medium::close(subsession->sink);
subsession->sink = NULL;
// Next, check whether *all* subsessions' streams have now been closed:
MediaSession& session = subsession->parentSession();
MediaSubsessionIterator iter(session);
while ((subsession = iter.next()) != NULL) {
if (subsession->sink != NULL) return; // this subsession is still active
}
// All subsessions' streams have now been closed, so shutdown the client:
shutdownStream(rtspClient);
}
void subsessionByeHandler(void* clientData) {
MediaSubsession* subsession = (MediaSubsession*)clientData;
RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
UsageEnvironment& env = rtspClient->envir(); // alias
env << *rtspClient << "Received RTCP \"BYE\" on \"" << *subsession << "\" subsession\n";
// Now act as if the subsession had closed:
subsessionAfterPlaying(subsession);
}
void streamTimerHandler(void* clientData) {
ourRTSPClient* rtspClient = (ourRTSPClient*)clientData;
StreamClientState& scs = rtspClient->scs; // alias
scs.streamTimerTask = NULL;
// Shut down the stream:
shutdownStream(rtspClient);
}
void shutdownStream(RTSPClient* rtspClient, int exitCode) {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
// First, check whether any subsessions have still to be closed:
if (scs.session != NULL) {
Boolean someSubsessionsWereActive = False;
MediaSubsessionIterator iter(*scs.session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
if (subsession->sink != NULL) {
Medium::close(subsession->sink);
subsession->sink = NULL;
if (subsession->rtcpInstance() != NULL) {
subsession->rtcpInstance()->setByeHandler(NULL, NULL); // in case the server sends a RTCP "BYE" while handling "TEARDOWN"
}
someSubsessionsWereActive = True;
}
}
if (someSubsessionsWereActive) {
// Send a RTSP "TEARDOWN" command, to tell the server to shutdown the stream.
// Don't bother handling the response to the "TEARDOWN".
rtspClient->sendTeardownCommand(*scs.session, NULL);
}
}
env << *rtspClient << "Closing the stream.\n";
Medium::close(rtspClient);
// Note that this will also cause this stream's "StreamClientState" structure to get reclaimed.
if (--rtspClientCount == 0) {
// The final stream has ended, so exit the application now.
// (Of course, if you're embedding this code into your own application, you might want to comment this out,
// and replace it with "eventLoopWatchVariable = 1;", so that we leave the LIVE555 event loop, and continue running "main()".)
exit(exitCode);
}
}
// Implementation of "ourRTSPClient":
ourRTSPClient* ourRTSPClient::createNew(UsageEnvironment& env, char const* rtspURL,
int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum) {
return new ourRTSPClient(env, rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum);
}
ourRTSPClient::ourRTSPClient(UsageEnvironment& env, char const* rtspURL,
int verbosityLevel, char const* applicationName, portNumBits tunnelOverHTTPPortNum)
: RTSPClient(env,rtspURL, verbosityLevel, applicationName, tunnelOverHTTPPortNum, -1) {
}
ourRTSPClient::~ourRTSPClient() {
}
// Implementation of "StreamClientState":
StreamClientState::StreamClientState()
: iter(NULL), session(NULL), subsession(NULL), streamTimerTask(NULL), duration(0.0) {
}
StreamClientState::~StreamClientState() {
delete iter;
if (session != NULL) {
// We also need to delete "session", and unschedule "streamTimerTask" (if set)
UsageEnvironment& env = session->envir(); // alias
env.taskScheduler().unscheduleDelayedTask(streamTimerTask);
Medium::close(session);
}
}
// Implementation of "DummySink":
// Even though we're not going to be doing anything with the incoming data, we still need to receive it.
// Define the size of the buffer that we'll use:
#define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000
DummySink* DummySink::createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) {
return new DummySink(env, subsession, streamId);
}
DummySink::DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId)
: MediaSink(env),
fSubsession(subsession) {
fStreamId = strDup(streamId);
fReceiveBuffer = new u_int8_t[DUMMY_SINK_RECEIVE_BUFFER_SIZE];
}
DummySink::~DummySink() {
delete[] fReceiveBuffer;
delete[] fStreamId;
}
void DummySink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned durationInMicroseconds) {
DummySink* sink = (DummySink*)clientData;
sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
}
// If you don't want to see debugging output for each received frame, then comment out the following line:
#define DEBUG_PRINT_EACH_RECEIVED_FRAME 1
void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {
// We've just received a frame of data. (Optionally) print out information about it:
#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";
envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";
if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)";
char uSecsStr[6+1]; // used to output the 'microseconds' part of the presentation time
sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);
envir() << ".\tPresentation time: " << (int)presentationTime.tv_sec << "." << uSecsStr;
if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
envir() << "!"; // mark the debugging output to indicate that this presentation time is not RTCP-synchronized
}
#ifdef DEBUG_PRINT_NPT
envir() << "\tNPT: " << fSubsession.getNormalPlayTime(presentationTime);
#endif
envir() << "\n";
#endif
// Then continue, to request the next frame of data:
continuePlaying();
}
Boolean DummySink::continuePlaying() {
if (fSource == NULL) return False; // sanity check (should not happen)
// Request the next frame of data from our input source. "afterGettingFrame()" will get called later, when it arrives:
fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
afterGettingFrame, this,
onSourceClosure, this);
return True;
}
live/testProgs/testWAVAudioStreamer.cpp 000400 001752 001752 00000024143 13242237367 020172 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that streams a WAV audio file via RTP/RTCP
// main program
#include "liveMedia.hh"
#include "GroupsockHelper.hh"
#include "BasicUsageEnvironment.hh"
// To convert 16-bit samples to 8-bit u-law ("u" is the Greek letter "mu")
// encoding, before streaming, uncomment the following line:
//#define CONVERT_TO_ULAW 1
UsageEnvironment* env;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warnings
}
char const* inputFileName = "test.wav";
void afterPlaying(void* clientData); // forward
// A structure to hold the state of the current session.
// It is used in the "afterPlaying()" function to clean up the session.
struct sessionState_t {
FramedSource* source;
RTPSink* sink;
RTCPInstance* rtcpInstance;
Groupsock* rtpGroupsock;
Groupsock* rtcpGroupsock;
RTSPServer* rtspServer;
} sessionState;
void play() {
// Open the file as a 'WAV' file:
WAVAudioFileSource* wavSource = WAVAudioFileSource::createNew(*env, inputFileName);
if (wavSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a WAV audio file source: "
<< env->getResultMsg() << "\n";
exit(1);
}
// Get attributes of the audio source:
unsigned char audioFormat = wavSource->getAudioFormat();
unsigned char const bitsPerSample = wavSource->bitsPerSample();
// We handle only 4,8,16,20,24 bits-per-sample audio:
if (bitsPerSample%4 != 0 || bitsPerSample < 4 || bitsPerSample > 24 || bitsPerSample == 12) {
*env << "The input file contains " << bitsPerSample << " bit-per-sample audio, which we don't handle\n";
exit(1);
}
unsigned const samplingFrequency = wavSource->samplingFrequency();
unsigned char const numChannels = wavSource->numChannels();
unsigned bitsPerSecond = samplingFrequency*bitsPerSample*numChannels;
*env << "Audio source parameters:\n\t" << samplingFrequency << " Hz, ";
*env << bitsPerSample << " bits-per-sample, ";
*env << numChannels << " channels => ";
*env << bitsPerSecond << " bits-per-second\n";
char const* mimeType;
unsigned char payloadFormatCode = 96; // by default, unless a static RTP payload type can be used
// Add in any filter necessary to transform the data prior to streaming.
// (This is where any audio compression would get added.)
sessionState.source = wavSource; // by default
if (audioFormat == WA_PCM) {
if (bitsPerSample == 16) {
// Note that samples in the WAV audio file are in little-endian order.
#ifdef CONVERT_TO_ULAW
// Add a filter that converts from raw 16-bit PCM audio (in little-endian order) to 8-bit u-law audio:
sessionState.source = uLawFromPCMAudioSource::createNew(*env, wavSource, 1/*little-endian*/);
if (sessionState.source == NULL) {
*env << "Unable to create a u-law filter from the PCM audio source: " << env->getResultMsg() << "\n";
exit(1);
}
bitsPerSecond /= 2;
*env << "Converting to 8-bit u-law audio for streaming => " << bitsPerSecond << " bits-per-second\n";
mimeType = "PCMU";
if (samplingFrequency == 8000 && numChannels == 1) {
payloadFormatCode = 0; // a static RTP payload type
}
#else
// Add a filter that converts from little-endian to network (big-endian) order:
sessionState.source = EndianSwap16::createNew(*env, wavSource);
if (sessionState.source == NULL) {
*env << "Unable to create a little->bit-endian order filter from the PCM audio source: " << env->getResultMsg() << "\n";
exit(1);
}
*env << "Converting to network byte order for streaming\n";
mimeType = "L16";
if (samplingFrequency == 44100 && numChannels == 2) {
payloadFormatCode = 10; // a static RTP payload type
} else if (samplingFrequency == 44100 && numChannels == 1) {
payloadFormatCode = 11; // a static RTP payload type
}
#endif
} else if (bitsPerSample == 20 || bitsPerSample == 24) {
// Add a filter that converts from little-endian to network (big-endian) order:
sessionState.source = EndianSwap24::createNew(*env, wavSource);
if (sessionState.source == NULL) {
*env << "Unable to create a little->bit-endian order filter from the PCM audio source: " << env->getResultMsg() << "\n";
exit(1);
}
*env << "Converting to network byte order for streaming\n";
mimeType = bitsPerSample == 20 ? "L20" : "L24";
} else { // bitsPerSample == 8 (we assume that bitsPerSample == 4 is only for WA_IMA_ADPCM)
// Don't do any transformation; send the 8-bit PCM data 'as is':
mimeType = "L8";
}
} else if (audioFormat == WA_PCMU) {
mimeType = "PCMU";
if (samplingFrequency == 8000 && numChannels == 1) {
payloadFormatCode = 0; // a static RTP payload type
}
} else if (audioFormat == WA_PCMA) {
mimeType = "PCMA";
if (samplingFrequency == 8000 && numChannels == 1) {
payloadFormatCode = 8; // a static RTP payload type
}
} else if (audioFormat == WA_IMA_ADPCM) {
mimeType = "DVI4";
// Use a static payload type, if one is defined:
if (numChannels == 1) {
if (samplingFrequency == 8000) {
payloadFormatCode = 5; // a static RTP payload type
} else if (samplingFrequency == 16000) {
payloadFormatCode = 6; // a static RTP payload type
} else if (samplingFrequency == 11025) {
payloadFormatCode = 16; // a static RTP payload type
} else if (samplingFrequency == 22050) {
payloadFormatCode = 17; // a static RTP payload type
}
}
} else { //unknown format
*env << "Unknown audio format code \"" << audioFormat << "\" in WAV file header\n";
exit(1);
}
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer" demo application,
// or the "LIVE555 Media Server" - not this application - as a model.
const unsigned short rtpPortNum = 2222;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 255;
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
sessionState.rtpGroupsock
= new Groupsock(*env, destinationAddress, rtpPort, ttl);
sessionState.rtpGroupsock->multicastSendOnly(); // we're a SSM source
sessionState.rtcpGroupsock
= new Groupsock(*env, destinationAddress, rtcpPort, ttl);
sessionState.rtcpGroupsock->multicastSendOnly(); // we're a SSM source
// Create an appropriate audio RTP sink (using "SimpleRTPSink") from the RTP 'groupsock':
sessionState.sink
= SimpleRTPSink::createNew(*env, sessionState.rtpGroupsock,
payloadFormatCode, samplingFrequency,
"audio", mimeType, numChannels);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = (bitsPerSecond + 500)/1000; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
sessionState.rtcpInstance
= RTCPInstance::createNew(*env, sessionState.rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
sessionState.sink, NULL /* we're a server */,
True /* we're a SSM source*/);
// Note: This starts RTCP running automatically
// Create and start a RTSP server to serve this stream:
sessionState.rtspServer = RTSPServer::createNew(*env, 8554);
if (sessionState.rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testWAVAudiotreamer\"", True/*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*sessionState.sink, sessionState.rtcpInstance));
sessionState.rtspServer->addServerMediaSession(sms);
char* url = sessionState.rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Finally, start the streaming:
*env << "Beginning streaming...\n";
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
}
void afterPlaying(void* /*clientData*/) {
*env << "...done streaming\n";
// End by closing the media:
Medium::close(sessionState.rtspServer);
Medium::close(sessionState.rtcpInstance);
Medium::close(sessionState.sink);
delete sessionState.rtpGroupsock;
Medium::close(sessionState.source);
delete sessionState.rtcpGroupsock;
// We're done:
exit(0);
}
live/testProgs/vobStreamer.cpp 000400 001752 001752 00000023570 13242237367 016444 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a VOB file
// splits it into Audio (AC3) and Video (MPEG) Elementary Streams,
// and streams both using RTP.
// main program
#include "liveMedia.hh"
#include "AC3AudioStreamFramer.hh"
#include "BasicUsageEnvironment.hh"
#include "GroupsockHelper.hh"
char const* programName;
// Whether to stream *only* "I" (key) frames
// (e.g., to reduce network bandwidth):
Boolean iFramesOnly = False;
unsigned const VOB_AUDIO = 1<<0;
unsigned const VOB_VIDEO = 1<<1;
unsigned mediaToStream = VOB_AUDIO|VOB_VIDEO; // by default
char const** inputFileNames;
char const** curInputFileName;
Boolean haveReadOneFile = False;
UsageEnvironment* env;
MPEG1or2Demux* mpegDemux;
AC3AudioStreamFramer* audioSource = NULL;
FramedSource* videoSource = NULL;
RTPSink* audioSink = NULL;
RTCPInstance* audioRTCP = NULL;
RTPSink* videoSink = NULL;
RTCPInstance* videoRTCP = NULL;
RTSPServer* rtspServer = NULL;
unsigned short const defaultRTSPServerPortNum = 554;
unsigned short rtspServerPortNum = defaultRTSPServerPortNum;
Groupsock* rtpGroupsockAudio;
Groupsock* rtcpGroupsockAudio;
Groupsock* rtpGroupsockVideo;
Groupsock* rtcpGroupsockVideo;
void usage() {
*env << "usage: " << programName << " [-i] [-a|-v] "
"[-p ] "
"...\n";
exit(1);
}
void play(); // forward
int main(int argc, char const** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Parse command-line options:
// (Unfortunately we can't use getopt() here; Windoze doesn't have it)
programName = argv[0];
while (argc > 2) {
char const* const opt = argv[1];
if (opt[0] != '-') break;
switch (opt[1]) {
case 'i': { // transmit video I-frames only
iFramesOnly = True;
break;
}
case 'a': { // transmit audio, but not video
mediaToStream &=~ VOB_VIDEO;
break;
}
case 'v': { // transmit video, but not audio
mediaToStream &=~ VOB_AUDIO;
break;
}
case 'p': { // specify port number for built-in RTSP server
int portArg;
if (sscanf(argv[2], "%d", &portArg) != 1) {
usage();
}
if (portArg <= 0 || portArg >= 65536) {
*env << "bad port number: " << portArg
<< " (must be in the range (0,65536))\n";
usage();
}
rtspServerPortNum = (unsigned short)portArg;
++argv; --argc;
break;
}
default: {
usage();
break;
}
}
++argv; --argc;
}
if (argc < 2) usage();
if (mediaToStream == 0) {
*env << "The -a and -v flags cannot both be used!\n";
usage();
}
if (iFramesOnly && (mediaToStream&VOB_VIDEO) == 0) {
*env << "Warning: Because we're not streaming video, the -i flag has no effect.\n";
}
inputFileNames = &argv[1];
curInputFileName = inputFileNames;
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
const unsigned short rtpPortNumAudio = 4444;
const unsigned short rtcpPortNumAudio = rtpPortNumAudio+1;
const unsigned short rtpPortNumVideo = 8888;
const unsigned short rtcpPortNumVideo = rtpPortNumVideo+1;
const unsigned char ttl = 255;
const Port rtpPortAudio(rtpPortNumAudio);
const Port rtcpPortAudio(rtcpPortNumAudio);
const Port rtpPortVideo(rtpPortNumVideo);
const Port rtcpPortVideo(rtcpPortNumVideo);
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
if (mediaToStream&VOB_AUDIO) {
rtpGroupsockAudio
= new Groupsock(*env, destinationAddress, rtpPortAudio, ttl);
rtpGroupsockAudio->multicastSendOnly(); // because we're a SSM source
// Create an 'AC3 Audio RTP' sink from the RTP 'groupsock':
audioSink
= AC3AudioRTPSink::createNew(*env, rtpGroupsockAudio, 96, 0);
// set the RTP timestamp frequency 'for real' later
// Create (and start) a 'RTCP instance' for this RTP sink:
rtcpGroupsockAudio
= new Groupsock(*env, destinationAddress, rtcpPortAudio, ttl);
rtcpGroupsockAudio->multicastSendOnly(); // because we're a SSM source
const unsigned estimatedSessionBandwidthAudio
= 160; // in kbps; for RTCP b/w share
audioRTCP = RTCPInstance::createNew(*env, rtcpGroupsockAudio,
estimatedSessionBandwidthAudio, CNAME,
audioSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
}
if (mediaToStream&VOB_VIDEO) {
rtpGroupsockVideo
= new Groupsock(*env, destinationAddress, rtpPortVideo, ttl);
rtpGroupsockVideo->multicastSendOnly(); // because we're a SSM source
// Create a 'MPEG Video RTP' sink from the RTP 'groupsock':
videoSink = MPEG1or2VideoRTPSink::createNew(*env, rtpGroupsockVideo);
// Create (and start) a 'RTCP instance' for this RTP sink:
rtcpGroupsockVideo
= new Groupsock(*env, destinationAddress, rtcpPortVideo, ttl);
rtcpGroupsockVideo->multicastSendOnly(); // because we're a SSM source
const unsigned estimatedSessionBandwidthVideo
= 4500; // in kbps; for RTCP b/w share
videoRTCP = RTCPInstance::createNew(*env, rtcpGroupsockVideo,
estimatedSessionBandwidthVideo, CNAME,
videoSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
}
if (rtspServer == NULL) {
rtspServer = RTSPServer::createNew(*env, rtspServerPortNum);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
*env << "To change the RTSP server's port number, use the \"-p \" option.\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "vobStream", *curInputFileName,
"Session streamed by \"vobStreamer\"", True /*SSM*/);
if (audioSink != NULL) sms->addSubsession(PassiveServerMediaSubsession::createNew(*audioSink, audioRTCP));
if (videoSink != NULL) sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, videoRTCP));
rtspServer->addServerMediaSession(sms);
*env << "Created RTSP server.\n";
// Display our "rtsp://" URL, for clients to connect to:
char* url = rtspServer->rtspURL(sms);
*env << "Access this stream using the URL:\n\t" << url << "\n";
delete[] url;
}
// Finally, start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* clientData) {
// One of the sinks has ended playing.
// Check whether any of the sources have a pending read. If so,
// wait until its sink ends playing also:
if ((audioSource != NULL && audioSource->isCurrentlyAwaitingData()) ||
(videoSource != NULL && videoSource->isCurrentlyAwaitingData())) {
return;
}
// Now that both sinks have ended, close both input sources,
// and start playing again:
*env << "...done reading from file\n";
if (audioSink != NULL) audioSink->stopPlaying();
if (videoSink != NULL) videoSink->stopPlaying();
// ensures that both are shut down
Medium::close(audioSource);
Medium::close(videoSource);
Medium::close(mpegDemux);
// Note: This also closes the input file that this source read from.
// Move to the next file name (if any):
++curInputFileName;
// Start playing once again:
play();
}
void play() {
if (*curInputFileName == NULL) {
// We have reached the end of the file name list.
// Start again, unless we didn't succeed in reading any files:
if (!haveReadOneFile) exit(1);
haveReadOneFile = False;
curInputFileName = inputFileNames;
}
// Open the current input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, *curInputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << *curInputFileName
<< "\" as a byte-stream file source\n";
// Try the next file instead:
++curInputFileName;
play();
return;
}
haveReadOneFile = True;
// We must demultiplex Audio and Video Elementary Streams
// from the input source:
mpegDemux = MPEG1or2Demux::createNew(*env, fileSource);
if (mediaToStream&VOB_AUDIO) {
FramedSource* audioES = mpegDemux->newElementaryStream(0xBD);
// Because, in a VOB file, the AC3 audio has stream id 0xBD
audioSource
= AC3AudioStreamFramer::createNew(*env, audioES, 0x80);
}
if (mediaToStream&VOB_VIDEO) {
FramedSource* videoES = mpegDemux->newVideoStream();
videoSource
= MPEG1or2VideoStreamFramer::createNew(*env, videoES, iFramesOnly);
}
// Finally, start playing each sink.
*env << "Beginning to read from \"" << *curInputFileName << "\"...\n";
if (videoSink != NULL) {
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}
if (audioSink != NULL) {
audioSink->setRTPTimestampFrequency(audioSource->samplingRate());
audioSink->startPlaying(*audioSource, afterPlaying, audioSink);
}
}
live/testProgs/testH265VideoStreamer.cpp 000444 001752 001752 00000011737 13242237367 020203 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// A test program that reads a H.265 Elementary Stream video file
// and streams it using RTP
// main program
//
// NOTE: For this application to work, the H.265 Elementary Stream video file *must* contain
// VPS, SPS and PPS NAL units, ideally at or near the start of the file.
// These VPS, SPS and PPS NAL units are used to specify 'configuration' information that is set in
// the output stream's SDP description (by the RTSP server that is built in to this application).
// Note also that - unlike some other "*Streamer" demo applications - the resulting stream can be
// received only using a RTSP client (such as "openRTSP")
#include
#include
#include
UsageEnvironment* env;
char const* inputFileName = "test.265";
H265VideoStreamFramer* videoSource;
RTPSink* videoSink;
void play(); // forward
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Create 'groupsocks' for RTP and RTCP:
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*env);
// Note: This is a multicast address. If you wish instead to stream
// using unicast, then you should use the "testOnDemandRTSPServer"
// test program - not this test program - as a model.
const unsigned short rtpPortNum = 18888;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 255;
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
Groupsock rtpGroupsock(*env, destinationAddress, rtpPort, ttl);
rtpGroupsock.multicastSendOnly(); // we're a SSM source
Groupsock rtcpGroupsock(*env, destinationAddress, rtcpPort, ttl);
rtcpGroupsock.multicastSendOnly(); // we're a SSM source
// Create a 'H265 Video RTP' sink from the RTP 'groupsock':
OutPacketBuffer::maxSize = 100000;
videoSink = H265VideoRTPSink::createNew(*env, &rtpGroupsock, 96);
// Create (and start) a 'RTCP instance' for this RTP sink:
const unsigned estimatedSessionBandwidth = 500; // in kbps; for RTCP b/w share
const unsigned maxCNAMElen = 100;
unsigned char CNAME[maxCNAMElen+1];
gethostname((char*)CNAME, maxCNAMElen);
CNAME[maxCNAMElen] = '\0'; // just in case
RTCPInstance* rtcp
= RTCPInstance::createNew(*env, &rtcpGroupsock,
estimatedSessionBandwidth, CNAME,
videoSink, NULL /* we're a server */,
True /* we're a SSM source */);
// Note: This starts RTCP running automatically
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554);
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
ServerMediaSession* sms
= ServerMediaSession::createNew(*env, "testStream", inputFileName,
"Session streamed by \"testH265VideoStreamer\"",
True /*SSM*/);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
char* url = rtspServer->rtspURL(sms);
*env << "Play this stream using the URL \"" << url << "\"\n";
delete[] url;
// Start the streaming:
*env << "Beginning streaming...\n";
play();
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
videoSink->stopPlaying();
Medium::close(videoSource);
// Note that this also closes the input file that this source read from.
// Start playing once again:
play();
}
void play() {
// Open the input file as a 'byte-stream file source':
ByteStreamFileSource* fileSource
= ByteStreamFileSource::createNew(*env, inputFileName);
if (fileSource == NULL) {
*env << "Unable to open file \"" << inputFileName
<< "\" as a byte-stream file source\n";
exit(1);
}
FramedSource* videoES = fileSource;
// Create a framer for the Video Elementary Stream:
videoSource = H265VideoStreamFramer::createNew(*env, videoES);
// Finally, start playing:
*env << "Beginning to read from file...\n";
videoSink->startPlaying(*videoSource, afterPlaying, videoSink);
}
live/BasicUsageEnvironment/BasicHashTable.cpp 000444 001752 001752 00000016756 13242237367 021231 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Hash Table implementation
// Implementation
#include "BasicHashTable.hh"
#include "strDup.hh"
#if defined(__WIN32__) || defined(_WIN32)
#else
#include
#endif
#include
#include
// When there are this many entries per bucket, on average, rebuild
// the table to increase the number of buckets
#define REBUILD_MULTIPLIER 3
BasicHashTable::BasicHashTable(int keyType)
: fBuckets(fStaticBuckets), fNumBuckets(SMALL_HASH_TABLE_SIZE),
fNumEntries(0), fRebuildSize(SMALL_HASH_TABLE_SIZE*REBUILD_MULTIPLIER),
fDownShift(28), fMask(0x3), fKeyType(keyType) {
for (unsigned i = 0; i < SMALL_HASH_TABLE_SIZE; ++i) {
fStaticBuckets[i] = NULL;
}
}
BasicHashTable::~BasicHashTable() {
// Free all the entries in the table:
for (unsigned i = 0; i < fNumBuckets; ++i) {
TableEntry* entry;
while ((entry = fBuckets[i]) != NULL) {
deleteEntry(i, entry);
}
}
// Also free the bucket array, if it was dynamically allocated:
if (fBuckets != fStaticBuckets) delete[] fBuckets;
}
void* BasicHashTable::Add(char const* key, void* value) {
void* oldValue;
unsigned index;
TableEntry* entry = lookupKey(key, index);
if (entry != NULL) {
// There's already an item with this key
oldValue = entry->value;
} else {
// There's no existing entry; create a new one:
entry = insertNewEntry(index, key);
oldValue = NULL;
}
entry->value = value;
// If the table has become too large, rebuild it with more buckets:
if (fNumEntries >= fRebuildSize) rebuild();
return oldValue;
}
Boolean BasicHashTable::Remove(char const* key) {
unsigned index;
TableEntry* entry = lookupKey(key, index);
if (entry == NULL) return False; // no such entry
deleteEntry(index, entry);
return True;
}
void* BasicHashTable::Lookup(char const* key) const {
unsigned index;
TableEntry* entry = lookupKey(key, index);
if (entry == NULL) return NULL; // no such entry
return entry->value;
}
unsigned BasicHashTable::numEntries() const {
return fNumEntries;
}
BasicHashTable::Iterator::Iterator(BasicHashTable const& table)
: fTable(table), fNextIndex(0), fNextEntry(NULL) {
}
void* BasicHashTable::Iterator::next(char const*& key) {
while (fNextEntry == NULL) {
if (fNextIndex >= fTable.fNumBuckets) return NULL;
fNextEntry = fTable.fBuckets[fNextIndex++];
}
BasicHashTable::TableEntry* entry = fNextEntry;
fNextEntry = entry->fNext;
key = entry->key;
return entry->value;
}
////////// Implementation of HashTable creation functions //////////
HashTable* HashTable::create(int keyType) {
return new BasicHashTable(keyType);
}
HashTable::Iterator* HashTable::Iterator::create(HashTable const& hashTable) {
// "hashTable" is assumed to be a BasicHashTable
return new BasicHashTable::Iterator((BasicHashTable const&)hashTable);
}
////////// Implementation of internal member functions //////////
BasicHashTable::TableEntry* BasicHashTable
::lookupKey(char const* key, unsigned& index) const {
TableEntry* entry;
index = hashIndexFromKey(key);
for (entry = fBuckets[index]; entry != NULL; entry = entry->fNext) {
if (keyMatches(key, entry->key)) break;
}
return entry;
}
Boolean BasicHashTable
::keyMatches(char const* key1, char const* key2) const {
// The way we check the keys for a match depends upon their type:
if (fKeyType == STRING_HASH_KEYS) {
return (strcmp(key1, key2) == 0);
} else if (fKeyType == ONE_WORD_HASH_KEYS) {
return (key1 == key2);
} else {
unsigned* k1 = (unsigned*)key1;
unsigned* k2 = (unsigned*)key2;
for (int i = 0; i < fKeyType; ++i) {
if (k1[i] != k2[i]) return False; // keys differ
}
return True;
}
}
BasicHashTable::TableEntry* BasicHashTable
::insertNewEntry(unsigned index, char const* key) {
TableEntry* entry = new TableEntry();
entry->fNext = fBuckets[index];
fBuckets[index] = entry;
++fNumEntries;
assignKey(entry, key);
return entry;
}
void BasicHashTable::assignKey(TableEntry* entry, char const* key) {
// The way we assign the key depends upon its type:
if (fKeyType == STRING_HASH_KEYS) {
entry->key = strDup(key);
} else if (fKeyType == ONE_WORD_HASH_KEYS) {
entry->key = key;
} else if (fKeyType > 0) {
unsigned* keyFrom = (unsigned*)key;
unsigned* keyTo = new unsigned[fKeyType];
for (int i = 0; i < fKeyType; ++i) keyTo[i] = keyFrom[i];
entry->key = (char const*)keyTo;
}
}
void BasicHashTable::deleteEntry(unsigned index, TableEntry* entry) {
TableEntry** ep = &fBuckets[index];
Boolean foundIt = False;
while (*ep != NULL) {
if (*ep == entry) {
foundIt = True;
*ep = entry->fNext;
break;
}
ep = &((*ep)->fNext);
}
if (!foundIt) { // shouldn't happen
#ifdef DEBUG
fprintf(stderr, "BasicHashTable[%p]::deleteEntry(%d,%p): internal error - not found (first entry %p", this, index, entry, fBuckets[index]);
if (fBuckets[index] != NULL) fprintf(stderr, ", next entry %p", fBuckets[index]->fNext);
fprintf(stderr, ")\n");
#endif
}
--fNumEntries;
deleteKey(entry);
delete entry;
}
void BasicHashTable::deleteKey(TableEntry* entry) {
// The way we delete the key depends upon its type:
if (fKeyType == ONE_WORD_HASH_KEYS) {
entry->key = NULL;
} else {
delete[] (char*)entry->key;
entry->key = NULL;
}
}
void BasicHashTable::rebuild() {
// Remember the existing table size:
unsigned oldSize = fNumBuckets;
TableEntry** oldBuckets = fBuckets;
// Create the new sized table:
fNumBuckets *= 4;
fBuckets = new TableEntry*[fNumBuckets];
for (unsigned i = 0; i < fNumBuckets; ++i) {
fBuckets[i] = NULL;
}
fRebuildSize *= 4;
fDownShift -= 2;
fMask = (fMask<<2)|0x3;
// Rehash the existing entries into the new table:
for (TableEntry** oldChainPtr = oldBuckets; oldSize > 0;
--oldSize, ++oldChainPtr) {
for (TableEntry* hPtr = *oldChainPtr; hPtr != NULL;
hPtr = *oldChainPtr) {
*oldChainPtr = hPtr->fNext;
unsigned index = hashIndexFromKey(hPtr->key);
hPtr->fNext = fBuckets[index];
fBuckets[index] = hPtr;
}
}
// Free the old bucket array, if it was dynamically allocated:
if (oldBuckets != fStaticBuckets) delete[] oldBuckets;
}
unsigned BasicHashTable::hashIndexFromKey(char const* key) const {
unsigned result = 0;
if (fKeyType == STRING_HASH_KEYS) {
while (1) {
char c = *key++;
if (c == 0) break;
result += (result<<3) + (unsigned)c;
}
result &= fMask;
} else if (fKeyType == ONE_WORD_HASH_KEYS) {
result = randomIndex((uintptr_t)key);
} else {
unsigned* k = (unsigned*)key;
uintptr_t sum = 0;
for (int i = 0; i < fKeyType; ++i) {
sum += k[i];
}
result = randomIndex(sum);
}
return result;
}
live/BasicUsageEnvironment/BasicTaskScheduler.cpp 000444 001752 001752 00000024474 13242237367 022133 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
#include "BasicUsageEnvironment.hh"
#include "HandlerSet.hh"
#include
#if defined(_QNX4)
#include
#include
#endif
////////// BasicTaskScheduler //////////
BasicTaskScheduler* BasicTaskScheduler::createNew(unsigned maxSchedulerGranularity) {
return new BasicTaskScheduler(maxSchedulerGranularity);
}
BasicTaskScheduler::BasicTaskScheduler(unsigned maxSchedulerGranularity)
: fMaxSchedulerGranularity(maxSchedulerGranularity), fMaxNumSockets(0)
#if defined(__WIN32__) || defined(_WIN32)
, fDummySocketNum(-1)
#endif
{
FD_ZERO(&fReadSet);
FD_ZERO(&fWriteSet);
FD_ZERO(&fExceptionSet);
if (maxSchedulerGranularity > 0) schedulerTickTask(); // ensures that we handle events frequently
}
BasicTaskScheduler::~BasicTaskScheduler() {
#if defined(__WIN32__) || defined(_WIN32)
if (fDummySocketNum >= 0) closeSocket(fDummySocketNum);
#endif
}
void BasicTaskScheduler::schedulerTickTask(void* clientData) {
((BasicTaskScheduler*)clientData)->schedulerTickTask();
}
void BasicTaskScheduler::schedulerTickTask() {
scheduleDelayedTask(fMaxSchedulerGranularity, schedulerTickTask, this);
}
#ifndef MILLION
#define MILLION 1000000
#endif
void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
fd_set readSet = fReadSet; // make a copy for this select() call
fd_set writeSet = fWriteSet; // ditto
fd_set exceptionSet = fExceptionSet; // ditto
DelayInterval const& timeToDelay = fDelayQueue.timeToNextAlarm();
struct timeval tv_timeToDelay;
tv_timeToDelay.tv_sec = timeToDelay.seconds();
tv_timeToDelay.tv_usec = timeToDelay.useconds();
// Very large "tv_sec" values cause select() to fail.
// Don't make it any larger than 1 million seconds (11.5 days)
const long MAX_TV_SEC = MILLION;
if (tv_timeToDelay.tv_sec > MAX_TV_SEC) {
tv_timeToDelay.tv_sec = MAX_TV_SEC;
}
// Also check our "maxDelayTime" parameter (if it's > 0):
if (maxDelayTime > 0 &&
(tv_timeToDelay.tv_sec > (long)maxDelayTime/MILLION ||
(tv_timeToDelay.tv_sec == (long)maxDelayTime/MILLION &&
tv_timeToDelay.tv_usec > (long)maxDelayTime%MILLION))) {
tv_timeToDelay.tv_sec = maxDelayTime/MILLION;
tv_timeToDelay.tv_usec = maxDelayTime%MILLION;
}
int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
if (selectResult < 0) {
#if defined(__WIN32__) || defined(_WIN32)
int err = WSAGetLastError();
// For some unknown reason, select() in Windoze sometimes fails with WSAEINVAL if
// it was called with no entries set in "readSet". If this happens, ignore it:
if (err == WSAEINVAL && readSet.fd_count == 0) {
err = EINTR;
// To stop this from happening again, create a dummy socket:
if (fDummySocketNum >= 0) closeSocket(fDummySocketNum);
fDummySocketNum = socket(AF_INET, SOCK_DGRAM, 0);
FD_SET((unsigned)fDummySocketNum, &fReadSet);
}
if (err != EINTR) {
#else
if (errno != EINTR && errno != EAGAIN) {
#endif
// Unexpected error - treat this as fatal:
#if !defined(_WIN32_WCE)
perror("BasicTaskScheduler::SingleStep(): select() fails");
// Because this failure is often "Bad file descriptor" - which is caused by an invalid socket number (i.e., a socket number
// that had already been closed) being used in "select()" - we print out the sockets that were being used in "select()",
// to assist in debugging:
fprintf(stderr, "socket numbers used in the select() call:");
for (int i = 0; i < 10000; ++i) {
if (FD_ISSET(i, &fReadSet) || FD_ISSET(i, &fWriteSet) || FD_ISSET(i, &fExceptionSet)) {
fprintf(stderr, " %d(", i);
if (FD_ISSET(i, &fReadSet)) fprintf(stderr, "r");
if (FD_ISSET(i, &fWriteSet)) fprintf(stderr, "w");
if (FD_ISSET(i, &fExceptionSet)) fprintf(stderr, "e");
fprintf(stderr, ")");
}
}
fprintf(stderr, "\n");
#endif
internalError();
}
}
// Call the handler function for one readable socket:
HandlerIterator iter(*fHandlers);
HandlerDescriptor* handler;
// To ensure forward progress through the handlers, begin past the last
// socket number that we handled:
if (fLastHandledSocketNum >= 0) {
while ((handler = iter.next()) != NULL) {
if (handler->socketNum == fLastHandledSocketNum) break;
}
if (handler == NULL) {
fLastHandledSocketNum = -1;
iter.reset(); // start from the beginning instead
}
}
while ((handler = iter.next()) != NULL) {
int sock = handler->socketNum; // alias
int resultConditionSet = 0;
if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
fLastHandledSocketNum = sock;
// Note: we set "fLastHandledSocketNum" before calling the handler,
// in case the handler calls "doEventLoop()" reentrantly.
(*handler->handlerProc)(handler->clientData, resultConditionSet);
break;
}
}
if (handler == NULL && fLastHandledSocketNum >= 0) {
// We didn't call a handler, but we didn't get to check all of them,
// so try again from the beginning:
iter.reset();
while ((handler = iter.next()) != NULL) {
int sock = handler->socketNum; // alias
int resultConditionSet = 0;
if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
fLastHandledSocketNum = sock;
// Note: we set "fLastHandledSocketNum" before calling the handler,
// in case the handler calls "doEventLoop()" reentrantly.
(*handler->handlerProc)(handler->clientData, resultConditionSet);
break;
}
}
if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler
}
// Also handle any newly-triggered event (Note that we do this *after* calling a socket handler,
// in case the triggered event handler modifies The set of readable sockets.)
if (fTriggersAwaitingHandling != 0) {
if (fTriggersAwaitingHandling == fLastUsedTriggerMask) {
// Common-case optimization for a single event trigger:
fTriggersAwaitingHandling &=~ fLastUsedTriggerMask;
if (fTriggeredEventHandlers[fLastUsedTriggerNum] != NULL) {
(*fTriggeredEventHandlers[fLastUsedTriggerNum])(fTriggeredEventClientDatas[fLastUsedTriggerNum]);
}
} else {
// Look for an event trigger that needs handling (making sure that we make forward progress through all possible triggers):
unsigned i = fLastUsedTriggerNum;
EventTriggerId mask = fLastUsedTriggerMask;
do {
i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
mask >>= 1;
if (mask == 0) mask = 0x80000000;
if ((fTriggersAwaitingHandling&mask) != 0) {
fTriggersAwaitingHandling &=~ mask;
if (fTriggeredEventHandlers[i] != NULL) {
(*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);
}
fLastUsedTriggerMask = mask;
fLastUsedTriggerNum = i;
break;
}
} while (i != fLastUsedTriggerNum);
}
}
// Also handle any delayed event that may have come due.
fDelayQueue.handleAlarm();
}
void BasicTaskScheduler
::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
if (socketNum < 0) return;
#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
if (socketNum >= (int)(FD_SETSIZE)) return;
#endif
FD_CLR((unsigned)socketNum, &fReadSet);
FD_CLR((unsigned)socketNum, &fWriteSet);
FD_CLR((unsigned)socketNum, &fExceptionSet);
if (conditionSet == 0) {
fHandlers->clearHandler(socketNum);
if (socketNum+1 == fMaxNumSockets) {
--fMaxNumSockets;
}
} else {
fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
if (socketNum+1 > fMaxNumSockets) {
fMaxNumSockets = socketNum+1;
}
if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
}
}
void BasicTaskScheduler::moveSocketHandling(int oldSocketNum, int newSocketNum) {
if (oldSocketNum < 0 || newSocketNum < 0) return; // sanity check
#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
if (oldSocketNum >= (int)(FD_SETSIZE) || newSocketNum >= (int)(FD_SETSIZE)) return; // sanity check
#endif
if (FD_ISSET(oldSocketNum, &fReadSet)) {FD_CLR((unsigned)oldSocketNum, &fReadSet); FD_SET((unsigned)newSocketNum, &fReadSet);}
if (FD_ISSET(oldSocketNum, &fWriteSet)) {FD_CLR((unsigned)oldSocketNum, &fWriteSet); FD_SET((unsigned)newSocketNum, &fWriteSet);}
if (FD_ISSET(oldSocketNum, &fExceptionSet)) {FD_CLR((unsigned)oldSocketNum, &fExceptionSet); FD_SET((unsigned)newSocketNum, &fExceptionSet);}
fHandlers->moveHandler(oldSocketNum, newSocketNum);
if (oldSocketNum+1 == fMaxNumSockets) {
--fMaxNumSockets;
}
if (newSocketNum+1 > fMaxNumSockets) {
fMaxNumSockets = newSocketNum+1;
}
}
live/BasicUsageEnvironment/BasicTaskScheduler0.cpp 000444 001752 001752 00000016537 13242237367 022214 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
#include "BasicUsageEnvironment0.hh"
#include "HandlerSet.hh"
////////// A subclass of DelayQueueEntry,
////////// used to implement BasicTaskScheduler0::scheduleDelayedTask()
class AlarmHandler: public DelayQueueEntry {
public:
AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay)
: DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData) {
}
private: // redefined virtual functions
virtual void handleTimeout() {
(*fProc)(fClientData);
DelayQueueEntry::handleTimeout();
}
private:
TaskFunc* fProc;
void* fClientData;
};
////////// BasicTaskScheduler0 //////////
BasicTaskScheduler0::BasicTaskScheduler0()
: fLastHandledSocketNum(-1), fTriggersAwaitingHandling(0), fLastUsedTriggerMask(1), fLastUsedTriggerNum(MAX_NUM_EVENT_TRIGGERS-1) {
fHandlers = new HandlerSet;
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
fTriggeredEventHandlers[i] = NULL;
fTriggeredEventClientDatas[i] = NULL;
}
}
BasicTaskScheduler0::~BasicTaskScheduler0() {
delete fHandlers;
}
TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,
TaskFunc* proc,
void* clientData) {
if (microseconds < 0) microseconds = 0;
DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000));
AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay);
fDelayQueue.addEntry(alarmHandler);
return (void*)(alarmHandler->token());
}
void BasicTaskScheduler0::unscheduleDelayedTask(TaskToken& prevTask) {
DelayQueueEntry* alarmHandler = fDelayQueue.removeEntry((intptr_t)prevTask);
prevTask = NULL;
delete alarmHandler;
}
void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {
// Repeatedly loop, handling readble sockets and timed events:
while (1) {
if (watchVariable != NULL && *watchVariable != 0) break;
SingleStep();
}
}
EventTriggerId BasicTaskScheduler0::createEventTrigger(TaskFunc* eventHandlerProc) {
unsigned i = fLastUsedTriggerNum;
EventTriggerId mask = fLastUsedTriggerMask;
do {
i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
mask >>= 1;
if (mask == 0) mask = 0x80000000;
if (fTriggeredEventHandlers[i] == NULL) {
// This trigger number is free; use it:
fTriggeredEventHandlers[i] = eventHandlerProc;
fTriggeredEventClientDatas[i] = NULL; // sanity
fLastUsedTriggerMask = mask;
fLastUsedTriggerNum = i;
return mask;
}
} while (i != fLastUsedTriggerNum);
// All available event triggers are allocated; return 0 instead:
return 0;
}
void BasicTaskScheduler0::deleteEventTrigger(EventTriggerId eventTriggerId) {
fTriggersAwaitingHandling &=~ eventTriggerId;
if (eventTriggerId == fLastUsedTriggerMask) { // common-case optimization:
fTriggeredEventHandlers[fLastUsedTriggerNum] = NULL;
fTriggeredEventClientDatas[fLastUsedTriggerNum] = NULL;
} else {
// "eventTriggerId" should have just one bit set.
// However, we do the reasonable thing if the user happened to 'or' together two or more "EventTriggerId"s:
EventTriggerId mask = 0x80000000;
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
if ((eventTriggerId&mask) != 0) {
fTriggeredEventHandlers[i] = NULL;
fTriggeredEventClientDatas[i] = NULL;
}
mask >>= 1;
}
}
}
void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId, void* clientData) {
// First, record the "clientData". (Note that we allow "eventTriggerId" to be a combination of bits for multiple events.)
EventTriggerId mask = 0x80000000;
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
if ((eventTriggerId&mask) != 0) {
fTriggeredEventClientDatas[i] = clientData;
}
mask >>= 1;
}
// Then, note this event as being ready to be handled.
// (Note that because this function (unlike others in the library) can be called from an external thread, we do this last, to
// reduce the risk of a race condition.)
fTriggersAwaitingHandling |= eventTriggerId;
}
////////// HandlerSet (etc.) implementation //////////
HandlerDescriptor::HandlerDescriptor(HandlerDescriptor* nextHandler)
: conditionSet(0), handlerProc(NULL) {
// Link this descriptor into a doubly-linked list:
if (nextHandler == this) { // initialization
fNextHandler = fPrevHandler = this;
} else {
fNextHandler = nextHandler;
fPrevHandler = nextHandler->fPrevHandler;
nextHandler->fPrevHandler = this;
fPrevHandler->fNextHandler = this;
}
}
HandlerDescriptor::~HandlerDescriptor() {
// Unlink this descriptor from a doubly-linked list:
fNextHandler->fPrevHandler = fPrevHandler;
fPrevHandler->fNextHandler = fNextHandler;
}
HandlerSet::HandlerSet()
: fHandlers(&fHandlers) {
fHandlers.socketNum = -1; // shouldn't ever get looked at, but in case...
}
HandlerSet::~HandlerSet() {
// Delete each handler descriptor:
while (fHandlers.fNextHandler != &fHandlers) {
delete fHandlers.fNextHandler; // changes fHandlers->fNextHandler
}
}
void HandlerSet
::assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData) {
// First, see if there's already a handler for this socket:
HandlerDescriptor* handler = lookupHandler(socketNum);
if (handler == NULL) { // No existing handler, so create a new descr:
handler = new HandlerDescriptor(fHandlers.fNextHandler);
handler->socketNum = socketNum;
}
handler->conditionSet = conditionSet;
handler->handlerProc = handlerProc;
handler->clientData = clientData;
}
void HandlerSet::clearHandler(int socketNum) {
HandlerDescriptor* handler = lookupHandler(socketNum);
delete handler;
}
void HandlerSet::moveHandler(int oldSocketNum, int newSocketNum) {
HandlerDescriptor* handler = lookupHandler(oldSocketNum);
if (handler != NULL) {
handler->socketNum = newSocketNum;
}
}
HandlerDescriptor* HandlerSet::lookupHandler(int socketNum) {
HandlerDescriptor* handler;
HandlerIterator iter(*this);
while ((handler = iter.next()) != NULL) {
if (handler->socketNum == socketNum) break;
}
return handler;
}
HandlerIterator::HandlerIterator(HandlerSet& handlerSet)
: fOurSet(handlerSet) {
reset();
}
HandlerIterator::~HandlerIterator() {
}
void HandlerIterator::reset() {
fNextPtr = fOurSet.fHandlers.fNextHandler;
}
HandlerDescriptor* HandlerIterator::next() {
HandlerDescriptor* result = fNextPtr;
if (result == &fOurSet.fHandlers) { // no more
result = NULL;
} else {
fNextPtr = fNextPtr->fNextHandler;
}
return result;
}
live/BasicUsageEnvironment/BasicUsageEnvironment.cpp 000444 001752 001752 00000004603 13242237367 022653 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
#include "BasicUsageEnvironment.hh"
#include
////////// BasicUsageEnvironment //////////
#if defined(__WIN32__) || defined(_WIN32)
extern "C" int initializeWinsockIfNecessary();
#endif
BasicUsageEnvironment::BasicUsageEnvironment(TaskScheduler& taskScheduler)
: BasicUsageEnvironment0(taskScheduler) {
#if defined(__WIN32__) || defined(_WIN32)
if (!initializeWinsockIfNecessary()) {
setResultErrMsg("Failed to initialize 'winsock': ");
reportBackgroundError();
internalError();
}
#endif
}
BasicUsageEnvironment::~BasicUsageEnvironment() {
}
BasicUsageEnvironment*
BasicUsageEnvironment::createNew(TaskScheduler& taskScheduler) {
return new BasicUsageEnvironment(taskScheduler);
}
int BasicUsageEnvironment::getErrno() const {
#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
return WSAGetLastError();
#else
return errno;
#endif
}
UsageEnvironment& BasicUsageEnvironment::operator<<(char const* str) {
if (str == NULL) str = "(NULL)"; // sanity check
fprintf(stderr, "%s", str);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(int i) {
fprintf(stderr, "%d", i);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(unsigned u) {
fprintf(stderr, "%u", u);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(double d) {
fprintf(stderr, "%f", d);
return *this;
}
UsageEnvironment& BasicUsageEnvironment::operator<<(void* p) {
fprintf(stderr, "%p", p);
return *this;
}
live/BasicUsageEnvironment/BasicUsageEnvironment0.cpp 000444 001752 001752 00000006312 13242237367 022732 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
#include "BasicUsageEnvironment0.hh"
#include
#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
#define snprintf _snprintf
#endif
////////// BasicUsageEnvironment //////////
BasicUsageEnvironment0::BasicUsageEnvironment0(TaskScheduler& taskScheduler)
: UsageEnvironment(taskScheduler),
fBufferMaxSize(RESULT_MSG_BUFFER_MAX) {
reset();
}
BasicUsageEnvironment0::~BasicUsageEnvironment0() {
}
void BasicUsageEnvironment0::reset() {
fCurBufferSize = 0;
fResultMsgBuffer[fCurBufferSize] = '\0';
}
// Implementation of virtual functions:
char const* BasicUsageEnvironment0::getResultMsg() const {
return fResultMsgBuffer;
}
void BasicUsageEnvironment0::setResultMsg(MsgString msg) {
reset();
appendToResultMsg(msg);
}
void BasicUsageEnvironment0::setResultMsg(MsgString msg1, MsgString msg2) {
setResultMsg(msg1);
appendToResultMsg(msg2);
}
void BasicUsageEnvironment0::setResultMsg(MsgString msg1, MsgString msg2,
MsgString msg3) {
setResultMsg(msg1, msg2);
appendToResultMsg(msg3);
}
void BasicUsageEnvironment0::setResultErrMsg(MsgString msg, int err) {
setResultMsg(msg);
if (err == 0) err = getErrno();
#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
#ifndef _UNICODE
char errMsg[RESULT_MSG_BUFFER_MAX] = "\0";
if (0 != FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, errMsg, sizeof(errMsg)/sizeof(errMsg[0]), NULL)) {
// Remove all trailing '\r', '\n' and '.'
for (char* p = errMsg + strlen(errMsg); p != errMsg && (*p == '\r' || *p == '\n' || *p == '.' || *p == '\0'); --p) {
*p = '\0';
}
} else
snprintf(errMsg, sizeof(errMsg)/sizeof(errMsg[0]), "error %d", err);
appendToResultMsg(errMsg);
#endif
#else
appendToResultMsg(strerror(err));
#endif
}
void BasicUsageEnvironment0::appendToResultMsg(MsgString msg) {
char* curPtr = &fResultMsgBuffer[fCurBufferSize];
unsigned spaceAvailable = fBufferMaxSize - fCurBufferSize;
unsigned msgLength = strlen(msg);
// Copy only enough of "msg" as will fit:
if (msgLength > spaceAvailable-1) {
msgLength = spaceAvailable-1;
}
memmove(curPtr, (char*)msg, msgLength);
fCurBufferSize += msgLength;
fResultMsgBuffer[fCurBufferSize] = '\0';
}
void BasicUsageEnvironment0::reportBackgroundError() {
fputs(getResultMsg(), stderr);
}
live/BasicUsageEnvironment/COPYING 000755 001752 001752 00000000000 13242237367 020205 2../COPYING ustar 00rsf rsf 000000 000000 live/BasicUsageEnvironment/DelayQueue.cpp 000444 001752 001752 00000014423 13242237367 020464 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// Help by Carlo Bonamico to get working for Windows
// Delay queue
// Implementation
#include "DelayQueue.hh"
#include "GroupsockHelper.hh"
static const int MILLION = 1000000;
///// Timeval /////
int Timeval::operator>=(const Timeval& arg2) const {
return seconds() > arg2.seconds()
|| (seconds() == arg2.seconds()
&& useconds() >= arg2.useconds());
}
void Timeval::operator+=(const DelayInterval& arg2) {
secs() += arg2.seconds(); usecs() += arg2.useconds();
if (useconds() >= MILLION) {
usecs() -= MILLION;
++secs();
}
}
void Timeval::operator-=(const DelayInterval& arg2) {
secs() -= arg2.seconds(); usecs() -= arg2.useconds();
if ((int)useconds() < 0) {
usecs() += MILLION;
--secs();
}
if ((int)seconds() < 0)
secs() = usecs() = 0;
}
DelayInterval operator-(const Timeval& arg1, const Timeval& arg2) {
time_base_seconds secs = arg1.seconds() - arg2.seconds();
time_base_seconds usecs = arg1.useconds() - arg2.useconds();
if ((int)usecs < 0) {
usecs += MILLION;
--secs;
}
if ((int)secs < 0)
return DELAY_ZERO;
else
return DelayInterval(secs, usecs);
}
///// DelayInterval /////
DelayInterval operator*(short arg1, const DelayInterval& arg2) {
time_base_seconds result_seconds = arg1*arg2.seconds();
time_base_seconds result_useconds = arg1*arg2.useconds();
time_base_seconds carry = result_useconds/MILLION;
result_useconds -= carry*MILLION;
result_seconds += carry;
return DelayInterval(result_seconds, result_useconds);
}
#ifndef INT_MAX
#define INT_MAX 0x7FFFFFFF
#endif
const DelayInterval DELAY_ZERO(0, 0);
const DelayInterval DELAY_SECOND(1, 0);
const DelayInterval DELAY_MINUTE = 60*DELAY_SECOND;
const DelayInterval DELAY_HOUR = 60*DELAY_MINUTE;
const DelayInterval DELAY_DAY = 24*DELAY_HOUR;
const DelayInterval ETERNITY(INT_MAX, MILLION-1);
// used internally to make the implementation work
///// DelayQueueEntry /////
intptr_t DelayQueueEntry::tokenCounter = 0;
DelayQueueEntry::DelayQueueEntry(DelayInterval delay)
: fDeltaTimeRemaining(delay) {
fNext = fPrev = this;
fToken = ++tokenCounter;
}
DelayQueueEntry::~DelayQueueEntry() {
}
void DelayQueueEntry::handleTimeout() {
delete this;
}
///// DelayQueue /////
DelayQueue::DelayQueue()
: DelayQueueEntry(ETERNITY) {
fLastSyncTime = TimeNow();
}
DelayQueue::~DelayQueue() {
while (fNext != this) {
DelayQueueEntry* entryToRemove = fNext;
removeEntry(entryToRemove);
delete entryToRemove;
}
}
void DelayQueue::addEntry(DelayQueueEntry* newEntry) {
synchronize();
DelayQueueEntry* cur = head();
while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) {
newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;
cur = cur->fNext;
}
cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;
// Add "newEntry" to the queue, just before "cur":
newEntry->fNext = cur;
newEntry->fPrev = cur->fPrev;
cur->fPrev = newEntry->fPrev->fNext = newEntry;
}
void DelayQueue::updateEntry(DelayQueueEntry* entry, DelayInterval newDelay) {
if (entry == NULL) return;
removeEntry(entry);
entry->fDeltaTimeRemaining = newDelay;
addEntry(entry);
}
void DelayQueue::updateEntry(intptr_t tokenToFind, DelayInterval newDelay) {
DelayQueueEntry* entry = findEntryByToken(tokenToFind);
updateEntry(entry, newDelay);
}
void DelayQueue::removeEntry(DelayQueueEntry* entry) {
if (entry == NULL || entry->fNext == NULL) return;
entry->fNext->fDeltaTimeRemaining += entry->fDeltaTimeRemaining;
entry->fPrev->fNext = entry->fNext;
entry->fNext->fPrev = entry->fPrev;
entry->fNext = entry->fPrev = NULL;
// in case we should try to remove it again
}
DelayQueueEntry* DelayQueue::removeEntry(intptr_t tokenToFind) {
DelayQueueEntry* entry = findEntryByToken(tokenToFind);
removeEntry(entry);
return entry;
}
DelayInterval const& DelayQueue::timeToNextAlarm() {
if (head()->fDeltaTimeRemaining == DELAY_ZERO) return DELAY_ZERO; // a common case
synchronize();
return head()->fDeltaTimeRemaining;
}
void DelayQueue::handleAlarm() {
if (head()->fDeltaTimeRemaining != DELAY_ZERO) synchronize();
if (head()->fDeltaTimeRemaining == DELAY_ZERO) {
// This event is due to be handled:
DelayQueueEntry* toRemove = head();
removeEntry(toRemove); // do this first, in case handler accesses queue
toRemove->handleTimeout();
}
}
DelayQueueEntry* DelayQueue::findEntryByToken(intptr_t tokenToFind) {
DelayQueueEntry* cur = head();
while (cur != this) {
if (cur->token() == tokenToFind) return cur;
cur = cur->fNext;
}
return NULL;
}
void DelayQueue::synchronize() {
// First, figure out how much time has elapsed since the last sync:
_EventTime timeNow = TimeNow();
if (timeNow < fLastSyncTime) {
// The system clock has apparently gone back in time; reset our sync time and return:
fLastSyncTime = timeNow;
return;
}
DelayInterval timeSinceLastSync = timeNow - fLastSyncTime;
fLastSyncTime = timeNow;
// Then, adjust the delay queue for any entries whose time is up:
DelayQueueEntry* curEntry = head();
while (timeSinceLastSync >= curEntry->fDeltaTimeRemaining) {
timeSinceLastSync -= curEntry->fDeltaTimeRemaining;
curEntry->fDeltaTimeRemaining = DELAY_ZERO;
curEntry = curEntry->fNext;
}
curEntry->fDeltaTimeRemaining -= timeSinceLastSync;
}
///// _EventTime /////
_EventTime TimeNow() {
struct timeval tvNow;
gettimeofday(&tvNow, NULL);
return _EventTime(tvNow.tv_sec, tvNow.tv_usec);
}
const _EventTime THE_END_OF_TIME(INT_MAX);
live/BasicUsageEnvironment/include/ 000755 001752 001752 00000000000 13242237367 017336 5 ustar 00rsf rsf 000000 000000 live/BasicUsageEnvironment/Makefile.head 000440 001752 001752 00000000246 13242237367 020247 0 ustar 00rsf rsf 000000 000000 INCLUDES = -Iinclude -I../UsageEnvironment/include -I../groupsock/include
PREFIX = /usr/local
LIBDIR = $(PREFIX)/lib
##### Change the following for your environment:
live/BasicUsageEnvironment/Makefile.tail 000444 001752 001752 00000003263 13242237367 020305 0 ustar 00rsf rsf 000000 000000 ##### End of variables to change
NAME = libBasicUsageEnvironment
LIB_FILE = $(NAME).$(LIB_SUFFIX)
ALL = $(LIB_FILE)
all: $(ALL)
OBJS = BasicUsageEnvironment0.$(OBJ) BasicUsageEnvironment.$(OBJ) \
BasicTaskScheduler0.$(OBJ) BasicTaskScheduler.$(OBJ) \
DelayQueue.$(OBJ) BasicHashTable.$(OBJ)
libBasicUsageEnvironment.$(LIB_SUFFIX): $(OBJS)
$(LIBRARY_LINK)$@ $(LIBRARY_LINK_OPTS) \
$(OBJS)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
BasicUsageEnvironment0.$(CPP): include/BasicUsageEnvironment0.hh
include/BasicUsageEnvironment0.hh: include/BasicUsageEnvironment_version.hh include/DelayQueue.hh
BasicUsageEnvironment.$(CPP): include/BasicUsageEnvironment.hh
include/BasicUsageEnvironment.hh: include/BasicUsageEnvironment0.hh
BasicTaskScheduler0.$(CPP): include/BasicUsageEnvironment0.hh include/HandlerSet.hh
BasicTaskScheduler.$(CPP): include/BasicUsageEnvironment.hh include/HandlerSet.hh
DelayQueue.$(CPP): include/DelayQueue.hh
BasicHashTable.$(CPP): include/BasicHashTable.hh
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
install: install1 $(INSTALL2)
install1: libBasicUsageEnvironment.$(LIB_SUFFIX)
install -d $(DESTDIR)$(PREFIX)/include/BasicUsageEnvironment $(DESTDIR)$(LIBDIR)
install -m 644 include/*.hh $(DESTDIR)$(PREFIX)/include/BasicUsageEnvironment
install -m 644 libBasicUsageEnvironment.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)
install_shared_libraries: libBasicUsageEnvironment.$(LIB_SUFFIX)
ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
##### Any additional, platform-specific rules come here:
live/BasicUsageEnvironment/COPYING.LESSER 000755 001752 001752 00000000000 13242237367 022175 2../COPYING.LESSER ustar 00rsf rsf 000000 000000 live/BasicUsageEnvironment/include/BasicHashTable.hh 000444 001752 001752 00000006621 13242237367 022457 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Hash Table implementation
// C++ header
#ifndef _BASIC_HASH_TABLE_HH
#define _BASIC_HASH_TABLE_HH
#ifndef _HASH_TABLE_HH
#include "HashTable.hh"
#endif
#ifndef _NET_COMMON_H
#include // to ensure that "uintptr_t" is defined
#endif
// A simple hash table implementation, inspired by the hash table
// implementation used in Tcl 7.6:
#define SMALL_HASH_TABLE_SIZE 4
class BasicHashTable: public HashTable {
private:
class TableEntry; // forward
public:
BasicHashTable(int keyType);
virtual ~BasicHashTable();
// Used to iterate through the members of the table:
class Iterator; friend class Iterator; // to make Sun's C++ compiler happy
class Iterator: public HashTable::Iterator {
public:
Iterator(BasicHashTable const& table);
private: // implementation of inherited pure virtual functions
void* next(char const*& key); // returns 0 if none
private:
BasicHashTable const& fTable;
unsigned fNextIndex; // index of next bucket to be enumerated after this
TableEntry* fNextEntry; // next entry in the current bucket
};
private: // implementation of inherited pure virtual functions
virtual void* Add(char const* key, void* value);
// Returns the old value if different, otherwise 0
virtual Boolean Remove(char const* key);
virtual void* Lookup(char const* key) const;
// Returns 0 if not found
virtual unsigned numEntries() const;
private:
class TableEntry {
public:
TableEntry* fNext;
char const* key;
void* value;
};
TableEntry* lookupKey(char const* key, unsigned& index) const;
// returns entry matching "key", or NULL if none
Boolean keyMatches(char const* key1, char const* key2) const;
// used to implement "lookupKey()"
TableEntry* insertNewEntry(unsigned index, char const* key);
// creates a new entry, and inserts it in the table
void assignKey(TableEntry* entry, char const* key);
// used to implement "insertNewEntry()"
void deleteEntry(unsigned index, TableEntry* entry);
void deleteKey(TableEntry* entry);
// used to implement "deleteEntry()"
void rebuild(); // rebuilds the table as its size increases
unsigned hashIndexFromKey(char const* key) const;
// used to implement many of the routines above
unsigned randomIndex(uintptr_t i) const {
return (unsigned)(((i*1103515245) >> fDownShift) & fMask);
}
private:
TableEntry** fBuckets; // pointer to bucket array
TableEntry* fStaticBuckets[SMALL_HASH_TABLE_SIZE];// used for small tables
unsigned fNumBuckets, fNumEntries, fRebuildSize, fDownShift, fMask;
int fKeyType;
};
#endif
live/BasicUsageEnvironment/include/BasicUsageEnvironment.hh 000444 001752 001752 00000006172 13242237367 024116 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// C++ header
#ifndef _BASIC_USAGE_ENVIRONMENT_HH
#define _BASIC_USAGE_ENVIRONMENT_HH
#ifndef _BASIC_USAGE_ENVIRONMENT0_HH
#include "BasicUsageEnvironment0.hh"
#endif
class BasicUsageEnvironment: public BasicUsageEnvironment0 {
public:
static BasicUsageEnvironment* createNew(TaskScheduler& taskScheduler);
// redefined virtual functions:
virtual int getErrno() const;
virtual UsageEnvironment& operator<<(char const* str);
virtual UsageEnvironment& operator<<(int i);
virtual UsageEnvironment& operator<<(unsigned u);
virtual UsageEnvironment& operator<<(double d);
virtual UsageEnvironment& operator<<(void* p);
protected:
BasicUsageEnvironment(TaskScheduler& taskScheduler);
// called only by "createNew()" (or subclass constructors)
virtual ~BasicUsageEnvironment();
};
class BasicTaskScheduler: public BasicTaskScheduler0 {
public:
static BasicTaskScheduler* createNew(unsigned maxSchedulerGranularity = 10000/*microseconds*/);
// "maxSchedulerGranularity" (default value: 10 ms) specifies the maximum time that we wait (in "select()") before
// returning to the event loop to handle non-socket or non-timer-based events, such as 'triggered events'.
// You can change this is you wish (but only if you know what you're doing!), or set it to 0, to specify no such maximum time.
// (You should set it to 0 only if you know that you will not be using 'event triggers'.)
virtual ~BasicTaskScheduler();
protected:
BasicTaskScheduler(unsigned maxSchedulerGranularity);
// called only by "createNew()"
static void schedulerTickTask(void* clientData);
void schedulerTickTask();
protected:
// Redefined virtual functions:
virtual void SingleStep(unsigned maxDelayTime);
virtual void setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData);
virtual void moveSocketHandling(int oldSocketNum, int newSocketNum);
protected:
unsigned fMaxSchedulerGranularity;
// To implement background operations:
int fMaxNumSockets;
fd_set fReadSet;
fd_set fWriteSet;
fd_set fExceptionSet;
private:
#if defined(__WIN32__) || defined(_WIN32)
// Hack to work around a bug in Windows' "select()" implementation:
int fDummySocketNum;
#endif
};
#endif
live/BasicUsageEnvironment/include/BasicUsageEnvironment0.hh 000444 001752 001752 00000007304 13242237367 024174 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// C++ header
#ifndef _BASIC_USAGE_ENVIRONMENT0_HH
#define _BASIC_USAGE_ENVIRONMENT0_HH
#ifndef _BASICUSAGEENVIRONMENT_VERSION_HH
#include "BasicUsageEnvironment_version.hh"
#endif
#ifndef _USAGE_ENVIRONMENT_HH
#include "UsageEnvironment.hh"
#endif
#ifndef _DELAY_QUEUE_HH
#include "DelayQueue.hh"
#endif
#define RESULT_MSG_BUFFER_MAX 1000
// An abstract base class, useful for subclassing
// (e.g., to redefine the implementation of "operator<<")
class BasicUsageEnvironment0: public UsageEnvironment {
public:
// redefined virtual functions:
virtual MsgString getResultMsg() const;
virtual void setResultMsg(MsgString msg);
virtual void setResultMsg(MsgString msg1,
MsgString msg2);
virtual void setResultMsg(MsgString msg1,
MsgString msg2,
MsgString msg3);
virtual void setResultErrMsg(MsgString msg, int err = 0);
virtual void appendToResultMsg(MsgString msg);
virtual void reportBackgroundError();
protected:
BasicUsageEnvironment0(TaskScheduler& taskScheduler);
virtual ~BasicUsageEnvironment0();
private:
void reset();
char fResultMsgBuffer[RESULT_MSG_BUFFER_MAX];
unsigned fCurBufferSize;
unsigned fBufferMaxSize;
};
class HandlerSet; // forward
#define MAX_NUM_EVENT_TRIGGERS 32
// An abstract base class, useful for subclassing
// (e.g., to redefine the implementation of socket event handling)
class BasicTaskScheduler0: public TaskScheduler {
public:
virtual ~BasicTaskScheduler0();
virtual void SingleStep(unsigned maxDelayTime = 0) = 0;
// "maxDelayTime" is in microseconds. It allows a subclass to impose a limit
// on how long "select()" can delay, in case it wants to also do polling.
// 0 (the default value) means: There's no maximum; just look at the delay queue
public:
// Redefined virtual functions:
virtual TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc,
void* clientData);
virtual void unscheduleDelayedTask(TaskToken& prevTask);
virtual void doEventLoop(char volatile* watchVariable);
virtual EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc);
virtual void deleteEventTrigger(EventTriggerId eventTriggerId);
virtual void triggerEvent(EventTriggerId eventTriggerId, void* clientData = NULL);
protected:
BasicTaskScheduler0();
protected:
// To implement delayed operations:
DelayQueue fDelayQueue;
// To implement background reads:
HandlerSet* fHandlers;
int fLastHandledSocketNum;
// To implement event triggers:
EventTriggerId volatile fTriggersAwaitingHandling; // implemented as a 32-bit bitmap
EventTriggerId fLastUsedTriggerMask; // implemented as a 32-bit bitmap
TaskFunc* fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGERS];
void* fTriggeredEventClientDatas[MAX_NUM_EVENT_TRIGGERS];
unsigned fLastUsedTriggerNum; // in the range [0,MAX_NUM_EVENT_TRIGGERS)
};
#endif
live/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh 000444 001752 001752 00000000542 13242237367 025656 0 ustar 00rsf rsf 000000 000000 // Version information for the "BasicUsageEnvironment" library
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
#ifndef _BASICUSAGEENVIRONMENT_VERSION_HH
#define _BASICUSAGEENVIRONMENT_VERSION_HH
#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_STRING "2018.02.18"
#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT 1518912000
#endif
live/BasicUsageEnvironment/include/DelayQueue.hh 000444 001752 001752 00000011056 13242237367 021723 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// Delay queue
// C++ header
#ifndef _DELAY_QUEUE_HH
#define _DELAY_QUEUE_HH
#ifndef _NET_COMMON_H
#include "NetCommon.h"
#endif
#ifdef TIME_BASE
typedef TIME_BASE time_base_seconds;
#else
typedef long time_base_seconds;
#endif
///// A "Timeval" can be either an absolute time, or a time interval /////
class Timeval {
public:
time_base_seconds seconds() const {
return fTv.tv_sec;
}
time_base_seconds seconds() {
return fTv.tv_sec;
}
time_base_seconds useconds() const {
return fTv.tv_usec;
}
time_base_seconds useconds() {
return fTv.tv_usec;
}
int operator>=(Timeval const& arg2) const;
int operator<=(Timeval const& arg2) const {
return arg2 >= *this;
}
int operator<(Timeval const& arg2) const {
return !(*this >= arg2);
}
int operator>(Timeval const& arg2) const {
return arg2 < *this;
}
int operator==(Timeval const& arg2) const {
return *this >= arg2 && arg2 >= *this;
}
int operator!=(Timeval const& arg2) const {
return !(*this == arg2);
}
void operator+=(class DelayInterval const& arg2);
void operator-=(class DelayInterval const& arg2);
// returns ZERO iff arg2 >= arg1
protected:
Timeval(time_base_seconds seconds, time_base_seconds useconds) {
fTv.tv_sec = seconds; fTv.tv_usec = useconds;
}
private:
time_base_seconds& secs() {
return (time_base_seconds&)fTv.tv_sec;
}
time_base_seconds& usecs() {
return (time_base_seconds&)fTv.tv_usec;
}
struct timeval fTv;
};
#ifndef max
inline Timeval max(Timeval const& arg1, Timeval const& arg2) {
return arg1 >= arg2 ? arg1 : arg2;
}
#endif
#ifndef min
inline Timeval min(Timeval const& arg1, Timeval const& arg2) {
return arg1 <= arg2 ? arg1 : arg2;
}
#endif
class DelayInterval operator-(Timeval const& arg1, Timeval const& arg2);
// returns ZERO iff arg2 >= arg1
///// DelayInterval /////
class DelayInterval: public Timeval {
public:
DelayInterval(time_base_seconds seconds, time_base_seconds useconds)
: Timeval(seconds, useconds) {}
};
DelayInterval operator*(short arg1, DelayInterval const& arg2);
extern DelayInterval const DELAY_ZERO;
extern DelayInterval const DELAY_SECOND;
extern DelayInterval const DELAY_MINUTE;
extern DelayInterval const DELAY_HOUR;
extern DelayInterval const DELAY_DAY;
///// _EventTime /////
class _EventTime: public Timeval {
public:
_EventTime(unsigned secondsSinceEpoch = 0,
unsigned usecondsSinceEpoch = 0)
// We use the Unix standard epoch: January 1, 1970
: Timeval(secondsSinceEpoch, usecondsSinceEpoch) {}
};
_EventTime TimeNow();
extern _EventTime const THE_END_OF_TIME;
///// DelayQueueEntry /////
class DelayQueueEntry {
public:
virtual ~DelayQueueEntry();
intptr_t token() {
return fToken;
}
protected: // abstract base class
DelayQueueEntry(DelayInterval delay);
virtual void handleTimeout();
private:
friend class DelayQueue;
DelayQueueEntry* fNext;
DelayQueueEntry* fPrev;
DelayInterval fDeltaTimeRemaining;
intptr_t fToken;
static intptr_t tokenCounter;
};
///// DelayQueue /////
class DelayQueue: public DelayQueueEntry {
public:
DelayQueue();
virtual ~DelayQueue();
void addEntry(DelayQueueEntry* newEntry); // returns a token for the entry
void updateEntry(DelayQueueEntry* entry, DelayInterval newDelay);
void updateEntry(intptr_t tokenToFind, DelayInterval newDelay);
void removeEntry(DelayQueueEntry* entry); // but doesn't delete it
DelayQueueEntry* removeEntry(intptr_t tokenToFind); // but doesn't delete it
DelayInterval const& timeToNextAlarm();
void handleAlarm();
private:
DelayQueueEntry* head() { return fNext; }
DelayQueueEntry* findEntryByToken(intptr_t token);
void synchronize(); // bring the 'time remaining' fields up-to-date
_EventTime fLastSyncTime;
};
#endif
live/BasicUsageEnvironment/include/HandlerSet.hh 000444 001752 001752 00000004221 13242237367 021705 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// C++ header
#ifndef _HANDLER_SET_HH
#define _HANDLER_SET_HH
#ifndef _BOOLEAN_HH
#include "Boolean.hh"
#endif
////////// HandlerSet (etc.) definition //////////
class HandlerDescriptor {
HandlerDescriptor(HandlerDescriptor* nextHandler);
virtual ~HandlerDescriptor();
public:
int socketNum;
int conditionSet;
TaskScheduler::BackgroundHandlerProc* handlerProc;
void* clientData;
private:
// Descriptors are linked together in a doubly-linked list:
friend class HandlerSet;
friend class HandlerIterator;
HandlerDescriptor* fNextHandler;
HandlerDescriptor* fPrevHandler;
};
class HandlerSet {
public:
HandlerSet();
virtual ~HandlerSet();
void assignHandler(int socketNum, int conditionSet, TaskScheduler::BackgroundHandlerProc* handlerProc, void* clientData);
void clearHandler(int socketNum);
void moveHandler(int oldSocketNum, int newSocketNum);
private:
HandlerDescriptor* lookupHandler(int socketNum);
private:
friend class HandlerIterator;
HandlerDescriptor fHandlers;
};
class HandlerIterator {
public:
HandlerIterator(HandlerSet& handlerSet);
virtual ~HandlerIterator();
HandlerDescriptor* next(); // returns NULL if none
void reset();
private:
HandlerSet& fOurSet;
HandlerDescriptor* fNextPtr;
};
#endif
live/UsageEnvironment/COPYING 000755 001752 001752 00000000000 13242237367 017243 2../COPYING ustar 00rsf rsf 000000 000000 live/UsageEnvironment/HashTable.cpp 000444 001752 001752 00000002621 13242237367 017307 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Generic Hash Table
// Implementation
#include "HashTable.hh"
HashTable::HashTable() {
}
HashTable::~HashTable() {
}
HashTable::Iterator::Iterator() {
}
HashTable::Iterator::~Iterator() {}
void* HashTable::RemoveNext() {
Iterator* iter = Iterator::create(*this);
char const* key;
void* removedValue = iter->next(key);
if (removedValue != 0) Remove(key);
delete iter;
return removedValue;
}
void* HashTable::getFirst() {
Iterator* iter = Iterator::create(*this);
char const* key;
void* firstValue = iter->next(key);
delete iter;
return firstValue;
}
live/UsageEnvironment/include/ 000755 001752 001752 00000000000 13242237367 016374 5 ustar 00rsf rsf 000000 000000 live/UsageEnvironment/Makefile.head 000440 001752 001752 00000000210 13242237367 017274 0 ustar 00rsf rsf 000000 000000 INCLUDES = -Iinclude -I../groupsock/include
PREFIX = /usr/local
LIBDIR = $(PREFIX)/lib
##### Change the following for your environment:
live/UsageEnvironment/Makefile.tail 000444 001752 001752 00000002403 13242237367 017336 0 ustar 00rsf rsf 000000 000000 ##### End of variables to change
NAME = libUsageEnvironment
USAGE_ENVIRONMENT_LIB = $(NAME).$(LIB_SUFFIX)
ALL = $(USAGE_ENVIRONMENT_LIB)
all: $(ALL)
OBJS = UsageEnvironment.$(OBJ) HashTable.$(OBJ) strDup.$(OBJ)
$(USAGE_ENVIRONMENT_LIB): $(OBJS)
$(LIBRARY_LINK)$@ $(LIBRARY_LINK_OPTS) $(OBJS)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
UsageEnvironment.$(CPP): include/UsageEnvironment.hh
include/UsageEnvironment.hh: include/UsageEnvironment_version.hh include/Boolean.hh include/strDup.hh
HashTable.$(CPP): include/HashTable.hh
include/HashTable.hh: include/Boolean.hh
strDup.$(CPP): include/strDup.hh
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
install: install1 $(INSTALL2)
install1: $(USAGE_ENVIRONMENT_LIB)
install -d $(DESTDIR)$(PREFIX)/include/UsageEnvironment $(DESTDIR)$(LIBDIR)
install -m 644 include/*.hh $(DESTDIR)$(PREFIX)/include/UsageEnvironment
install -m 644 $(USAGE_ENVIRONMENT_LIB) $(DESTDIR)$(LIBDIR)
install_shared_libraries: $(USAGE_ENVIRONMENT_LIB)
ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).$(SHORT_LIB_SUFFIX)
ln -fs $(NAME).$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/$(NAME).so
##### Any additional, platform-specific rules come here:
live/UsageEnvironment/strDup.cpp 000444 001752 001752 00000002747 13242237367 016746 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// A C++ equivalent to the standard C routine "strdup()".
// This generates a char* that can be deleted using "delete[]"
// Implementation
#include "strDup.hh"
char* strDup(char const* str) {
if (str == NULL) return NULL;
size_t len = strlen(str) + 1;
char* copy = new char[len];
if (copy != NULL) {
memcpy(copy, str, len);
}
return copy;
}
char* strDupSize(char const* str) {
size_t dummy;
return strDupSize(str, dummy);
}
char* strDupSize(char const* str, size_t& resultBufSize) {
if (str == NULL) {
resultBufSize = 0;
return NULL;
}
resultBufSize = strlen(str) + 1;
char* copy = new char[resultBufSize];
return copy;
}
live/UsageEnvironment/UsageEnvironment.cpp 000444 001752 001752 00000004044 13242237367 020746 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Usage Environment
// Implementation
#include "UsageEnvironment.hh"
Boolean UsageEnvironment::reclaim() {
// We delete ourselves only if we have no remainining state:
if (liveMediaPriv == NULL && groupsockPriv == NULL) {
delete this;
return True;
}
return False;
}
UsageEnvironment::UsageEnvironment(TaskScheduler& scheduler)
: liveMediaPriv(NULL), groupsockPriv(NULL), fScheduler(scheduler) {
}
UsageEnvironment::~UsageEnvironment() {
}
// By default, we handle 'should not occur'-type library errors by calling abort(). Subclasses can redefine this, if desired.
// (If your runtime library doesn't define the "abort()" function, then define your own (e.g., that does nothing).)
void UsageEnvironment::internalError() {
abort();
}
TaskScheduler::TaskScheduler() {
}
TaskScheduler::~TaskScheduler() {
}
void TaskScheduler::rescheduleDelayedTask(TaskToken& task,
int64_t microseconds, TaskFunc* proc,
void* clientData) {
unscheduleDelayedTask(task);
task = scheduleDelayedTask(microseconds, proc, clientData);
}
// By default, we handle 'should not occur'-type library errors by calling abort(). Subclasses can redefine this, if desired.
void TaskScheduler::internalError() {
abort();
}
live/UsageEnvironment/COPYING.LESSER 000755 001752 001752 00000000000 13242237367 021233 2../COPYING.LESSER ustar 00rsf rsf 000000 000000 live/UsageEnvironment/include/Boolean.hh 000444 001752 001752 00000002361 13242237367 020274 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
#ifndef _BOOLEAN_HH
#define _BOOLEAN_HH
#if defined(__BORLANDC__) || (!defined(USE_LIVE555_BOOLEAN) && defined(_MSC_VER) && _MSC_VER >= 1400)
// Use the "bool" type defined by the Borland compiler, and MSVC++ 8.0, Visual Studio 2005 and higher
typedef bool Boolean;
#define False false
#define True true
#else
typedef unsigned char Boolean;
#ifndef __MSHTML_LIBRARY_DEFINED__
#ifndef False
const Boolean False = 0;
#endif
#ifndef True
const Boolean True = 1;
#endif
#endif
#endif
#endif
live/UsageEnvironment/include/HashTable.hh 000444 001752 001752 00000004735 13242237367 020557 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Generic Hash Table
// C++ header
#ifndef _HASH_TABLE_HH
#define _HASH_TABLE_HH
#ifndef _BOOLEAN_HH
#include "Boolean.hh"
#endif
class HashTable {
public:
virtual ~HashTable();
// The following must be implemented by a particular
// implementation (subclass):
static HashTable* create(int keyType);
virtual void* Add(char const* key, void* value) = 0;
// Returns the old value if different, otherwise 0
virtual Boolean Remove(char const* key) = 0;
virtual void* Lookup(char const* key) const = 0;
// Returns 0 if not found
virtual unsigned numEntries() const = 0;
Boolean IsEmpty() const { return numEntries() == 0; }
// Used to iterate through the members of the table:
class Iterator {
public:
// The following must be implemented by a particular
// implementation (subclass):
static Iterator* create(HashTable const& hashTable);
virtual ~Iterator();
virtual void* next(char const*& key) = 0; // returns 0 if none
protected:
Iterator(); // abstract base class
};
// A shortcut that can be used to successively remove each of
// the entries in the table (e.g., so that their values can be
// deleted, if they happen to be pointers to allocated memory).
void* RemoveNext();
// Returns the first entry in the table.
// (This is useful for deleting each entry in the table, if the entry's destructor also removes itself from the table.)
void* getFirst();
protected:
HashTable(); // abstract base class
};
// Warning: The following are deliberately the same as in
// Tcl's hash table implementation
int const STRING_HASH_KEYS = 0;
int const ONE_WORD_HASH_KEYS = 1;
#endif
live/UsageEnvironment/include/strDup.hh 000444 001752 001752 00000002635 13242237367 020202 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
#ifndef _STRDUP_HH
#define _STRDUP_HH
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// A C++ equivalent to the standard C routine "strdup()".
// This generates a char* that can be deleted using "delete[]"
// Header
#include
char* strDup(char const* str);
// Note: strDup(NULL) returns NULL
char* strDupSize(char const* str);
// Like "strDup()", except that it *doesn't* copy the original.
// (Instead, it just allocates a string of the same size as the original.)
char* strDupSize(char const* str, size_t& resultBufSize);
// An alternative form of "strDupSize()" that also returns the size of the allocated buffer.
#endif
live/UsageEnvironment/include/UsageEnvironment.hh 000444 001752 001752 00000015433 13242237367 022212 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Usage Environment
// C++ header
#ifndef _USAGE_ENVIRONMENT_HH
#define _USAGE_ENVIRONMENT_HH
#ifndef _USAGEENVIRONMENT_VERSION_HH
#include "UsageEnvironment_version.hh"
#endif
#ifndef _NETCOMMON_H
#include "NetCommon.h"
#endif
#ifndef _BOOLEAN_HH
#include "Boolean.hh"
#endif
#ifndef _STRDUP_HH
// "strDup()" is used often, so include this here, so everyone gets it:
#include "strDup.hh"
#endif
#ifndef NULL
#define NULL 0
#endif
#ifdef __BORLANDC__
#define _setmode setmode
#define _O_BINARY O_BINARY
#endif
class TaskScheduler; // forward
// An abstract base class, subclassed for each use of the library
class UsageEnvironment {
public:
Boolean reclaim();
// returns True iff we were actually able to delete our object
// task scheduler:
TaskScheduler& taskScheduler() const {return fScheduler;}
// result message handling:
typedef char const* MsgString;
virtual MsgString getResultMsg() const = 0;
virtual void setResultMsg(MsgString msg) = 0;
virtual void setResultMsg(MsgString msg1, MsgString msg2) = 0;
virtual void setResultMsg(MsgString msg1, MsgString msg2, MsgString msg3) = 0;
virtual void setResultErrMsg(MsgString msg, int err = 0) = 0;
// like setResultMsg(), except that an 'errno' message is appended. (If "err == 0", the "getErrno()" code is used instead.)
virtual void appendToResultMsg(MsgString msg) = 0;
virtual void reportBackgroundError() = 0;
// used to report a (previously set) error message within
// a background event
virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library.
// 'errno'
virtual int getErrno() const = 0;
// 'console' output:
virtual UsageEnvironment& operator<<(char const* str) = 0;
virtual UsageEnvironment& operator<<(int i) = 0;
virtual UsageEnvironment& operator<<(unsigned u) = 0;
virtual UsageEnvironment& operator<<(double d) = 0;
virtual UsageEnvironment& operator<<(void* p) = 0;
// a pointer to additional, optional, client-specific state
void* liveMediaPriv;
void* groupsockPriv;
protected:
UsageEnvironment(TaskScheduler& scheduler); // abstract base class
virtual ~UsageEnvironment(); // we are deleted only by reclaim()
private:
TaskScheduler& fScheduler;
};
typedef void TaskFunc(void* clientData);
typedef void* TaskToken;
typedef u_int32_t EventTriggerId;
class TaskScheduler {
public:
virtual ~TaskScheduler();
virtual TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc,
void* clientData) = 0;
// Schedules a task to occur (after a delay) when we next
// reach a scheduling point.
// (Does not delay if "microseconds" <= 0)
// Returns a token that can be used in a subsequent call to
// unscheduleDelayedTask() or rescheduleDelayedTask()
// (but only if the task has not yet occurred).
virtual void unscheduleDelayedTask(TaskToken& prevTask) = 0;
// (Has no effect if "prevTask" == NULL)
// Sets "prevTask" to NULL afterwards.
// Note: This MUST NOT be called if the scheduled task has already occurred.
virtual void rescheduleDelayedTask(TaskToken& task,
int64_t microseconds, TaskFunc* proc,
void* clientData);
// Combines "unscheduleDelayedTask()" with "scheduleDelayedTask()"
// (setting "task" to the new task token).
// Note: This MUST NOT be called if the scheduled task has already occurred.
// For handling socket operations in the background (from the event loop):
typedef void BackgroundHandlerProc(void* clientData, int mask);
// Possible bits to set in "mask". (These are deliberately defined
// the same as those in Tcl, to make a Tcl-based subclass easy.)
#define SOCKET_READABLE (1<<1)
#define SOCKET_WRITABLE (1<<2)
#define SOCKET_EXCEPTION (1<<3)
virtual void setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) = 0;
void disableBackgroundHandling(int socketNum) { setBackgroundHandling(socketNum, 0, NULL, NULL); }
virtual void moveSocketHandling(int oldSocketNum, int newSocketNum) = 0;
// Changes any socket handling for "oldSocketNum" so that occurs with "newSocketNum" instead.
virtual void doEventLoop(char volatile* watchVariable = NULL) = 0;
// Causes further execution to take place within the event loop.
// Delayed tasks, background I/O handling, and other events are handled, sequentially (as a single thread of control).
// (If "watchVariable" is not NULL, then we return from this routine when *watchVariable != 0)
virtual EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc) = 0;
// Creates a 'trigger' for an event, which - if it occurs - will be handled (from the event loop) using "eventHandlerProc".
// (Returns 0 iff no such trigger can be created (e.g., because of implementation limits on the number of triggers).)
virtual void deleteEventTrigger(EventTriggerId eventTriggerId) = 0;
virtual void triggerEvent(EventTriggerId eventTriggerId, void* clientData = NULL) = 0;
// Causes the (previously-registered) handler function for the specified event to be handled (from the event loop).
// The handler function is called with "clientData" as parameter.
// Note: This function (unlike other library functions) may be called from an external thread
// - to signal an external event. (However, "triggerEvent()" should not be called with the
// same 'event trigger id' from different threads.)
// The following two functions are deprecated, and are provided for backwards-compatibility only:
void turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void* clientData) {
setBackgroundHandling(socketNum, SOCKET_READABLE, handlerProc, clientData);
}
void turnOffBackgroundReadHandling(int socketNum) { disableBackgroundHandling(socketNum); }
virtual void internalError(); // used to 'handle' a 'should not occur'-type error condition within the library.
protected:
TaskScheduler(); // abstract base class
};
#endif
live/UsageEnvironment/include/UsageEnvironment_version.hh 000444 001752 001752 00000000511 13242237367 023746 0 ustar 00rsf rsf 000000 000000 // Version information for the "UsageEnvironment" library
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
#ifndef _USAGEENVIRONMENT_VERSION_HH
#define _USAGEENVIRONMENT_VERSION_HH
#define USAGEENVIRONMENT_LIBRARY_VERSION_STRING "2018.02.18"
#define USAGEENVIRONMENT_LIBRARY_VERSION_INT 1518912000
#endif
live/groupsock/COPYING 000755 001752 001752 00000000000 13242237367 015766 2../COPYING ustar 00rsf rsf 000000 000000 live/groupsock/GroupEId.cpp 000444 001752 001752 00000003232 13242237367 015654 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// "Group Endpoint Id"
// Implementation
#include "GroupEId.hh"
GroupEId::GroupEId(struct in_addr const& groupAddr,
portNumBits portNum, u_int8_t ttl) {
struct in_addr sourceFilterAddr;
sourceFilterAddr.s_addr = ~0; // indicates no source filter
init(groupAddr, sourceFilterAddr, portNum, ttl);
}
GroupEId::GroupEId(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
portNumBits portNum) {
init(groupAddr, sourceFilterAddr, portNum, 255);
}
Boolean GroupEId::isSSM() const {
return fSourceFilterAddress.s_addr != netAddressBits(~0);
}
void GroupEId::init(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
portNumBits portNum,
u_int8_t ttl) {
fGroupAddress = groupAddr;
fSourceFilterAddress = sourceFilterAddr;
fPortNum = portNum;
fTTL = ttl;
}
live/groupsock/Groupsock.cpp 000444 001752 001752 00000051401 13242237367 016153 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// 'Group sockets'
// Implementation
#include "Groupsock.hh"
#include "GroupsockHelper.hh"
//##### Eventually fix the following #include; we shouldn't know about tunnels
#include "TunnelEncaps.hh"
#ifndef NO_SSTREAM
#include
#endif
#include
///////// OutputSocket //////////
OutputSocket::OutputSocket(UsageEnvironment& env)
: Socket(env, 0 /* let kernel choose port */),
fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
}
OutputSocket::OutputSocket(UsageEnvironment& env, Port port)
: Socket(env, port),
fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
}
OutputSocket::~OutputSocket() {
}
Boolean OutputSocket::write(netAddressBits address, portNumBits portNum, u_int8_t ttl,
unsigned char* buffer, unsigned bufferSize) {
struct in_addr destAddr; destAddr.s_addr = address;
if ((unsigned)ttl == fLastSentTTL) {
// Optimization: Don't do a 'set TTL' system call again
if (!writeSocket(env(), socketNum(), destAddr, portNum, buffer, bufferSize)) return False;
} else {
if (!writeSocket(env(), socketNum(), destAddr, portNum, ttl, buffer, bufferSize)) return False;
fLastSentTTL = (unsigned)ttl;
}
if (sourcePortNum() == 0) {
// Now that we've sent a packet, we can find out what the
// kernel chose as our ephemeral source port number:
if (!getSourcePort(env(), socketNum(), fSourcePort)) {
if (DebugLevel >= 1)
env() << *this
<< ": failed to get source port: "
<< env().getResultMsg() << "\n";
return False;
}
}
return True;
}
// By default, we don't do reads:
Boolean OutputSocket
::handleRead(unsigned char* /*buffer*/, unsigned /*bufferMaxSize*/,
unsigned& /*bytesRead*/, struct sockaddr_in& /*fromAddressAndPort*/) {
return True;
}
///////// destRecord //////////
destRecord
::destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId,
destRecord* next)
: fNext(next), fGroupEId(addr, port.num(), ttl), fSessionId(sessionId) {
}
destRecord::~destRecord() {
delete fNext;
}
///////// Groupsock //////////
NetInterfaceTrafficStats Groupsock::statsIncoming;
NetInterfaceTrafficStats Groupsock::statsOutgoing;
NetInterfaceTrafficStats Groupsock::statsRelayedIncoming;
NetInterfaceTrafficStats Groupsock::statsRelayedOutgoing;
// Constructor for a source-independent multicast group
Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
Port port, u_int8_t ttl)
: OutputSocket(env, port),
deleteIfNoMembers(False), isSlave(False),
fDests(new destRecord(groupAddr, port, ttl, 0, NULL)),
fIncomingGroupEId(groupAddr, port.num(), ttl) {
if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr)) {
if (DebugLevel >= 1) {
env << *this << ": failed to join group: "
<< env.getResultMsg() << "\n";
}
}
// Make sure we can get our source address:
if (ourIPAddress(env) == 0) {
if (DebugLevel >= 0) { // this is a fatal error
env << "Unable to determine our source address: "
<< env.getResultMsg() << "\n";
}
}
if (DebugLevel >= 2) env << *this << ": created\n";
}
// Constructor for a source-specific multicast group
Groupsock::Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
Port port)
: OutputSocket(env, port),
deleteIfNoMembers(False), isSlave(False),
fDests(new destRecord(groupAddr, port, 255, 0, NULL)),
fIncomingGroupEId(groupAddr, sourceFilterAddr, port.num()) {
// First try a SSM join. If that fails, try a regular join:
if (!socketJoinGroupSSM(env, socketNum(), groupAddr.s_addr,
sourceFilterAddr.s_addr)) {
if (DebugLevel >= 3) {
env << *this << ": SSM join failed: "
<< env.getResultMsg();
env << " - trying regular join instead\n";
}
if (!socketJoinGroup(env, socketNum(), groupAddr.s_addr)) {
if (DebugLevel >= 1) {
env << *this << ": failed to join group: "
<< env.getResultMsg() << "\n";
}
}
}
if (DebugLevel >= 2) env << *this << ": created\n";
}
Groupsock::~Groupsock() {
if (isSSM()) {
if (!socketLeaveGroupSSM(env(), socketNum(), groupAddress().s_addr,
sourceFilterAddress().s_addr)) {
socketLeaveGroup(env(), socketNum(), groupAddress().s_addr);
}
} else {
socketLeaveGroup(env(), socketNum(), groupAddress().s_addr);
}
delete fDests;
if (DebugLevel >= 2) env() << *this << ": deleting\n";
}
destRecord* Groupsock
::createNewDestRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl,
unsigned sessionId, destRecord* next) {
// Default implementation:
return new destRecord(addr, port, ttl, sessionId, next);
}
void
Groupsock::changeDestinationParameters(struct in_addr const& newDestAddr,
Port newDestPort, int newDestTTL, unsigned sessionId) {
destRecord* dest;
for (dest = fDests; dest != NULL && dest->fSessionId != sessionId; dest = dest->fNext) {}
if (dest == NULL) { // There's no existing 'destRecord' for this "sessionId"; add a new one:
fDests = createNewDestRecord(newDestAddr, newDestPort, newDestTTL, sessionId, fDests);
return;
}
// "dest" is an existing 'destRecord' for this "sessionId"; change its values to the new ones:
struct in_addr destAddr = dest->fGroupEId.groupAddress();
if (newDestAddr.s_addr != 0) {
if (newDestAddr.s_addr != destAddr.s_addr
&& IsMulticastAddress(newDestAddr.s_addr)) {
// If the new destination is a multicast address, then we assume that
// we want to join it also. (If this is not in fact the case, then
// call "multicastSendOnly()" afterwards.)
socketLeaveGroup(env(), socketNum(), destAddr.s_addr);
socketJoinGroup(env(), socketNum(), newDestAddr.s_addr);
}
destAddr.s_addr = newDestAddr.s_addr;
}
portNumBits destPortNum = dest->fGroupEId.portNum();
if (newDestPort.num() != 0) {
if (newDestPort.num() != destPortNum
&& IsMulticastAddress(destAddr.s_addr)) {
// Also bind to the new port number:
changePort(newDestPort);
// And rejoin the multicast group:
socketJoinGroup(env(), socketNum(), destAddr.s_addr);
}
destPortNum = newDestPort.num();
}
u_int8_t destTTL = ttl();
if (newDestTTL != ~0) destTTL = (u_int8_t)newDestTTL;
dest->fGroupEId = GroupEId(destAddr, destPortNum, destTTL);
// Finally, remove any other 'destRecord's that might also have this "sessionId":
removeDestinationFrom(dest->fNext, sessionId);
}
unsigned Groupsock
::lookupSessionIdFromDestination(struct sockaddr_in const& destAddrAndPort) const {
destRecord* dest = lookupDestRecordFromDestination(destAddrAndPort);
if (dest == NULL) return 0;
return dest->fSessionId;
}
void Groupsock::addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId) {
// Default implementation:
// If there's no existing 'destRecord' with the same "addr", "port", and "sessionId", add a new one:
for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
if (sessionId == dest->fSessionId
&& addr.s_addr == dest->fGroupEId.groupAddress().s_addr
&& port.num() == dest->fGroupEId.portNum()) {
return;
}
}
fDests = createNewDestRecord(addr, port, 255, sessionId, fDests);
}
void Groupsock::removeDestination(unsigned sessionId) {
// Default implementation:
removeDestinationFrom(fDests, sessionId);
}
void Groupsock::removeAllDestinations() {
delete fDests; fDests = NULL;
}
void Groupsock::multicastSendOnly() {
// We disable this code for now, because - on some systems - leaving the multicast group seems to cause sent packets
// to not be received by other applications (at least, on the same host).
#if 0
socketLeaveGroup(env(), socketNum(), fIncomingGroupEId.groupAddress().s_addr);
for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
socketLeaveGroup(env(), socketNum(), dests->fGroupEId.groupAddress().s_addr);
}
#endif
}
Boolean Groupsock::output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize,
DirectedNetInterface* interfaceNotToFwdBackTo) {
do {
// First, do the datagram send, to each destination:
Boolean writeSuccess = True;
for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
if (!write(dests->fGroupEId.groupAddress().s_addr, dests->fGroupEId.portNum(), dests->fGroupEId.ttl(),
buffer, bufferSize)) {
writeSuccess = False;
break;
}
}
if (!writeSuccess) break;
statsOutgoing.countPacket(bufferSize);
statsGroupOutgoing.countPacket(bufferSize);
// Then, forward to our members:
int numMembers = 0;
if (!members().IsEmpty()) {
numMembers =
outputToAllMembersExcept(interfaceNotToFwdBackTo,
ttl(), buffer, bufferSize,
ourIPAddress(env));
if (numMembers < 0) break;
}
if (DebugLevel >= 3) {
env << *this << ": wrote " << bufferSize << " bytes, ttl " << (unsigned)ttl();
if (numMembers > 0) {
env << "; relayed to " << numMembers << " members";
}
env << "\n";
}
return True;
} while (0);
if (DebugLevel >= 0) { // this is a fatal error
UsageEnvironment::MsgString msg = strDup(env.getResultMsg());
env.setResultMsg("Groupsock write failed: ", msg);
delete[] (char*)msg;
}
return False;
}
Boolean Groupsock::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
struct sockaddr_in& fromAddressAndPort) {
// Read data from the socket, and relay it across any attached tunnels
//##### later make this code more general - independent of tunnels
bytesRead = 0;
int maxBytesToRead = bufferMaxSize - TunnelEncapsulationTrailerMaxSize;
int numBytes = readSocket(env(), socketNum(),
buffer, maxBytesToRead, fromAddressAndPort);
if (numBytes < 0) {
if (DebugLevel >= 0) { // this is a fatal error
UsageEnvironment::MsgString msg = strDup(env().getResultMsg());
env().setResultMsg("Groupsock read failed: ", msg);
delete[] (char*)msg;
}
return False;
}
// If we're a SSM group, make sure the source address matches:
if (isSSM()
&& fromAddressAndPort.sin_addr.s_addr != sourceFilterAddress().s_addr) {
return True;
}
// We'll handle this data.
// Also write it (with the encapsulation trailer) to each member,
// unless the packet was originally sent by us to begin with.
bytesRead = numBytes;
int numMembers = 0;
if (!wasLoopedBackFromUs(env(), fromAddressAndPort)) {
statsIncoming.countPacket(numBytes);
statsGroupIncoming.countPacket(numBytes);
numMembers =
outputToAllMembersExcept(NULL, ttl(),
buffer, bytesRead,
fromAddressAndPort.sin_addr.s_addr);
if (numMembers > 0) {
statsRelayedIncoming.countPacket(numBytes);
statsGroupRelayedIncoming.countPacket(numBytes);
}
}
if (DebugLevel >= 3) {
env() << *this << ": read " << bytesRead << " bytes from " << AddressString(fromAddressAndPort).val() << ", port " << ntohs(fromAddressAndPort.sin_port);
if (numMembers > 0) {
env() << "; relayed to " << numMembers << " members";
}
env() << "\n";
}
return True;
}
Boolean Groupsock::wasLoopedBackFromUs(UsageEnvironment& env,
struct sockaddr_in& fromAddressAndPort) {
if (fromAddressAndPort.sin_addr.s_addr == ourIPAddress(env) ||
fromAddressAndPort.sin_addr.s_addr == 0x7F000001/*127.0.0.1*/) {
if (fromAddressAndPort.sin_port == sourcePortNum()) {
#ifdef DEBUG_LOOPBACK_CHECKING
if (DebugLevel >= 3) {
env() << *this << ": got looped-back packet\n";
}
#endif
return True;
}
}
return False;
}
destRecord* Groupsock
::lookupDestRecordFromDestination(struct sockaddr_in const& destAddrAndPort) const {
for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
if (destAddrAndPort.sin_addr.s_addr == dest->fGroupEId.groupAddress().s_addr
&& destAddrAndPort.sin_port == dest->fGroupEId.portNum()) {
return dest;
}
}
return NULL;
}
void Groupsock::removeDestinationFrom(destRecord*& dests, unsigned sessionId) {
destRecord** destsPtr = &dests;
while (*destsPtr != NULL) {
if (sessionId == (*destsPtr)->fSessionId) {
// Remove the record pointed to by *destsPtr :
destRecord* next = (*destsPtr)->fNext;
(*destsPtr)->fNext = NULL;
delete (*destsPtr);
*destsPtr = next;
} else {
destsPtr = &((*destsPtr)->fNext);
}
}
}
int Groupsock::outputToAllMembersExcept(DirectedNetInterface* exceptInterface,
u_int8_t ttlToFwd,
unsigned char* data, unsigned size,
netAddressBits sourceAddr) {
// Don't forward TTL-0 packets
if (ttlToFwd == 0) return 0;
DirectedNetInterfaceSet::Iterator iter(members());
unsigned numMembers = 0;
DirectedNetInterface* interf;
while ((interf = iter.next()) != NULL) {
// Check whether we've asked to exclude this interface:
if (interf == exceptInterface)
continue;
// Check that the packet's source address makes it OK to
// be relayed across this interface:
UsageEnvironment& saveEnv = env();
// because the following call may delete "this"
if (!interf->SourceAddrOKForRelaying(saveEnv, sourceAddr)) {
if (strcmp(saveEnv.getResultMsg(), "") != 0) {
// Treat this as a fatal error
return -1;
} else {
continue;
}
}
if (numMembers == 0) {
// We know that we're going to forward to at least one
// member, so fill in the tunnel encapsulation trailer.
// (Note: Allow for it not being 4-byte-aligned.)
TunnelEncapsulationTrailer* trailerInPacket
= (TunnelEncapsulationTrailer*)&data[size];
TunnelEncapsulationTrailer* trailer;
Boolean misaligned = ((uintptr_t)trailerInPacket & 3) != 0;
unsigned trailerOffset;
u_int8_t tunnelCmd;
if (isSSM()) {
// add an 'auxilliary address' before the trailer
trailerOffset = TunnelEncapsulationTrailerAuxSize;
tunnelCmd = TunnelDataAuxCmd;
} else {
trailerOffset = 0;
tunnelCmd = TunnelDataCmd;
}
unsigned trailerSize = TunnelEncapsulationTrailerSize + trailerOffset;
unsigned tmpTr[TunnelEncapsulationTrailerMaxSize];
if (misaligned) {
trailer = (TunnelEncapsulationTrailer*)&tmpTr;
} else {
trailer = trailerInPacket;
}
trailer += trailerOffset;
if (fDests != NULL) {
trailer->address() = fDests->fGroupEId.groupAddress().s_addr;
Port destPort(ntohs(fDests->fGroupEId.portNum()));
trailer->port() = destPort; // structure copy
}
trailer->ttl() = ttlToFwd;
trailer->command() = tunnelCmd;
if (isSSM()) {
trailer->auxAddress() = sourceFilterAddress().s_addr;
}
if (misaligned) {
memmove(trailerInPacket, trailer-trailerOffset, trailerSize);
}
size += trailerSize;
}
interf->write(data, size);
++numMembers;
}
return numMembers;
}
UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g) {
UsageEnvironment& s1 = s << timestampString() << " Groupsock("
<< g.socketNum() << ": "
<< AddressString(g.groupAddress()).val()
<< ", " << g.port() << ", ";
if (g.isSSM()) {
return s1 << "SSM source: "
<< AddressString(g.sourceFilterAddress()).val() << ")";
} else {
return s1 << (unsigned)(g.ttl()) << ")";
}
}
////////// GroupsockLookupTable //////////
// A hash table used to index Groupsocks by socket number.
static HashTable*& getSocketTable(UsageEnvironment& env) {
_groupsockPriv* priv = groupsockPriv(env);
if (priv->socketTable == NULL) { // We need to create it
priv->socketTable = HashTable::create(ONE_WORD_HASH_KEYS);
}
return priv->socketTable;
}
static Boolean unsetGroupsockBySocket(Groupsock const* groupsock) {
do {
if (groupsock == NULL) break;
int sock = groupsock->socketNum();
// Make sure "sock" is in bounds:
if (sock < 0) break;
HashTable*& sockets = getSocketTable(groupsock->env());
Groupsock* gs = (Groupsock*)sockets->Lookup((char*)(long)sock);
if (gs == NULL || gs != groupsock) break;
sockets->Remove((char*)(long)sock);
if (sockets->IsEmpty()) {
// We can also delete the table (to reclaim space):
delete sockets; sockets = NULL;
reclaimGroupsockPriv(gs->env());
}
return True;
} while (0);
return False;
}
static Boolean setGroupsockBySocket(UsageEnvironment& env, int sock,
Groupsock* groupsock) {
do {
// Make sure the "sock" parameter is in bounds:
if (sock < 0) {
char buf[100];
sprintf(buf, "trying to use bad socket (%d)", sock);
env.setResultMsg(buf);
break;
}
HashTable* sockets = getSocketTable(env);
// Make sure we're not replacing an existing Groupsock (although that shouldn't happen)
Boolean alreadyExists
= (sockets->Lookup((char*)(long)sock) != 0);
if (alreadyExists) {
char buf[100];
sprintf(buf, "Attempting to replace an existing socket (%d)", sock);
env.setResultMsg(buf);
break;
}
sockets->Add((char*)(long)sock, groupsock);
return True;
} while (0);
return False;
}
static Groupsock* getGroupsockBySocket(UsageEnvironment& env, int sock) {
do {
// Make sure the "sock" parameter is in bounds:
if (sock < 0) break;
HashTable* sockets = getSocketTable(env);
return (Groupsock*)sockets->Lookup((char*)(long)sock);
} while (0);
return NULL;
}
Groupsock*
GroupsockLookupTable::Fetch(UsageEnvironment& env,
netAddressBits groupAddress,
Port port, u_int8_t ttl,
Boolean& isNew) {
isNew = False;
Groupsock* groupsock;
do {
groupsock = (Groupsock*) fTable.Lookup(groupAddress, (~0), port);
if (groupsock == NULL) { // we need to create one:
groupsock = AddNew(env, groupAddress, (~0), port, ttl);
if (groupsock == NULL) break;
isNew = True;
}
} while (0);
return groupsock;
}
Groupsock*
GroupsockLookupTable::Fetch(UsageEnvironment& env,
netAddressBits groupAddress,
netAddressBits sourceFilterAddr, Port port,
Boolean& isNew) {
isNew = False;
Groupsock* groupsock;
do {
groupsock
= (Groupsock*) fTable.Lookup(groupAddress, sourceFilterAddr, port);
if (groupsock == NULL) { // we need to create one:
groupsock = AddNew(env, groupAddress, sourceFilterAddr, port, 0);
if (groupsock == NULL) break;
isNew = True;
}
} while (0);
return groupsock;
}
Groupsock*
GroupsockLookupTable::Lookup(netAddressBits groupAddress, Port port) {
return (Groupsock*) fTable.Lookup(groupAddress, (~0), port);
}
Groupsock*
GroupsockLookupTable::Lookup(netAddressBits groupAddress,
netAddressBits sourceFilterAddr, Port port) {
return (Groupsock*) fTable.Lookup(groupAddress, sourceFilterAddr, port);
}
Groupsock* GroupsockLookupTable::Lookup(UsageEnvironment& env, int sock) {
return getGroupsockBySocket(env, sock);
}
Boolean GroupsockLookupTable::Remove(Groupsock const* groupsock) {
unsetGroupsockBySocket(groupsock);
return fTable.Remove(groupsock->groupAddress().s_addr,
groupsock->sourceFilterAddress().s_addr,
groupsock->port());
}
Groupsock* GroupsockLookupTable::AddNew(UsageEnvironment& env,
netAddressBits groupAddress,
netAddressBits sourceFilterAddress,
Port port, u_int8_t ttl) {
Groupsock* groupsock;
do {
struct in_addr groupAddr; groupAddr.s_addr = groupAddress;
if (sourceFilterAddress == netAddressBits(~0)) {
// regular, ISM groupsock
groupsock = new Groupsock(env, groupAddr, port, ttl);
} else {
// SSM groupsock
struct in_addr sourceFilterAddr;
sourceFilterAddr.s_addr = sourceFilterAddress;
groupsock = new Groupsock(env, groupAddr, sourceFilterAddr, port);
}
if (groupsock == NULL || groupsock->socketNum() < 0) break;
if (!setGroupsockBySocket(env, groupsock->socketNum(), groupsock)) break;
fTable.Add(groupAddress, sourceFilterAddress, port, (void*)groupsock);
} while (0);
return groupsock;
}
GroupsockLookupTable::Iterator::Iterator(GroupsockLookupTable& groupsocks)
: fIter(AddressPortLookupTable::Iterator(groupsocks.fTable)) {
}
Groupsock* GroupsockLookupTable::Iterator::next() {
return (Groupsock*) fIter.next();
};
live/groupsock/GroupsockHelper.cpp 000444 001752 001752 00000064331 13242237367 017321 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Helper routines to implement 'group sockets'
// Implementation
#include "GroupsockHelper.hh"
#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
#include
extern "C" int initializeWinsockIfNecessary();
#else
#include
#include
#include
#include
#define initializeWinsockIfNecessary() 1
#endif
#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
#else
#include
#define USE_SIGNALS 1
#endif
#include
// By default, use INADDR_ANY for the sending and receiving interfaces:
netAddressBits SendingInterfaceAddr = INADDR_ANY;
netAddressBits ReceivingInterfaceAddr = INADDR_ANY;
static void socketErr(UsageEnvironment& env, char const* errorMsg) {
env.setResultErrMsg(errorMsg);
}
NoReuse::NoReuse(UsageEnvironment& env)
: fEnv(env) {
groupsockPriv(fEnv)->reuseFlag = 0;
}
NoReuse::~NoReuse() {
groupsockPriv(fEnv)->reuseFlag = 1;
reclaimGroupsockPriv(fEnv);
}
_groupsockPriv* groupsockPriv(UsageEnvironment& env) {
if (env.groupsockPriv == NULL) { // We need to create it
_groupsockPriv* result = new _groupsockPriv;
result->socketTable = NULL;
result->reuseFlag = 1; // default value => allow reuse of socket numbers
env.groupsockPriv = result;
}
return (_groupsockPriv*)(env.groupsockPriv);
}
void reclaimGroupsockPriv(UsageEnvironment& env) {
_groupsockPriv* priv = (_groupsockPriv*)(env.groupsockPriv);
if (priv->socketTable == NULL && priv->reuseFlag == 1/*default value*/) {
// We can delete the structure (to save space); it will get created again, if needed:
delete priv;
env.groupsockPriv = NULL;
}
}
static int createSocket(int type) {
// Call "socket()" to create a (IPv4) socket of the specified type.
// But also set it to have the 'close on exec' property (if we can)
int sock;
#ifdef SOCK_CLOEXEC
sock = socket(AF_INET, type|SOCK_CLOEXEC, 0);
if (sock != -1 || errno != EINVAL) return sock;
// An "errno" of EINVAL likely means that the system wasn't happy with the SOCK_CLOEXEC; fall through and try again without it:
#endif
sock = socket(AF_INET, type, 0);
#ifdef FD_CLOEXEC
if (sock != -1) fcntl(sock, F_SETFD, FD_CLOEXEC);
#endif
return sock;
}
int setupDatagramSocket(UsageEnvironment& env, Port port) {
if (!initializeWinsockIfNecessary()) {
socketErr(env, "Failed to initialize 'winsock': ");
return -1;
}
int newSocket = createSocket(SOCK_DGRAM);
if (newSocket < 0) {
socketErr(env, "unable to create datagram socket: ");
return newSocket;
}
int reuseFlag = groupsockPriv(env)->reuseFlag;
reclaimGroupsockPriv(env);
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,
(const char*)&reuseFlag, sizeof reuseFlag) < 0) {
socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
// Windoze doesn't properly handle SO_REUSEPORT or IP_MULTICAST_LOOP
#else
#ifdef SO_REUSEPORT
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,
(const char*)&reuseFlag, sizeof reuseFlag) < 0) {
socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
closeSocket(newSocket);
return -1;
}
#endif
#ifdef IP_MULTICAST_LOOP
const u_int8_t loop = 1;
if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
(const char*)&loop, sizeof loop) < 0) {
socketErr(env, "setsockopt(IP_MULTICAST_LOOP) error: ");
closeSocket(newSocket);
return -1;
}
#endif
#endif
// Note: Windoze requires binding, even if the port number is 0
netAddressBits addr = INADDR_ANY;
#if defined(__WIN32__) || defined(_WIN32)
#else
if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
#endif
if (port.num() == 0) addr = ReceivingInterfaceAddr;
MAKE_SOCKADDR_IN(name, addr, port.num());
if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
char tmpBuffer[100];
sprintf(tmpBuffer, "bind() error (port number: %d): ",
ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
#else
}
#endif
// Set the sending interface for multicasts, if it's not the default:
if (SendingInterfaceAddr != INADDR_ANY) {
struct in_addr addr;
addr.s_addr = SendingInterfaceAddr;
if (setsockopt(newSocket, IPPROTO_IP, IP_MULTICAST_IF,
(const char*)&addr, sizeof addr) < 0) {
socketErr(env, "error setting outgoing multicast interface: ");
closeSocket(newSocket);
return -1;
}
}
return newSocket;
}
Boolean makeSocketNonBlocking(int sock) {
#if defined(__WIN32__) || defined(_WIN32)
unsigned long arg = 1;
return ioctlsocket(sock, FIONBIO, &arg) == 0;
#elif defined(VXWORKS)
int arg = 1;
return ioctl(sock, FIONBIO, (int)&arg) == 0;
#else
int curFlags = fcntl(sock, F_GETFL, 0);
return fcntl(sock, F_SETFL, curFlags|O_NONBLOCK) >= 0;
#endif
}
Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds) {
Boolean result;
#if defined(__WIN32__) || defined(_WIN32)
unsigned long arg = 0;
result = ioctlsocket(sock, FIONBIO, &arg) == 0;
#elif defined(VXWORKS)
int arg = 0;
result = ioctl(sock, FIONBIO, (int)&arg) == 0;
#else
int curFlags = fcntl(sock, F_GETFL, 0);
result = fcntl(sock, F_SETFL, curFlags&(~O_NONBLOCK)) >= 0;
#endif
if (writeTimeoutInMilliseconds > 0) {
#ifdef SO_SNDTIMEO
#if defined(__WIN32__) || defined(_WIN32)
DWORD msto = (DWORD)writeTimeoutInMilliseconds;
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&msto, sizeof(msto) );
#else
struct timeval tv;
tv.tv_sec = writeTimeoutInMilliseconds/1000;
tv.tv_usec = (writeTimeoutInMilliseconds%1000)*1000;
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof tv);
#endif
#endif
}
return result;
}
int setupStreamSocket(UsageEnvironment& env,
Port port, Boolean makeNonBlocking) {
if (!initializeWinsockIfNecessary()) {
socketErr(env, "Failed to initialize 'winsock': ");
return -1;
}
int newSocket = createSocket(SOCK_STREAM);
if (newSocket < 0) {
socketErr(env, "unable to create stream socket: ");
return newSocket;
}
int reuseFlag = groupsockPriv(env)->reuseFlag;
reclaimGroupsockPriv(env);
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR,
(const char*)&reuseFlag, sizeof reuseFlag) < 0) {
socketErr(env, "setsockopt(SO_REUSEADDR) error: ");
closeSocket(newSocket);
return -1;
}
// SO_REUSEPORT doesn't really make sense for TCP sockets, so we
// normally don't set them. However, if you really want to do this
// #define REUSE_FOR_TCP
#ifdef REUSE_FOR_TCP
#if defined(__WIN32__) || defined(_WIN32)
// Windoze doesn't properly handle SO_REUSEPORT
#else
#ifdef SO_REUSEPORT
if (setsockopt(newSocket, SOL_SOCKET, SO_REUSEPORT,
(const char*)&reuseFlag, sizeof reuseFlag) < 0) {
socketErr(env, "setsockopt(SO_REUSEPORT) error: ");
closeSocket(newSocket);
return -1;
}
#endif
#endif
#endif
// Note: Windoze requires binding, even if the port number is 0
#if defined(__WIN32__) || defined(_WIN32)
#else
if (port.num() != 0 || ReceivingInterfaceAddr != INADDR_ANY) {
#endif
MAKE_SOCKADDR_IN(name, ReceivingInterfaceAddr, port.num());
if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
char tmpBuffer[100];
sprintf(tmpBuffer, "bind() error (port number: %d): ",
ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
#else
}
#endif
if (makeNonBlocking) {
if (!makeSocketNonBlocking(newSocket)) {
socketErr(env, "failed to make non-blocking: ");
closeSocket(newSocket);
return -1;
}
}
return newSocket;
}
int readSocket(UsageEnvironment& env,
int socket, unsigned char* buffer, unsigned bufferSize,
struct sockaddr_in& fromAddress) {
SOCKLEN_T addressSize = sizeof fromAddress;
int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0,
(struct sockaddr*)&fromAddress,
&addressSize);
if (bytesRead < 0) {
//##### HACK to work around bugs in Linux and Windows:
int err = env.getErrno();
if (err == 111 /*ECONNREFUSED (Linux)*/
#if defined(__WIN32__) || defined(_WIN32)
// What a piece of crap Windows is. Sometimes
// recvfrom() returns -1, but with an 'errno' of 0.
// This appears not to be a real error; just treat
// it as if it were a read of zero bytes, and hope
// we don't have to do anything else to 'reset'
// this alleged error:
|| err == 0 || err == EWOULDBLOCK
#else
|| err == EAGAIN
#endif
|| err == 113 /*EHOSTUNREACH (Linux)*/) { // Why does Linux return this for datagram sock?
fromAddress.sin_addr.s_addr = 0;
return 0;
}
//##### END HACK
socketErr(env, "recvfrom() error: ");
} else if (bytesRead == 0) {
// "recvfrom()" on a stream socket can return 0 if the remote end has closed the connection. Treat this as an error:
return -1;
}
return bytesRead;
}
Boolean writeSocket(UsageEnvironment& env,
int socket, struct in_addr address, portNumBits portNum,
u_int8_t ttlArg,
unsigned char* buffer, unsigned bufferSize) {
// Before sending, set the socket's TTL:
#if defined(__WIN32__) || defined(_WIN32)
#define TTL_TYPE int
#else
#define TTL_TYPE u_int8_t
#endif
TTL_TYPE ttl = (TTL_TYPE)ttlArg;
if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL,
(const char*)&ttl, sizeof ttl) < 0) {
socketErr(env, "setsockopt(IP_MULTICAST_TTL) error: ");
return False;
}
return writeSocket(env, socket, address, portNum, buffer, bufferSize);
}
Boolean writeSocket(UsageEnvironment& env,
int socket, struct in_addr address, portNumBits portNum,
unsigned char* buffer, unsigned bufferSize) {
do {
MAKE_SOCKADDR_IN(dest, address.s_addr, portNum);
int bytesSent = sendto(socket, (char*)buffer, bufferSize, 0,
(struct sockaddr*)&dest, sizeof dest);
if (bytesSent != (int)bufferSize) {
char tmpBuf[100];
sprintf(tmpBuf, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);
socketErr(env, tmpBuf);
break;
}
return True;
} while (0);
return False;
}
void ignoreSigPipeOnSocket(int socketNum) {
#ifdef USE_SIGNALS
#ifdef SO_NOSIGPIPE
int set_option = 1;
setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
#else
signal(SIGPIPE, SIG_IGN);
#endif
#endif
}
static unsigned getBufferSize(UsageEnvironment& env, int bufOptName,
int socket) {
unsigned curSize;
SOCKLEN_T sizeSize = sizeof curSize;
if (getsockopt(socket, SOL_SOCKET, bufOptName,
(char*)&curSize, &sizeSize) < 0) {
socketErr(env, "getBufferSize() error: ");
return 0;
}
return curSize;
}
unsigned getSendBufferSize(UsageEnvironment& env, int socket) {
return getBufferSize(env, SO_SNDBUF, socket);
}
unsigned getReceiveBufferSize(UsageEnvironment& env, int socket) {
return getBufferSize(env, SO_RCVBUF, socket);
}
static unsigned setBufferTo(UsageEnvironment& env, int bufOptName,
int socket, unsigned requestedSize) {
SOCKLEN_T sizeSize = sizeof requestedSize;
setsockopt(socket, SOL_SOCKET, bufOptName, (char*)&requestedSize, sizeSize);
// Get and return the actual, resulting buffer size:
return getBufferSize(env, bufOptName, socket);
}
unsigned setSendBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize) {
return setBufferTo(env, SO_SNDBUF, socket, requestedSize);
}
unsigned setReceiveBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize) {
return setBufferTo(env, SO_RCVBUF, socket, requestedSize);
}
static unsigned increaseBufferTo(UsageEnvironment& env, int bufOptName,
int socket, unsigned requestedSize) {
// First, get the current buffer size. If it's already at least
// as big as what we're requesting, do nothing.
unsigned curSize = getBufferSize(env, bufOptName, socket);
// Next, try to increase the buffer to the requested size,
// or to some smaller size, if that's not possible:
while (requestedSize > curSize) {
SOCKLEN_T sizeSize = sizeof requestedSize;
if (setsockopt(socket, SOL_SOCKET, bufOptName,
(char*)&requestedSize, sizeSize) >= 0) {
// success
return requestedSize;
}
requestedSize = (requestedSize+curSize)/2;
}
return getBufferSize(env, bufOptName, socket);
}
unsigned increaseSendBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize) {
return increaseBufferTo(env, SO_SNDBUF, socket, requestedSize);
}
unsigned increaseReceiveBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize) {
return increaseBufferTo(env, SO_RCVBUF, socket, requestedSize);
}
static void clearMulticastAllSocketOption(int socket) {
#ifdef IP_MULTICAST_ALL
// This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior.
// When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address,
// even if some other process on the same system has joined a different multicast group with the same port number.
int multicastAll = 0;
(void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll);
// Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended)
#endif
}
Boolean socketJoinGroup(UsageEnvironment& env, int socket,
netAddressBits groupAddress){
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const char*)&imr, sizeof (struct ip_mreq)) < 0) {
#if defined(__WIN32__) || defined(_WIN32)
if (env.getErrno() != 0) {
// That piece-of-shit toy operating system (Windows) sometimes lies
// about setsockopt() failing!
#endif
socketErr(env, "setsockopt(IP_ADD_MEMBERSHIP) error: ");
return False;
#if defined(__WIN32__) || defined(_WIN32)
}
#endif
}
clearMulticastAllSocketOption(socket);
return True;
}
Boolean socketLeaveGroup(UsageEnvironment&, int socket,
netAddressBits groupAddress) {
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(const char*)&imr, sizeof (struct ip_mreq)) < 0) {
return False;
}
return True;
}
// The source-specific join/leave operations require special setsockopt()
// commands, and a special structure (ip_mreq_source). If the include files
// didn't define these, we do so here:
#if !defined(IP_ADD_SOURCE_MEMBERSHIP)
struct ip_mreq_source {
struct in_addr imr_multiaddr; /* IP multicast address of group */
struct in_addr imr_sourceaddr; /* IP address of source */
struct in_addr imr_interface; /* local IP address of interface */
};
#endif
#ifndef IP_ADD_SOURCE_MEMBERSHIP
#ifdef LINUX
#define IP_ADD_SOURCE_MEMBERSHIP 39
#define IP_DROP_SOURCE_MEMBERSHIP 40
#else
#define IP_ADD_SOURCE_MEMBERSHIP 25
#define IP_DROP_SOURCE_MEMBERSHIP 26
#endif
#endif
Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket,
netAddressBits groupAddress,
netAddressBits sourceFilterAddr) {
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
struct ip_mreq_source imr;
#ifdef __ANDROID__
imr.imr_multiaddr = groupAddress;
imr.imr_sourceaddr = sourceFilterAddr;
imr.imr_interface = ReceivingInterfaceAddr;
#else
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_sourceaddr.s_addr = sourceFilterAddr;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
#endif
if (setsockopt(socket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP,
(const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {
socketErr(env, "setsockopt(IP_ADD_SOURCE_MEMBERSHIP) error: ");
return False;
}
clearMulticastAllSocketOption(socket);
return True;
}
Boolean socketLeaveGroupSSM(UsageEnvironment& /*env*/, int socket,
netAddressBits groupAddress,
netAddressBits sourceFilterAddr) {
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
struct ip_mreq_source imr;
#ifdef __ANDROID__
imr.imr_multiaddr = groupAddress;
imr.imr_sourceaddr = sourceFilterAddr;
imr.imr_interface = ReceivingInterfaceAddr;
#else
imr.imr_multiaddr.s_addr = groupAddress;
imr.imr_sourceaddr.s_addr = sourceFilterAddr;
imr.imr_interface.s_addr = ReceivingInterfaceAddr;
#endif
if (setsockopt(socket, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP,
(const char*)&imr, sizeof (struct ip_mreq_source)) < 0) {
return False;
}
return True;
}
static Boolean getSourcePort0(int socket, portNumBits& resultPortNum/*host order*/) {
sockaddr_in test; test.sin_port = 0;
SOCKLEN_T len = sizeof test;
if (getsockname(socket, (struct sockaddr*)&test, &len) < 0) return False;
resultPortNum = ntohs(test.sin_port);
return True;
}
Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port) {
portNumBits portNum = 0;
if (!getSourcePort0(socket, portNum) || portNum == 0) {
// Hack - call bind(), then try again:
MAKE_SOCKADDR_IN(name, INADDR_ANY, 0);
bind(socket, (struct sockaddr*)&name, sizeof name);
if (!getSourcePort0(socket, portNum) || portNum == 0) {
socketErr(env, "getsockname() error: ");
return False;
}
}
port = Port(portNum);
return True;
}
static Boolean badAddressForUs(netAddressBits addr) {
// Check for some possible erroneous addresses:
netAddressBits nAddr = htonl(addr);
return (nAddr == 0x7F000001 /* 127.0.0.1 */
|| nAddr == 0
|| nAddr == (netAddressBits)(~0));
}
Boolean loopbackWorks = 1;
netAddressBits ourIPAddress(UsageEnvironment& env) {
static netAddressBits ourAddress = 0;
int sock = -1;
struct in_addr testAddr;
if (ReceivingInterfaceAddr != INADDR_ANY) {
// Hack: If we were told to receive on a specific interface address, then
// define this to be our ip address:
ourAddress = ReceivingInterfaceAddr;
}
if (ourAddress == 0) {
// We need to find our source address
struct sockaddr_in fromAddr;
fromAddr.sin_addr.s_addr = 0;
// Get our address by sending a (0-TTL) multicast packet,
// receiving it, and looking at the source address used.
// (This is kinda bogus, but it provides the best guarantee
// that other nodes will think our address is the same as we do.)
do {
loopbackWorks = 0; // until we learn otherwise
testAddr.s_addr = our_inet_addr("228.67.43.91"); // arbitrary
Port testPort(15947); // ditto
sock = setupDatagramSocket(env, testPort);
if (sock < 0) break;
if (!socketJoinGroup(env, sock, testAddr.s_addr)) break;
unsigned char testString[] = "hostIdTest";
unsigned testStringLength = sizeof testString;
if (!writeSocket(env, sock, testAddr, testPort.num(), 0,
testString, testStringLength)) break;
// Block until the socket is readable (with a 5-second timeout):
fd_set rd_set;
FD_ZERO(&rd_set);
FD_SET((unsigned)sock, &rd_set);
const unsigned numFds = sock+1;
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int result = select(numFds, &rd_set, NULL, NULL, &timeout);
if (result <= 0) break;
unsigned char readBuffer[20];
int bytesRead = readSocket(env, sock,
readBuffer, sizeof readBuffer,
fromAddr);
if (bytesRead != (int)testStringLength
|| strncmp((char*)readBuffer, (char*)testString, testStringLength) != 0) {
break;
}
// We use this packet's source address, if it's good:
loopbackWorks = !badAddressForUs(fromAddr.sin_addr.s_addr);
} while (0);
if (sock >= 0) {
socketLeaveGroup(env, sock, testAddr.s_addr);
closeSocket(sock);
}
if (!loopbackWorks) do {
// We couldn't find our address using multicast loopback,
// so try instead to look it up directly - by first getting our host name, and then resolving this host name
char hostname[100];
hostname[0] = '\0';
int result = gethostname(hostname, sizeof hostname);
if (result != 0 || hostname[0] == '\0') {
env.setResultErrMsg("initial gethostname() failed");
break;
}
// Try to resolve "hostname" to an IP address:
NetAddressList addresses(hostname);
NetAddressList::Iterator iter(addresses);
NetAddress const* address;
// Take the first address that's not bad:
netAddressBits addr = 0;
while ((address = iter.nextAddress()) != NULL) {
netAddressBits a = *(netAddressBits*)(address->data());
if (!badAddressForUs(a)) {
addr = a;
break;
}
}
// Assign the address that we found to "fromAddr" (as if the 'loopback' method had worked), to simplify the code below:
fromAddr.sin_addr.s_addr = addr;
} while (0);
// Make sure we have a good address:
netAddressBits from = fromAddr.sin_addr.s_addr;
if (badAddressForUs(from)) {
char tmp[100];
sprintf(tmp, "This computer has an invalid IP address: %s", AddressString(from).val());
env.setResultMsg(tmp);
from = 0;
}
ourAddress = from;
// Use our newly-discovered IP address, and the current time,
// to initialize the random number generator's seed:
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
unsigned seed = ourAddress^timeNow.tv_sec^timeNow.tv_usec;
our_srandom(seed);
}
return ourAddress;
}
netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env) {
// First, a hack to ensure that our random number generator is seeded:
(void) ourIPAddress(env);
// Choose a random address in the range [232.0.1.0, 232.255.255.255)
// i.e., [0xE8000100, 0xE8FFFFFF)
netAddressBits const first = 0xE8000100, lastPlus1 = 0xE8FFFFFF;
netAddressBits const range = lastPlus1 - first;
return ntohl(first + ((netAddressBits)our_random())%range);
}
char const* timestampString() {
struct timeval tvNow;
gettimeofday(&tvNow, NULL);
#if !defined(_WIN32_WCE)
static char timeString[9]; // holds hh:mm:ss plus trailing '\0'
time_t tvNow_t = tvNow.tv_sec;
char const* ctimeResult = ctime(&tvNow_t);
if (ctimeResult == NULL) {
sprintf(timeString, "??:??:??");
} else {
char const* from = &ctimeResult[11];
int i;
for (i = 0; i < 8; ++i) {
timeString[i] = from[i];
}
timeString[i] = '\0';
}
#else
// WinCE apparently doesn't have "ctime()", so instead, construct
// a timestamp string just using the integer and fractional parts
// of "tvNow":
static char timeString[50];
sprintf(timeString, "%lu.%06ld", tvNow.tv_sec, tvNow.tv_usec);
#endif
return (char const*)&timeString;
}
#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
// For Windoze, we need to implement our own gettimeofday()
// used to make sure that static variables in gettimeofday() aren't initialized simultaneously by multiple threads
static LONG initializeLock_gettimeofday = 0;
#if !defined(_WIN32_WCE)
#include
#endif
int gettimeofday(struct timeval* tp, int* /*tz*/) {
static LARGE_INTEGER tickFrequency, epochOffset;
static Boolean isInitialized = False;
LARGE_INTEGER tickNow;
#if !defined(_WIN32_WCE)
QueryPerformanceCounter(&tickNow);
#else
tickNow.QuadPart = GetTickCount();
#endif
if (!isInitialized) {
if(1 == InterlockedIncrement(&initializeLock_gettimeofday)) {
#if !defined(_WIN32_WCE)
// For our first call, use "ftime()", so that we get a time with a proper epoch.
// For subsequent calls, use "QueryPerformanceCount()", because it's more fine-grain.
struct timeb tb;
ftime(&tb);
tp->tv_sec = tb.time;
tp->tv_usec = 1000*tb.millitm;
// Also get our counter frequency:
QueryPerformanceFrequency(&tickFrequency);
#else
/* FILETIME of Jan 1 1970 00:00:00. */
const LONGLONG epoch = 116444736000000000LL;
FILETIME fileTime;
LARGE_INTEGER time;
GetSystemTimeAsFileTime(&fileTime);
time.HighPart = fileTime.dwHighDateTime;
time.LowPart = fileTime.dwLowDateTime;
// convert to from 100ns time to unix timestamp in seconds, 1000*1000*10
tp->tv_sec = (long)((time.QuadPart - epoch) / 10000000L);
/*
GetSystemTimeAsFileTime has just a seconds resolution,
thats why wince-version of gettimeofday is not 100% accurate, usec accuracy would be calculated like this:
// convert 100 nanoseconds to usec
tp->tv_usec= (long)((time.QuadPart - epoch)%10000000L) / 10L;
*/
tp->tv_usec = 0;
// resolution of GetTickCounter() is always milliseconds
tickFrequency.QuadPart = 1000;
#endif
// compute an offset to add to subsequent counter times, so we get a proper epoch:
epochOffset.QuadPart
= tp->tv_sec * tickFrequency.QuadPart + (tp->tv_usec * tickFrequency.QuadPart) / 1000000L - tickNow.QuadPart;
// next caller can use ticks for time calculation
isInitialized = True;
return 0;
} else {
InterlockedDecrement(&initializeLock_gettimeofday);
// wait until first caller has initialized static values
while(!isInitialized){
Sleep(1);
}
}
}
// adjust our tick count so that we get a proper epoch:
tickNow.QuadPart += epochOffset.QuadPart;
tp->tv_sec = (long)(tickNow.QuadPart / tickFrequency.QuadPart);
tp->tv_usec = (long)(((tickNow.QuadPart % tickFrequency.QuadPart) * 1000000L) / tickFrequency.QuadPart);
return 0;
}
#endif
live/groupsock/include/ 000755 001752 001752 00000000000 13242237367 015117 5 ustar 00rsf rsf 000000 000000 live/groupsock/inet.c 000444 001752 001752 00000034525 13242237367 014606 0 ustar 00rsf rsf 000000 000000 #ifndef _NET_COMMON_H
#include "NetCommon.h"
#endif
#include
#ifdef VXWORKS
#include
#endif
/* Some systems (e.g., SunOS) have header files that erroneously declare inet_addr() as taking no arguments.
* This confuses C++. To overcome this, we use our own routine, implemented in C.
*/
unsigned our_inet_addr(cp)
char const* cp;
{
return inet_addr(cp);
}
#if defined(__WIN32__) || defined(_WIN32)
#ifndef IMN_PIM
#define WS_VERSION_CHOICE1 0x202/*MAKEWORD(2,2)*/
#define WS_VERSION_CHOICE2 0x101/*MAKEWORD(1,1)*/
int initializeWinsockIfNecessary(void) {
/* We need to call an initialization routine before
* we can do anything with winsock. (How fucking lame!):
*/
static int _haveInitializedWinsock = 0;
WSADATA wsadata;
if (!_haveInitializedWinsock) {
if ((WSAStartup(WS_VERSION_CHOICE1, &wsadata) != 0)
&& ((WSAStartup(WS_VERSION_CHOICE2, &wsadata)) != 0)) {
return 0; /* error in initialization */
}
if ((wsadata.wVersion != WS_VERSION_CHOICE1)
&& (wsadata.wVersion != WS_VERSION_CHOICE2)) {
WSACleanup();
return 0; /* desired Winsock version was not available */
}
_haveInitializedWinsock = 1;
}
return 1;
}
#else
int initializeWinsockIfNecessary(void) { return 1; }
#endif
#else
#define initializeWinsockIfNecessary() 1
#endif
#ifndef NULL
#define NULL 0
#endif
#ifdef USE_SYSTEM_RANDOM
/* Use the system-supplied "random()" and "srandom()" functions */
#include
long our_random() {
#if defined(__WIN32__) || defined(_WIN32)
return rand();
#else
return random();
#endif
}
void our_srandom(unsigned int x) {
#if defined(__WIN32__) || defined(_WIN32)
srand(x);
#else
srandom(x);
#endif
}
#else
/* Use our own implementation of the "random()" and "srandom()" functions */
/*
* random.c:
*
* An improved random number generation package. In addition to the standard
* rand()/srand() like interface, this package also has a special state info
* interface. The our_initstate() routine is called with a seed, an array of
* bytes, and a count of how many bytes are being passed in; this array is
* then initialized to contain information for random number generation with
* that much state information. Good sizes for the amount of state
* information are 32, 64, 128, and 256 bytes. The state can be switched by
* calling the our_setstate() routine with the same array as was initiallized
* with our_initstate(). By default, the package runs with 128 bytes of state
* information and generates far better random numbers than a linear
* congruential generator. If the amount of state information is less than
* 32 bytes, a simple linear congruential R.N.G. is used.
*
* Internally, the state information is treated as an array of longs; the
* zeroeth element of the array is the type of R.N.G. being used (small
* integer); the remainder of the array is the state information for the
* R.N.G. Thus, 32 bytes of state information will give 7 longs worth of
* state information, which will allow a degree seven polynomial. (Note:
* the zeroeth word of state information also has some other information
* stored in it -- see our_setstate() for details).
*
* The random number generation technique is a linear feedback shift register
* approach, employing trinomials (since there are fewer terms to sum up that
* way). In this approach, the least significant bit of all the numbers in
* the state table will act as a linear feedback shift register, and will
* have period 2^deg - 1 (where deg is the degree of the polynomial being
* used, assuming that the polynomial is irreducible and primitive). The
* higher order bits will have longer periods, since their values are also
* influenced by pseudo-random carries out of the lower bits. The total
* period of the generator is approximately deg*(2**deg - 1); thus doubling
* the amount of state information has a vast influence on the period of the
* generator. Note: the deg*(2**deg - 1) is an approximation only good for
* large deg, when the period of the shift register is the dominant factor.
* With deg equal to seven, the period is actually much longer than the
* 7*(2**7 - 1) predicted by this formula.
*/
/*
* For each of the currently supported random number generators, we have a
* break value on the amount of state information (you need at least this
* many bytes of state info to support this random number generator), a degree
* for the polynomial (actually a trinomial) that the R.N.G. is based on, and
* the separation between the two lower order coefficients of the trinomial.
*/
#define TYPE_0 0 /* linear congruential */
#define BREAK_0 8
#define DEG_0 0
#define SEP_0 0
#define TYPE_1 1 /* x**7 + x**3 + 1 */
#define BREAK_1 32
#define DEG_1 7
#define SEP_1 3
#define TYPE_2 2 /* x**15 + x + 1 */
#define BREAK_2 64
#define DEG_2 15
#define SEP_2 1
#define TYPE_3 3 /* x**31 + x**3 + 1 */
#define BREAK_3 128
#define DEG_3 31
#define SEP_3 3
#define TYPE_4 4 /* x**63 + x + 1 */
#define BREAK_4 256
#define DEG_4 63
#define SEP_4 1
/*
* Array versions of the above information to make code run faster --
* relies on fact that TYPE_i == i.
*/
#define MAX_TYPES 5 /* max number of types above */
static int const degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
static int const seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
/*
* Initially, everything is set up as if from:
*
* our_initstate(1, &randtbl, 128);
*
* Note that this initialization takes advantage of the fact that srandom()
* advances the front and rear pointers 10*rand_deg times, and hence the
* rear pointer which starts at 0 will also end up at zero; thus the zeroeth
* element of the state information, which contains info about the current
* position of the rear pointer is just
*
* MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3.
*/
static long randtbl[DEG_3 + 1] = {
TYPE_3,
0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, 0xde3b81e0, 0xdf0a6fb5,
0xf103bc02, 0x48f340fb, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd,
0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88,
0xe369735d, 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc,
0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b,
0x27fb47b9,
};
/*
* fptr and rptr are two pointers into the state info, a front and a rear
* pointer. These two pointers are always rand_sep places aparts, as they
* cycle cyclically through the state information. (Yes, this does mean we
* could get away with just one pointer, but the code for random() is more
* efficient this way). The pointers are left positioned as they would be
* from the call
*
* our_initstate(1, randtbl, 128);
*
* (The position of the rear pointer, rptr, is really 0 (as explained above
* in the initialization of randtbl) because the state table pointer is set
* to point to randtbl[1] (as explained below).
*/
static long* fptr = &randtbl[SEP_3 + 1];
static long* rptr = &randtbl[1];
/*
* The following things are the pointer to the state information table, the
* type of the current generator, the degree of the current polynomial being
* used, and the separation between the two pointers. Note that for efficiency
* of random(), we remember the first location of the state information, not
* the zeroeth. Hence it is valid to access state[-1], which is used to
* store the type of the R.N.G. Also, we remember the last location, since
* this is more efficient than indexing every time to find the address of
* the last element to see if the front and rear pointers have wrapped.
*/
static long *state = &randtbl[1];
static int rand_type = TYPE_3;
static int rand_deg = DEG_3;
static int rand_sep = SEP_3;
static long* end_ptr = &randtbl[DEG_3 + 1];
/*
* srandom:
*
* Initialize the random number generator based on the given seed. If the
* type is the trivial no-state-information type, just remember the seed.
* Otherwise, initializes state[] based on the given "seed" via a linear
* congruential generator. Then, the pointers are set to known locations
* that are exactly rand_sep places apart. Lastly, it cycles the state
* information a given number of times to get rid of any initial dependencies
* introduced by the L.C.R.N.G. Note that the initialization of randtbl[]
* for default usage relies on values produced by this routine.
*/
long our_random(void); /*forward*/
void
our_srandom(unsigned int x)
{
register int i;
if (rand_type == TYPE_0)
state[0] = x;
else {
state[0] = x;
for (i = 1; i < rand_deg; i++)
state[i] = 1103515245 * state[i - 1] + 12345;
fptr = &state[rand_sep];
rptr = &state[0];
for (i = 0; i < 10 * rand_deg; i++)
(void)our_random();
}
}
/*
* our_initstate:
*
* Initialize the state information in the given array of n bytes for future
* random number generation. Based on the number of bytes we are given, and
* the break values for the different R.N.G.'s, we choose the best (largest)
* one we can and set things up for it. srandom() is then called to
* initialize the state information.
*
* Note that on return from srandom(), we set state[-1] to be the type
* multiplexed with the current value of the rear pointer; this is so
* successive calls to our_initstate() won't lose this information and will be
* able to restart with our_setstate().
*
* Note: the first thing we do is save the current state, if any, just like
* our_setstate() so that it doesn't matter when our_initstate is called.
*
* Returns a pointer to the old state.
*/
char *
our_initstate(seed, arg_state, n)
unsigned int seed; /* seed for R.N.G. */
char *arg_state; /* pointer to state array */
int n; /* # bytes of state info */
{
register char *ostate = (char *)(&state[-1]);
if (rand_type == TYPE_0)
state[-1] = rand_type;
else
state[-1] = MAX_TYPES * (rptr - state) + rand_type;
if (n < BREAK_0) {
#ifdef DEBUG
(void)fprintf(stderr,
"random: not enough state (%d bytes); ignored.\n", n);
#endif
return(0);
}
if (n < BREAK_1) {
rand_type = TYPE_0;
rand_deg = DEG_0;
rand_sep = SEP_0;
} else if (n < BREAK_2) {
rand_type = TYPE_1;
rand_deg = DEG_1;
rand_sep = SEP_1;
} else if (n < BREAK_3) {
rand_type = TYPE_2;
rand_deg = DEG_2;
rand_sep = SEP_2;
} else if (n < BREAK_4) {
rand_type = TYPE_3;
rand_deg = DEG_3;
rand_sep = SEP_3;
} else {
rand_type = TYPE_4;
rand_deg = DEG_4;
rand_sep = SEP_4;
}
state = &(((long *)arg_state)[1]); /* first location */
end_ptr = &state[rand_deg]; /* must set end_ptr before srandom */
our_srandom(seed);
if (rand_type == TYPE_0)
state[-1] = rand_type;
else
state[-1] = MAX_TYPES*(rptr - state) + rand_type;
return(ostate);
}
/*
* our_setstate:
*
* Restore the state from the given state array.
*
* Note: it is important that we also remember the locations of the pointers
* in the current state information, and restore the locations of the pointers
* from the old state information. This is done by multiplexing the pointer
* location into the zeroeth word of the state information.
*
* Note that due to the order in which things are done, it is OK to call
* our_setstate() with the same state as the current state.
*
* Returns a pointer to the old state information.
*/
char *
our_setstate(arg_state)
char *arg_state;
{
register long *new_state = (long *)arg_state;
register int type = new_state[0] % MAX_TYPES;
register int rear = new_state[0] / MAX_TYPES;
char *ostate = (char *)(&state[-1]);
if (rand_type == TYPE_0)
state[-1] = rand_type;
else
state[-1] = MAX_TYPES * (rptr - state) + rand_type;
switch(type) {
case TYPE_0:
case TYPE_1:
case TYPE_2:
case TYPE_3:
case TYPE_4:
rand_type = type;
rand_deg = degrees[type];
rand_sep = seps[type];
break;
default:
#ifdef DEBUG
(void)fprintf(stderr,
"random: state info corrupted; not changed.\n");
#endif
break;
}
state = &new_state[1];
if (rand_type != TYPE_0) {
rptr = &state[rear];
fptr = &state[(rear + rand_sep) % rand_deg];
}
end_ptr = &state[rand_deg]; /* set end_ptr too */
return(ostate);
}
/*
* random:
*
* If we are using the trivial TYPE_0 R.N.G., just do the old linear
* congruential bit. Otherwise, we do our fancy trinomial stuff, which is
* the same in all the other cases due to all the global variables that have
* been set up. The basic operation is to add the number at the rear pointer
* into the one at the front pointer. Then both pointers are advanced to
* the next location cyclically in the table. The value returned is the sum
* generated, reduced to 31 bits by throwing away the "least random" low bit.
*
* Note: the code takes advantage of the fact that both the front and
* rear pointers can't wrap on the same call by not testing the rear
* pointer if the front one has wrapped.
*
* Returns a 31-bit random number.
*/
long our_random() {
long i;
if (rand_type == TYPE_0) {
i = state[0] = (state[0] * 1103515245 + 12345) & 0x7fffffff;
} else {
/* Make copies of "rptr" and "fptr" before working with them, in case we're being called concurrently by multiple threads: */
long* rp = rptr;
long* fp = fptr;
/* Make sure "rp" and "fp" are separated by the correct distance (again, allowing for concurrent access): */
if (!(fp == rp+SEP_3 || fp+DEG_3 == rp+SEP_3)) {
/* A rare case that should occur only if we're being called concurrently by multiple threads. */
/* Restore the proper separation between the pointers: */
if (rp <= fp) rp = fp-SEP_3; else rp = fp+DEG_3-SEP_3;
}
*fp += *rp;
i = (*fp >> 1) & 0x7fffffff; /* chucking least random bit */
if (++fp >= end_ptr) {
fp = state;
++rp;
} else if (++rp >= end_ptr) {
rp = state;
}
/* Restore "rptr" and "fptr" from our working copies: */
rptr = rp;
fptr = fp;
}
return i;
}
#endif
u_int32_t our_random32() {
/* Return a 32-bit random number.
Because "our_random()" returns a 31-bit random number, we call it a second
time, to generate the high bit.
(Actually, to increase the likelhood of randomness, we take the middle 16 bits of two successive calls to "our_random()")
*/
long random_1 = our_random();
u_int32_t random16_1 = (u_int32_t)(random_1&0x00FFFF00);
long random_2 = our_random();
u_int32_t random16_2 = (u_int32_t)(random_2&0x00FFFF00);
return (random16_1<<8) | (random16_2>>8);
}
#ifdef USE_OUR_BZERO
#ifndef __bzero
void
__bzero (to, count)
char *to;
int count;
{
while (count-- > 0)
{
*to++ = 0;
}
}
#endif
#endif
live/groupsock/IOHandlers.cpp 000444 001752 001752 00000003732 13242237367 016173 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// IO event handlers
// Implementation
#include "IOHandlers.hh"
#include "TunnelEncaps.hh"
//##### TEMP: Use a single buffer, sized for UDP tunnels:
//##### This assumes that the I/O handlers are non-reentrant
static unsigned const maxPacketLength = 50*1024; // bytes
// This is usually overkill, because UDP packets are usually no larger
// than the typical Ethernet MTU (1500 bytes). However, I've seen
// reports of Windows Media Servers sending UDP packets as large as
// 27 kBytes. These will probably undego lots of IP-level
// fragmentation, but that occurs below us. We just have to hope that
// fragments don't get lost.
static unsigned const ioBufferSize
= maxPacketLength + TunnelEncapsulationTrailerMaxSize;
static unsigned char ioBuffer[ioBufferSize];
void socketReadHandler(Socket* sock, int /*mask*/) {
unsigned bytesRead;
struct sockaddr_in fromAddress;
UsageEnvironment& saveEnv = sock->env();
// because handleRead(), if it fails, may delete "sock"
if (!sock->handleRead(ioBuffer, ioBufferSize, bytesRead, fromAddress)) {
saveEnv.reportBackgroundError();
}
}
live/groupsock/Makefile.head 000440 001752 001752 00000000217 13242237367 016026 0 ustar 00rsf rsf 000000 000000 INCLUDES = -Iinclude -I../UsageEnvironment/include
PREFIX = /usr/local
LIBDIR = $(PREFIX)/lib
##### Change the following for your environment:
live/groupsock/Makefile.tail 000444 001752 001752 00000003510 13242237367 016061 0 ustar 00rsf rsf 000000 000000 ##### End of variables to change
NAME = libgroupsock
ALL = $(NAME).$(LIB_SUFFIX)
all: $(ALL)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
GROUPSOCK_LIB_OBJS = GroupsockHelper.$(OBJ) GroupEId.$(OBJ) inet.$(OBJ) Groupsock.$(OBJ) NetInterface.$(OBJ) NetAddress.$(OBJ) IOHandlers.$(OBJ)
GroupsockHelper.$(CPP): include/GroupsockHelper.hh
include/GroupsockHelper.hh: include/NetAddress.hh
include/NetAddress.hh: include/NetCommon.h
GroupEId.$(CPP): include/GroupEId.hh
include/GroupEId.hh: include/NetAddress.hh
inet.$(C): include/NetCommon.h
Groupsock.$(CPP): include/Groupsock.hh include/GroupsockHelper.hh include/TunnelEncaps.hh
include/Groupsock.hh: include/groupsock_version.hh include/NetInterface.hh include/GroupEId.hh
include/NetInterface.hh: include/NetAddress.hh
include/TunnelEncaps.hh: include/NetAddress.hh
NetInterface.$(CPP): include/NetInterface.hh include/GroupsockHelper.hh
NetAddress.$(CPP): include/NetAddress.hh include/GroupsockHelper.hh
IOHandlers.$(CPP): include/IOHandlers.hh include/TunnelEncaps.hh
libgroupsock.$(LIB_SUFFIX): $(GROUPSOCK_LIB_OBJS) \
$(PLATFORM_SPECIFIC_LIB_OBJS)
$(LIBRARY_LINK)$@ $(LIBRARY_LINK_OPTS) \
$(GROUPSOCK_LIB_OBJS)
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
install: install1 $(INSTALL2)
install1: libgroupsock.$(LIB_SUFFIX)
install -d $(DESTDIR)$(PREFIX)/include/groupsock $(DESTDIR)$(LIBDIR)
install -m 644 include/*.hh include/*.h $(DESTDIR)$(PREFIX)/include/groupsock
install -m 644 libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)
install_shared_libraries: libgroupsock.$(LIB_SUFFIX)
ln -fs libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.$(SHORT_LIB_SUFFIX)
ln -fs libgroupsock.$(LIB_SUFFIX) $(DESTDIR)$(LIBDIR)/libgroupsock.so
##### Any additional, platform-specific rules come here:
live/groupsock/NetAddress.cpp 000444 001752 001752 00000021216 13242237367 016234 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Network Addresses
// Implementation
#include "NetAddress.hh"
#include "GroupsockHelper.hh"
#include
#include
#if defined(__WIN32__) || defined(_WIN32)
#define USE_GETHOSTBYNAME 1 /*because at least some Windows don't have getaddrinfo()*/
#else
#ifndef INADDR_NONE
#define INADDR_NONE 0xFFFFFFFF
#endif
#endif
////////// NetAddress //////////
NetAddress::NetAddress(u_int8_t const* data, unsigned length) {
assign(data, length);
}
NetAddress::NetAddress(unsigned length) {
fData = new u_int8_t[length];
if (fData == NULL) {
fLength = 0;
return;
}
for (unsigned i = 0; i < length; ++i) fData[i] = 0;
fLength = length;
}
NetAddress::NetAddress(NetAddress const& orig) {
assign(orig.data(), orig.length());
}
NetAddress& NetAddress::operator=(NetAddress const& rightSide) {
if (&rightSide != this) {
clean();
assign(rightSide.data(), rightSide.length());
}
return *this;
}
NetAddress::~NetAddress() {
clean();
}
void NetAddress::assign(u_int8_t const* data, unsigned length) {
fData = new u_int8_t[length];
if (fData == NULL) {
fLength = 0;
return;
}
for (unsigned i = 0; i < length; ++i) fData[i] = data[i];
fLength = length;
}
void NetAddress::clean() {
delete[] fData; fData = NULL;
fLength = 0;
}
////////// NetAddressList //////////
NetAddressList::NetAddressList(char const* hostname)
: fNumAddresses(0), fAddressArray(NULL) {
// First, check whether "hostname" is an IP address string:
netAddressBits addr = our_inet_addr((char*)hostname);
if (addr != INADDR_NONE) {
// Yes, it was an IP address string. Return a 1-element list with this address:
fNumAddresses = 1;
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
fAddressArray[0] = new NetAddress((u_int8_t*)&addr, sizeof (netAddressBits));
return;
}
// "hostname" is not an IP address string; try resolving it as a real host name instead:
#if defined(USE_GETHOSTBYNAME) || defined(VXWORKS)
struct hostent* host;
#if defined(VXWORKS)
char hostentBuf[512];
host = (struct hostent*)resolvGetHostByName((char*)hostname, (char*)&hostentBuf, sizeof hostentBuf);
#else
host = gethostbyname((char*)hostname);
#endif
if (host == NULL || host->h_length != 4 || host->h_addr_list == NULL) return; // no luck
u_int8_t const** const hAddrPtr = (u_int8_t const**)host->h_addr_list;
// First, count the number of addresses:
u_int8_t const** hAddrPtr1 = hAddrPtr;
while (*hAddrPtr1 != NULL) {
++fNumAddresses;
++hAddrPtr1;
}
// Next, set up the list:
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
for (unsigned i = 0; i < fNumAddresses; ++i) {
fAddressArray[i] = new NetAddress(hAddrPtr[i], host->h_length);
}
#else
// Use "getaddrinfo()" (rather than the older, deprecated "gethostbyname()"):
struct addrinfo addrinfoHints;
memset(&addrinfoHints, 0, sizeof addrinfoHints);
addrinfoHints.ai_family = AF_INET; // For now, we're interested in IPv4 addresses only
struct addrinfo* addrinfoResultPtr = NULL;
int result = getaddrinfo(hostname, NULL, &addrinfoHints, &addrinfoResultPtr);
if (result != 0 || addrinfoResultPtr == NULL) return; // no luck
// First, count the number of addresses:
const struct addrinfo* p = addrinfoResultPtr;
while (p != NULL) {
if (p->ai_addrlen < 4) continue; // sanity check: skip over addresses that are too small
++fNumAddresses;
p = p->ai_next;
}
// Next, set up the list:
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
unsigned i = 0;
p = addrinfoResultPtr;
while (p != NULL) {
if (p->ai_addrlen < 4) continue;
fAddressArray[i++] = new NetAddress((u_int8_t const*)&(((struct sockaddr_in*)p->ai_addr)->sin_addr.s_addr), 4);
p = p->ai_next;
}
// Finally, free the data that we had allocated by calling "getaddrinfo()":
freeaddrinfo(addrinfoResultPtr);
#endif
}
NetAddressList::NetAddressList(NetAddressList const& orig) {
assign(orig.numAddresses(), orig.fAddressArray);
}
NetAddressList& NetAddressList::operator=(NetAddressList const& rightSide) {
if (&rightSide != this) {
clean();
assign(rightSide.numAddresses(), rightSide.fAddressArray);
}
return *this;
}
NetAddressList::~NetAddressList() {
clean();
}
void NetAddressList::assign(unsigned numAddresses, NetAddress** addressArray) {
fAddressArray = new NetAddress*[numAddresses];
if (fAddressArray == NULL) {
fNumAddresses = 0;
return;
}
for (unsigned i = 0; i < numAddresses; ++i) {
fAddressArray[i] = new NetAddress(*addressArray[i]);
}
fNumAddresses = numAddresses;
}
void NetAddressList::clean() {
while (fNumAddresses-- > 0) {
delete fAddressArray[fNumAddresses];
}
delete[] fAddressArray; fAddressArray = NULL;
}
NetAddress const* NetAddressList::firstAddress() const {
if (fNumAddresses == 0) return NULL;
return fAddressArray[0];
}
////////// NetAddressList::Iterator //////////
NetAddressList::Iterator::Iterator(NetAddressList const& addressList)
: fAddressList(addressList), fNextIndex(0) {}
NetAddress const* NetAddressList::Iterator::nextAddress() {
if (fNextIndex >= fAddressList.numAddresses()) return NULL; // no more
return fAddressList.fAddressArray[fNextIndex++];
}
////////// Port //////////
Port::Port(portNumBits num /* in host byte order */) {
fPortNum = htons(num);
}
UsageEnvironment& operator<<(UsageEnvironment& s, const Port& p) {
return s << ntohs(p.num());
}
////////// AddressPortLookupTable //////////
AddressPortLookupTable::AddressPortLookupTable()
: fTable(HashTable::create(3)) { // three-word keys are used
}
AddressPortLookupTable::~AddressPortLookupTable() {
delete fTable;
}
void* AddressPortLookupTable::Add(netAddressBits address1,
netAddressBits address2,
Port port, void* value) {
int key[3];
key[0] = (int)address1;
key[1] = (int)address2;
key[2] = (int)port.num();
return fTable->Add((char*)key, value);
}
void* AddressPortLookupTable::Lookup(netAddressBits address1,
netAddressBits address2,
Port port) {
int key[3];
key[0] = (int)address1;
key[1] = (int)address2;
key[2] = (int)port.num();
return fTable->Lookup((char*)key);
}
Boolean AddressPortLookupTable::Remove(netAddressBits address1,
netAddressBits address2,
Port port) {
int key[3];
key[0] = (int)address1;
key[1] = (int)address2;
key[2] = (int)port.num();
return fTable->Remove((char*)key);
}
AddressPortLookupTable::Iterator::Iterator(AddressPortLookupTable& table)
: fIter(HashTable::Iterator::create(*(table.fTable))) {
}
AddressPortLookupTable::Iterator::~Iterator() {
delete fIter;
}
void* AddressPortLookupTable::Iterator::next() {
char const* key; // dummy
return fIter->next(key);
}
////////// isMulticastAddress() implementation //////////
Boolean IsMulticastAddress(netAddressBits address) {
// Note: We return False for addresses in the range 224.0.0.0
// through 224.0.0.255, because these are non-routable
// Note: IPv4-specific #####
netAddressBits addressInNetworkOrder = htonl(address);
return addressInNetworkOrder > 0xE00000FF &&
addressInNetworkOrder <= 0xEFFFFFFF;
}
////////// AddressString implementation //////////
AddressString::AddressString(struct sockaddr_in const& addr) {
init(addr.sin_addr.s_addr);
}
AddressString::AddressString(struct in_addr const& addr) {
init(addr.s_addr);
}
AddressString::AddressString(netAddressBits addr) {
init(addr);
}
void AddressString::init(netAddressBits addr) {
fVal = new char[16]; // large enough for "abc.def.ghi.jkl"
netAddressBits addrNBO = htonl(addr); // make sure we have a value in a known byte order: big endian
sprintf(fVal, "%u.%u.%u.%u", (addrNBO>>24)&0xFF, (addrNBO>>16)&0xFF, (addrNBO>>8)&0xFF, addrNBO&0xFF);
}
AddressString::~AddressString() {
delete[] fVal;
}
live/groupsock/NetInterface.cpp 000444 001752 001752 00000011102 13242237367 016540 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Network Interfaces
// Implementation
#include "NetInterface.hh"
#include "GroupsockHelper.hh"
#ifndef NO_SSTREAM
#include
#endif
#include
////////// NetInterface //////////
UsageEnvironment* NetInterface::DefaultUsageEnvironment = NULL;
NetInterface::NetInterface() {
}
NetInterface::~NetInterface() {
}
////////// NetInterface //////////
DirectedNetInterface::DirectedNetInterface() {
}
DirectedNetInterface::~DirectedNetInterface() {
}
////////// DirectedNetInterfaceSet //////////
DirectedNetInterfaceSet::DirectedNetInterfaceSet()
: fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
}
DirectedNetInterfaceSet::~DirectedNetInterfaceSet() {
delete fTable;
}
DirectedNetInterface*
DirectedNetInterfaceSet::Add(DirectedNetInterface const* interf) {
return (DirectedNetInterface*) fTable->Add((char*)interf, (void*)interf);
}
Boolean
DirectedNetInterfaceSet::Remove(DirectedNetInterface const* interf) {
return fTable->Remove((char*)interf);
}
DirectedNetInterfaceSet::Iterator::
Iterator(DirectedNetInterfaceSet& interfaces)
: fIter(HashTable::Iterator::create(*(interfaces.fTable))) {
}
DirectedNetInterfaceSet::Iterator::~Iterator() {
delete fIter;
}
DirectedNetInterface* DirectedNetInterfaceSet::Iterator::next() {
char const* key; // dummy
return (DirectedNetInterface*) fIter->next(key);
};
////////// Socket //////////
int Socket::DebugLevel = 1; // default value
Socket::Socket(UsageEnvironment& env, Port port)
: fEnv(DefaultUsageEnvironment != NULL ? *DefaultUsageEnvironment : env), fPort(port) {
fSocketNum = setupDatagramSocket(fEnv, port);
}
void Socket::reset() {
if (fSocketNum >= 0) closeSocket(fSocketNum);
fSocketNum = -1;
}
Socket::~Socket() {
reset();
}
Boolean Socket::changePort(Port newPort) {
int oldSocketNum = fSocketNum;
unsigned oldReceiveBufferSize = getReceiveBufferSize(fEnv, fSocketNum);
unsigned oldSendBufferSize = getSendBufferSize(fEnv, fSocketNum);
closeSocket(fSocketNum);
fSocketNum = setupDatagramSocket(fEnv, newPort);
if (fSocketNum < 0) {
fEnv.taskScheduler().turnOffBackgroundReadHandling(oldSocketNum);
return False;
}
setReceiveBufferTo(fEnv, fSocketNum, oldReceiveBufferSize);
setSendBufferTo(fEnv, fSocketNum, oldSendBufferSize);
if (fSocketNum != oldSocketNum) { // the socket number has changed, so move any event handling for it:
fEnv.taskScheduler().moveSocketHandling(oldSocketNum, fSocketNum);
}
return True;
}
UsageEnvironment& operator<<(UsageEnvironment& s, const Socket& sock) {
return s << timestampString() << " Socket(" << sock.socketNum() << ")";
}
////////// SocketLookupTable //////////
SocketLookupTable::SocketLookupTable()
: fTable(HashTable::create(ONE_WORD_HASH_KEYS)) {
}
SocketLookupTable::~SocketLookupTable() {
delete fTable;
}
Socket* SocketLookupTable::Fetch(UsageEnvironment& env, Port port,
Boolean& isNew) {
isNew = False;
Socket* sock;
do {
sock = (Socket*) fTable->Lookup((char*)(long)(port.num()));
if (sock == NULL) { // we need to create one:
sock = CreateNew(env, port);
if (sock == NULL || sock->socketNum() < 0) break;
fTable->Add((char*)(long)(port.num()), (void*)sock);
isNew = True;
}
return sock;
} while (0);
delete sock;
return NULL;
}
Boolean SocketLookupTable::Remove(Socket const* sock) {
return fTable->Remove( (char*)(long)(sock->port().num()) );
}
////////// NetInterfaceTrafficStats //////////
NetInterfaceTrafficStats::NetInterfaceTrafficStats() {
fTotNumPackets = fTotNumBytes = 0.0;
}
void NetInterfaceTrafficStats::countPacket(unsigned packetSize) {
fTotNumPackets += 1.0;
fTotNumBytes += packetSize;
}
Boolean NetInterfaceTrafficStats::haveSeenTraffic() const {
return fTotNumPackets != 0.0;
}
live/groupsock/COPYING.LESSER 000755 001752 001752 00000000000 13242237367 017756 2../COPYING.LESSER ustar 00rsf rsf 000000 000000 live/groupsock/include/GroupEId.hh 000444 001752 001752 00000003703 13242237367 017117 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "multikit" Multicast Application Shell
// Copyright (c) 1996-2018, Live Networks, Inc. All rights reserved
// "Group Endpoint Id"
// C++ header
#ifndef _GROUPEID_HH
#define _GROUPEID_HH
#ifndef _BOOLEAN_HH
#include "Boolean.hh"
#endif
#ifndef _NET_ADDRESS_HH
#include "NetAddress.hh"
#endif
class GroupEId {
public:
GroupEId(struct in_addr const& groupAddr,
portNumBits portNum, u_int8_t ttl);
// used for a 'source-independent multicast' group
GroupEId(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
portNumBits portNum);
// used for a 'source-specific multicast' group
struct in_addr const& groupAddress() const { return fGroupAddress; }
struct in_addr const& sourceFilterAddress() const { return fSourceFilterAddress; }
Boolean isSSM() const;
portNumBits portNum() const { return fPortNum; }
u_int8_t ttl() const { return fTTL; }
private:
void init(struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
portNumBits portNum,
u_int8_t ttl);
private:
struct in_addr fGroupAddress;
struct in_addr fSourceFilterAddress;
portNumBits fPortNum; // in network byte order
u_int8_t fTTL;
};
#endif
live/groupsock/include/Groupsock.hh 000444 001752 001752 00000017303 13242237367 017416 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// 'Group sockets'
// C++ header
#ifndef _GROUPSOCK_HH
#define _GROUPSOCK_HH
#ifndef _GROUPSOCK_VERSION_HH
#include "groupsock_version.hh"
#endif
#ifndef _NET_INTERFACE_HH
#include "NetInterface.hh"
#endif
#ifndef _GROUPEID_HH
#include "GroupEId.hh"
#endif
// An "OutputSocket" is (by default) used only to send packets.
// No packets are received on it (unless a subclass arranges this)
class OutputSocket: public Socket {
public:
OutputSocket(UsageEnvironment& env);
virtual ~OutputSocket();
virtual Boolean write(netAddressBits address, portNumBits portNum/*in network order*/, u_int8_t ttl,
unsigned char* buffer, unsigned bufferSize);
Boolean write(struct sockaddr_in& addressAndPort, u_int8_t ttl,
unsigned char* buffer, unsigned bufferSize) {
return write(addressAndPort.sin_addr.s_addr, addressAndPort.sin_port, ttl, buffer, bufferSize);
}
protected:
OutputSocket(UsageEnvironment& env, Port port);
portNumBits sourcePortNum() const {return fSourcePort.num();}
private: // redefined virtual function
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
struct sockaddr_in& fromAddressAndPort);
private:
Port fSourcePort;
unsigned fLastSentTTL;
};
class destRecord {
public:
destRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId,
destRecord* next);
virtual ~destRecord();
public:
destRecord* fNext;
GroupEId fGroupEId;
unsigned fSessionId;
};
// A "Groupsock" is used to both send and receive packets.
// As the name suggests, it was originally designed to send/receive
// multicast, but it can send/receive unicast as well.
class Groupsock: public OutputSocket {
public:
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
Port port, u_int8_t ttl);
// used for a 'source-independent multicast' group
Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
struct in_addr const& sourceFilterAddr,
Port port);
// used for a 'source-specific multicast' group
virtual ~Groupsock();
virtual destRecord* createNewDestRecord(struct in_addr const& addr, Port const& port, u_int8_t ttl, unsigned sessionId, destRecord* next);
// Can be redefined by subclasses that also subclass "destRecord"
void changeDestinationParameters(struct in_addr const& newDestAddr,
Port newDestPort, int newDestTTL,
unsigned sessionId = 0);
// By default, the destination address, port and ttl for
// outgoing packets are those that were specified in
// the constructor. This works OK for multicast sockets,
// but for unicast we usually want the destination port
// number, at least, to be different from the source port.
// (If a parameter is 0 (or ~0 for ttl), then no change is made to that parameter.)
// (If no existing "destRecord" exists with this "sessionId", then we add a new "destRecord".)
unsigned lookupSessionIdFromDestination(struct sockaddr_in const& destAddrAndPort) const;
// returns 0 if not found
// As a special case, we also allow multiple destinations (addresses & ports)
// (This can be used to implement multi-unicast.)
virtual void addDestination(struct in_addr const& addr, Port const& port, unsigned sessionId);
virtual void removeDestination(unsigned sessionId);
void removeAllDestinations();
Boolean hasMultipleDestinations() const { return fDests != NULL && fDests->fNext != NULL; }
struct in_addr const& groupAddress() const {
return fIncomingGroupEId.groupAddress();
}
struct in_addr const& sourceFilterAddress() const {
return fIncomingGroupEId.sourceFilterAddress();
}
Boolean isSSM() const {
return fIncomingGroupEId.isSSM();
}
u_int8_t ttl() const { return fIncomingGroupEId.ttl(); }
void multicastSendOnly(); // send, but don't receive any multicast packets
virtual Boolean output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize,
DirectedNetInterface* interfaceNotToFwdBackTo = NULL);
DirectedNetInterfaceSet& members() { return fMembers; }
Boolean deleteIfNoMembers;
Boolean isSlave; // for tunneling
static NetInterfaceTrafficStats statsIncoming;
static NetInterfaceTrafficStats statsOutgoing;
static NetInterfaceTrafficStats statsRelayedIncoming;
static NetInterfaceTrafficStats statsRelayedOutgoing;
NetInterfaceTrafficStats statsGroupIncoming; // *not* static
NetInterfaceTrafficStats statsGroupOutgoing; // *not* static
NetInterfaceTrafficStats statsGroupRelayedIncoming; // *not* static
NetInterfaceTrafficStats statsGroupRelayedOutgoing; // *not* static
Boolean wasLoopedBackFromUs(UsageEnvironment& env, struct sockaddr_in& fromAddressAndPort);
public: // redefined virtual functions
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
struct sockaddr_in& fromAddressAndPort);
protected:
destRecord* lookupDestRecordFromDestination(struct sockaddr_in const& destAddrAndPort) const;
private:
void removeDestinationFrom(destRecord*& dests, unsigned sessionId);
// used to implement (the public) "removeDestination()", and "changeDestinationParameters()"
int outputToAllMembersExcept(DirectedNetInterface* exceptInterface,
u_int8_t ttlToFwd,
unsigned char* data, unsigned size,
netAddressBits sourceAddr);
protected:
destRecord* fDests;
private:
GroupEId fIncomingGroupEId;
DirectedNetInterfaceSet fMembers;
};
UsageEnvironment& operator<<(UsageEnvironment& s, const Groupsock& g);
// A data structure for looking up a 'groupsock'
// by (multicast address, port), or by socket number
class GroupsockLookupTable {
public:
Groupsock* Fetch(UsageEnvironment& env, netAddressBits groupAddress,
Port port, u_int8_t ttl, Boolean& isNew);
// Creates a new Groupsock if none already exists
Groupsock* Fetch(UsageEnvironment& env, netAddressBits groupAddress,
netAddressBits sourceFilterAddr,
Port port, Boolean& isNew);
// Creates a new Groupsock if none already exists
Groupsock* Lookup(netAddressBits groupAddress, Port port);
// Returns NULL if none already exists
Groupsock* Lookup(netAddressBits groupAddress,
netAddressBits sourceFilterAddr,
Port port);
// Returns NULL if none already exists
Groupsock* Lookup(UsageEnvironment& env, int sock);
// Returns NULL if none already exists
Boolean Remove(Groupsock const* groupsock);
// Used to iterate through the groupsocks in the table
class Iterator {
public:
Iterator(GroupsockLookupTable& groupsocks);
Groupsock* next(); // NULL iff none
private:
AddressPortLookupTable::Iterator fIter;
};
private:
Groupsock* AddNew(UsageEnvironment& env,
netAddressBits groupAddress,
netAddressBits sourceFilterAddress,
Port port, u_int8_t ttl);
private:
friend class Iterator;
AddressPortLookupTable fTable;
};
#endif
live/groupsock/include/groupsock_version.hh 000444 001752 001752 00000000446 13242237367 021223 0 ustar 00rsf rsf 000000 000000 // Version information for the "groupsock" library
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
#ifndef _GROUPSOCK_VERSION_HH
#define _GROUPSOCK_VERSION_HH
#define GROUPSOCK_LIBRARY_VERSION_STRING "2018.02.18"
#define GROUPSOCK_LIBRARY_VERSION_INT 1518912000
#endif
live/groupsock/include/GroupsockHelper.hh 000444 001752 001752 00000012135 13242237367 020554 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Helper routines to implement 'group sockets'
// C++ header
#ifndef _GROUPSOCK_HELPER_HH
#define _GROUPSOCK_HELPER_HH
#ifndef _NET_ADDRESS_HH
#include "NetAddress.hh"
#endif
int setupDatagramSocket(UsageEnvironment& env, Port port);
int setupStreamSocket(UsageEnvironment& env,
Port port, Boolean makeNonBlocking = True);
int readSocket(UsageEnvironment& env,
int socket, unsigned char* buffer, unsigned bufferSize,
struct sockaddr_in& fromAddress);
Boolean writeSocket(UsageEnvironment& env,
int socket, struct in_addr address, portNumBits portNum/*network byte order*/,
u_int8_t ttlArg,
unsigned char* buffer, unsigned bufferSize);
Boolean writeSocket(UsageEnvironment& env,
int socket, struct in_addr address, portNumBits portNum/*network byte order*/,
unsigned char* buffer, unsigned bufferSize);
// An optimized version of "writeSocket" that omits the "setsockopt()" call to set the TTL.
void ignoreSigPipeOnSocket(int socketNum);
unsigned getSendBufferSize(UsageEnvironment& env, int socket);
unsigned getReceiveBufferSize(UsageEnvironment& env, int socket);
unsigned setSendBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize);
unsigned setReceiveBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize);
unsigned increaseSendBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize);
unsigned increaseReceiveBufferTo(UsageEnvironment& env,
int socket, unsigned requestedSize);
Boolean makeSocketNonBlocking(int sock);
Boolean makeSocketBlocking(int sock, unsigned writeTimeoutInMilliseconds = 0);
// A "writeTimeoutInMilliseconds" value of 0 means: Don't timeout
Boolean socketJoinGroup(UsageEnvironment& env, int socket,
netAddressBits groupAddress);
Boolean socketLeaveGroup(UsageEnvironment&, int socket,
netAddressBits groupAddress);
// source-specific multicast join/leave
Boolean socketJoinGroupSSM(UsageEnvironment& env, int socket,
netAddressBits groupAddress,
netAddressBits sourceFilterAddr);
Boolean socketLeaveGroupSSM(UsageEnvironment&, int socket,
netAddressBits groupAddress,
netAddressBits sourceFilterAddr);
Boolean getSourcePort(UsageEnvironment& env, int socket, Port& port);
netAddressBits ourIPAddress(UsageEnvironment& env); // in network order
// IP addresses of our sending and receiving interfaces. (By default, these
// are INADDR_ANY (i.e., 0), specifying the default interface.)
extern netAddressBits SendingInterfaceAddr;
extern netAddressBits ReceivingInterfaceAddr;
// Allocates a randomly-chosen IPv4 SSM (multicast) address:
netAddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env);
// Returns a simple "hh:mm:ss" string, for use in debugging output (e.g.)
char const* timestampString();
#ifdef HAVE_SOCKADDR_LEN
#define SET_SOCKADDR_SIN_LEN(var) var.sin_len = sizeof var
#else
#define SET_SOCKADDR_SIN_LEN(var)
#endif
#define MAKE_SOCKADDR_IN(var,adr,prt) /*adr,prt must be in network order*/\
struct sockaddr_in var;\
var.sin_family = AF_INET;\
var.sin_addr.s_addr = (adr);\
var.sin_port = (prt);\
SET_SOCKADDR_SIN_LEN(var);
// By default, we create sockets with the SO_REUSE_* flag set.
// If, instead, you want to create sockets without the SO_REUSE_* flags,
// Then enclose the creation code with:
// {
// NoReuse dummy;
// ...
// }
class NoReuse {
public:
NoReuse(UsageEnvironment& env);
~NoReuse();
private:
UsageEnvironment& fEnv;
};
// Define the "UsageEnvironment"-specific "groupsockPriv" structure:
struct _groupsockPriv { // There should be only one of these allocated
HashTable* socketTable;
int reuseFlag;
};
_groupsockPriv* groupsockPriv(UsageEnvironment& env); // allocates it if necessary
void reclaimGroupsockPriv(UsageEnvironment& env);
#if (defined(__WIN32__) || defined(_WIN32)) && !defined(__MINGW32__)
// For Windoze, we need to implement our own gettimeofday()
extern int gettimeofday(struct timeval*, int*);
#else
#include
#endif
// The following are implemented in inet.c:
extern "C" netAddressBits our_inet_addr(char const*);
extern "C" void our_srandom(int x);
extern "C" long our_random();
extern "C" u_int32_t our_random32(); // because "our_random()" returns a 31-bit number
#endif
live/groupsock/include/IOHandlers.hh 000444 001752 001752 00000002140 13242237367 017423 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// IO event handlers
// C++ header
#ifndef _IO_HANDLERS_HH
#define _IO_HANDLERS_HH
#ifndef _NET_INTERFACE_HH
#include "NetInterface.hh"
#endif
// Handles incoming data on sockets:
void socketReadHandler(Socket* sock, int mask);
#endif
live/groupsock/include/NetAddress.hh 000444 001752 001752 00000011011 13242237367 017464 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Network Addresses
// C++ header
#ifndef _NET_ADDRESS_HH
#define _NET_ADDRESS_HH
#ifndef _HASH_TABLE_HH
#include "HashTable.hh"
#endif
#ifndef _NET_COMMON_H
#include "NetCommon.h"
#endif
#ifndef _USAGE_ENVIRONMENT_HH
#include "UsageEnvironment.hh"
#endif
// Definition of a type representing a low-level network address.
// At present, this is 32-bits, for IPv4. Later, generalize it,
// to allow for IPv6.
typedef u_int32_t netAddressBits;
class NetAddress {
public:
NetAddress(u_int8_t const* data,
unsigned length = 4 /* default: 32 bits */);
NetAddress(unsigned length = 4); // sets address data to all-zeros
NetAddress(NetAddress const& orig);
NetAddress& operator=(NetAddress const& rightSide);
virtual ~NetAddress();
unsigned length() const { return fLength; }
u_int8_t const* data() const // always in network byte order
{ return fData; }
private:
void assign(u_int8_t const* data, unsigned length);
void clean();
unsigned fLength;
u_int8_t* fData;
};
class NetAddressList {
public:
NetAddressList(char const* hostname);
NetAddressList(NetAddressList const& orig);
NetAddressList& operator=(NetAddressList const& rightSide);
virtual ~NetAddressList();
unsigned numAddresses() const { return fNumAddresses; }
NetAddress const* firstAddress() const;
// Used to iterate through the addresses in a list:
class Iterator {
public:
Iterator(NetAddressList const& addressList);
NetAddress const* nextAddress(); // NULL iff none
private:
NetAddressList const& fAddressList;
unsigned fNextIndex;
};
private:
void assign(netAddressBits numAddresses, NetAddress** addressArray);
void clean();
friend class Iterator;
unsigned fNumAddresses;
NetAddress** fAddressArray;
};
typedef u_int16_t portNumBits;
class Port {
public:
Port(portNumBits num /* in host byte order */);
portNumBits num() const { return fPortNum; } // in network byte order
private:
portNumBits fPortNum; // stored in network byte order
#ifdef IRIX
portNumBits filler; // hack to overcome a bug in IRIX C++ compiler
#endif
};
UsageEnvironment& operator<<(UsageEnvironment& s, const Port& p);
// A generic table for looking up objects by (address1, address2, port)
class AddressPortLookupTable {
public:
AddressPortLookupTable();
virtual ~AddressPortLookupTable();
void* Add(netAddressBits address1, netAddressBits address2, Port port, void* value);
// Returns the old value if different, otherwise 0
Boolean Remove(netAddressBits address1, netAddressBits address2, Port port);
void* Lookup(netAddressBits address1, netAddressBits address2, Port port);
// Returns 0 if not found
void* RemoveNext() { return fTable->RemoveNext(); }
// Used to iterate through the entries in the table
class Iterator {
public:
Iterator(AddressPortLookupTable& table);
virtual ~Iterator();
void* next(); // NULL iff none
private:
HashTable::Iterator* fIter;
};
private:
friend class Iterator;
HashTable* fTable;
};
Boolean IsMulticastAddress(netAddressBits address);
// A mechanism for displaying an IPv4 address in ASCII. This is intended to replace "inet_ntoa()", which is not thread-safe.
class AddressString {
public:
AddressString(struct sockaddr_in const& addr);
AddressString(struct in_addr const& addr);
AddressString(netAddressBits addr); // "addr" is assumed to be in host byte order here
virtual ~AddressString();
char const* val() const { return fVal; }
private:
void init(netAddressBits addr); // used to implement each of the constructors
private:
char* fVal; // The result ASCII string: allocated by the constructor; deleted by the destructor
};
#endif
live/groupsock/include/NetCommon.h 000444 001752 001752 00000006230 13242237367 017166 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
/* "groupsock" interface
* Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
* Common include files, typically used for networking
*/
#ifndef _NET_COMMON_H
#define _NET_COMMON_H
#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_WCE)
/* Windows */
#if defined(WINNT) || defined(_WINNT) || defined(__BORLANDC__) || defined(__MINGW32__) || defined(_WIN32_WCE) || defined (_MSC_VER)
#define _MSWSOCK_
#include
#include
#endif
#include
#include
#include
#define closeSocket closesocket
#ifdef EWOULDBLOCK
#undef EWOULDBLOCK
#endif
#ifdef EINPROGRESS
#undef EINPROGRESS
#endif
#ifdef EAGAIN
#undef EAGAIN
#endif
#ifdef EINTR
#undef EINTR
#endif
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEWOULDBLOCK
#define EAGAIN WSAEWOULDBLOCK
#define EINTR WSAEINTR
#if defined(_WIN32_WCE)
#define NO_STRSTREAM 1
#endif
/* Definitions of size-specific types: */
typedef __int64 int64_t;
typedef unsigned __int64 u_int64_t;
typedef int int32_t;
typedef unsigned u_int32_t;
typedef short int16_t;
typedef unsigned short u_int16_t;
typedef unsigned char u_int8_t;
// For "uintptr_t" and "intptr_t", we assume that if they're not already defined, then this must be
// an old, 32-bit version of Windows:
#if !defined(_MSC_STDINT_H_) && !defined(_UINTPTR_T_DEFINED) && !defined(_UINTPTR_T_DECLARED) && !defined(_UINTPTR_T)
typedef unsigned uintptr_t;
#endif
#if !defined(_MSC_STDINT_H_) && !defined(_INTPTR_T_DEFINED) && !defined(_INTPTR_T_DECLARED) && !defined(_INTPTR_T)
typedef int intptr_t;
#endif
#elif defined(VXWORKS)
/* VxWorks */
#include
#include
#include
#include
#include
#include
#include
typedef unsigned int u_int32_t;
typedef unsigned short u_int16_t;
typedef unsigned char u_int8_t;
#else
/* Unix */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(_QNX4)
#include
#include
#endif
#define closeSocket close
#ifdef SOLARIS
#define u_int64_t uint64_t
#define u_int32_t uint32_t
#define u_int16_t uint16_t
#define u_int8_t uint8_t
#endif
#endif
#ifndef SOCKLEN_T
#define SOCKLEN_T int
#endif
#endif
live/groupsock/include/NetInterface.hh 000444 001752 001752 00000007402 13242237367 020010 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Network Interfaces
// C++ header
#ifndef _NET_INTERFACE_HH
#define _NET_INTERFACE_HH
#ifndef _NET_ADDRESS_HH
#include "NetAddress.hh"
#endif
class NetInterface {
public:
virtual ~NetInterface();
static UsageEnvironment* DefaultUsageEnvironment;
// if non-NULL, used for each new interfaces
protected:
NetInterface(); // virtual base class
};
class DirectedNetInterface: public NetInterface {
public:
virtual ~DirectedNetInterface();
virtual Boolean write(unsigned char* data, unsigned numBytes) = 0;
virtual Boolean SourceAddrOKForRelaying(UsageEnvironment& env,
unsigned addr) = 0;
protected:
DirectedNetInterface(); // virtual base class
};
class DirectedNetInterfaceSet {
public:
DirectedNetInterfaceSet();
virtual ~DirectedNetInterfaceSet();
DirectedNetInterface* Add(DirectedNetInterface const* interf);
// Returns the old value if different, otherwise 0
Boolean Remove(DirectedNetInterface const* interf);
Boolean IsEmpty() { return fTable->IsEmpty(); }
// Used to iterate through the interfaces in the set
class Iterator {
public:
Iterator(DirectedNetInterfaceSet& interfaces);
virtual ~Iterator();
DirectedNetInterface* next(); // NULL iff none
private:
HashTable::Iterator* fIter;
};
private:
friend class Iterator;
HashTable* fTable;
};
class Socket: public NetInterface {
public:
virtual ~Socket();
void reset(); // closes the socket, and sets "fSocketNum" to -1
virtual Boolean handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead,
struct sockaddr_in& fromAddress) = 0;
// Returns False on error; resultData == NULL if data ignored
int socketNum() const { return fSocketNum; }
Port port() const {
return fPort;
}
UsageEnvironment& env() const { return fEnv; }
static int DebugLevel;
protected:
Socket(UsageEnvironment& env, Port port); // virtual base class
Boolean changePort(Port newPort); // will also cause socketNum() to change
private:
int fSocketNum;
UsageEnvironment& fEnv;
Port fPort;
};
UsageEnvironment& operator<<(UsageEnvironment& s, const Socket& sock);
// A data structure for looking up a Socket by port:
class SocketLookupTable {
public:
virtual ~SocketLookupTable();
Socket* Fetch(UsageEnvironment& env, Port port, Boolean& isNew);
// Creates a new Socket if none already exists
Boolean Remove(Socket const* sock);
protected:
SocketLookupTable(); // abstract base class
virtual Socket* CreateNew(UsageEnvironment& env, Port port) = 0;
private:
HashTable* fTable;
};
// A data structure for counting traffic:
class NetInterfaceTrafficStats {
public:
NetInterfaceTrafficStats();
void countPacket(unsigned packetSize);
float totNumPackets() const {return fTotNumPackets;}
float totNumBytes() const {return fTotNumBytes;}
Boolean haveSeenTraffic() const;
private:
float fTotNumPackets;
float fTotNumBytes;
};
#endif
live/groupsock/include/TunnelEncaps.hh 000444 001752 001752 00000006575 13242237367 020052 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See .)
This 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 this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "mTunnel" multicast access service
// Copyright (c) 1996-2018 Live Networks, Inc. All rights reserved.
// Encapsulation trailer for tunnels
// C++ header
#ifndef _TUNNEL_ENCAPS_HH
#define _TUNNEL_ENCAPS_HH
#ifndef _NET_ADDRESS_HH
#include "NetAddress.hh"
#endif
typedef u_int16_t Cookie;
class TunnelEncapsulationTrailer {
// The trailer is layed out as follows:
// bytes 0-1: source 'cookie'
// bytes 2-3: destination 'cookie'
// bytes 4-7: address
// bytes 8-9: port
// byte 10: ttl
// byte 11: command
// Optionally, there may also be a 4-byte 'auxilliary address'
// (e.g., for 'source-specific multicast' preceding this)
// bytes -4 through -1: auxilliary address
public:
Cookie& srcCookie()
{ return *(Cookie*)byteOffset(0); }
Cookie& dstCookie()
{ return *(Cookie*)byteOffset(2); }
u_int32_t& address()
{ return *(u_int32_t*)byteOffset(4); }
Port& port()
{ return *(Port*)byteOffset(8); }
u_int8_t& ttl()
{ return *(u_int8_t*)byteOffset(10); }
u_int8_t& command()
{ return *(u_int8_t*)byteOffset(11); }
u_int32_t& auxAddress()
{ return *(u_int32_t*)byteOffset(-4); }
private:
inline char* byteOffset(int charIndex)
{ return ((char*)this) + charIndex; }
};
const unsigned TunnelEncapsulationTrailerSize = 12; // bytes
const unsigned TunnelEncapsulationTrailerAuxSize = 4; // bytes
const unsigned TunnelEncapsulationTrailerMaxSize
= TunnelEncapsulationTrailerSize + TunnelEncapsulationTrailerAuxSize;
// Command codes:
// 0: unused
const u_int8_t TunnelDataCmd = 1;
const u_int8_t TunnelJoinGroupCmd = 2;
const u_int8_t TunnelLeaveGroupCmd = 3;
const u_int8_t TunnelTearDownCmd = 4;
const u_int8_t TunnelProbeCmd = 5;
const u_int8_t TunnelProbeAckCmd = 6;
const u_int8_t TunnelProbeNackCmd = 7;
const u_int8_t TunnelJoinRTPGroupCmd = 8;
const u_int8_t TunnelLeaveRTPGroupCmd = 9;
// 0x0A through 0x10: currently unused.
const u_int8_t TunnelExtensionFlag = 0x80; // a flag, not a cmd code
const u_int8_t TunnelDataAuxCmd
= (TunnelExtensionFlag|TunnelDataCmd);
const u_int8_t TunnelJoinGroupAuxCmd
= (TunnelExtensionFlag|TunnelJoinGroupCmd);
const u_int8_t TunnelLeaveGroupAuxCmd
= (TunnelExtensionFlag|TunnelLeaveGroupCmd);
// Note: the TearDown, Probe, ProbeAck, ProbeNack cmds have no Aux version
// 0x84 through 0x87: currently unused.
const u_int8_t TunnelJoinRTPGroupAuxCmd
= (TunnelExtensionFlag|TunnelJoinRTPGroupCmd);
const u_int8_t TunnelLeaveRTPGroupAuxCmd
= (TunnelExtensionFlag|TunnelLeaveRTPGroupCmd);
// 0x8A through 0xFF: currently unused
inline Boolean TunnelIsAuxCmd(u_int8_t cmd) {
return (cmd&TunnelExtensionFlag) != 0;
}
#endif
live/liveMedia/AC3AudioFileServerMediaSubsession.cpp 000444 001752 001752 00000004564 13242237366 022426 0 ustar 00rsf rsf 000000 000000 /**********
This 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 3 of the License, or (at your
option) any later version. (See