live/ 000755 001752 001752 00000000000 15270745342 013146 5 ustar 00ec2-user ec2-user 000000 000000 live/liveMedia/ 000755 001752 001752 00000000000 15270745314 015044 5 ustar 00ec2-user ec2-user 000000 000000 live/groupsock/ 000755 001752 001752 00000000000 15270745314 015161 5 ustar 00ec2-user ec2-user 000000 000000 live/UsageEnvironment/ 000755 001752 001752 00000000000 15270745314 016436 5 ustar 00ec2-user ec2-user 000000 000000 live/BasicUsageEnvironment/ 000755 001752 001752 00000000000 15270745314 017400 5 ustar 00ec2-user ec2-user 000000 000000 live/testProgs/ 000755 001752 001752 00000000000 15270745314 015137 5 ustar 00ec2-user ec2-user 000000 000000 live/mediaServer/ 000755 001752 001752 00000000000 15270745314 015413 5 ustar 00ec2-user ec2-user 000000 000000 live/proxyServer/ 000755 001752 001752 00000000000 15270745314 015515 5 ustar 00ec2-user ec2-user 000000 000000 live/hlsProxy/ 000755 001752 001752 00000000000 15270745314 014775 5 ustar 00ec2-user ec2-user 000000 000000 live/WindowsAudioInputDevice/ 000755 001752 001752 00000000000 15270745314 017721 5 ustar 00ec2-user ec2-user 000000 000000 live/configure 000551 001752 001752 00000000571 15270745314 015051 0 ustar 00ec2-user ec2-user 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.armeb-uclibc 000444 001752 001752 00000001353 15270745342 016662 0 ustar 00ec2-user ec2-user 000000 000000 CROSS_COMPILE= armeb-linux-uclibc-
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.armlinux 000444 001752 001752 00000001133 15270745342 016170 0 ustar 00ec2-user ec2-user 000000 000000 CROSS_COMPILE?= arm-elf-
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.avr32-linux 000444 001752 001752 00000001342 15270745342 016425 0 ustar 00ec2-user ec2-user 000000 000000 CROSS_COMPILE= avr32-linux-uclibc-
COMPILE_OPTS = -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.bfin-linux-uclibc 000444 001752 001752 00000001307 15270745342 017646 0 ustar 00ec2-user ec2-user 000000 000000 CROSS_COMPILER = bfin-linux-uclibc-
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -Wall
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILER)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.bfin-uclinux 000444 001752 001752 00000001305 15270745342 016735 0 ustar 00ec2-user ec2-user 000000 000000 CROSS_COMPILER= bfin-uclinux-
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -Wall
CPP = cpp
CPLUSPLUS_COMPILER = $(CROSS_COMPILER)g++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.bsplinux 000444 001752 001752 00000001370 15270745342 016200 0 ustar 00ec2-user ec2-user 000000 000000 CROSS_COMPILE=
COMPILE_OPTS = $(INCLUDES) -I. -I/usr/local/include -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) -std=c++20 -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 -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.cris-axis-linux-gnu 000444 001752 001752 00000001662 15270745342 020166 0 ustar 00ec2-user ec2-user 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 -I/usr/local/include -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.cygwin 000444 001752 001752 00000001012 15270745342 015625 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.freebsd 000444 001752 001752 00000000747 15270745342 015755 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.cygwin-for-vlc 000444 001752 001752 00000001064 15270745342 017202 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.freebsd-no-openssl 000444 001752 001752 00000000722 15270745342 020041 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O -DBSD=1 -DNEWLOCALE_NOT_USED=1 -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -DNO_OPENSSL=1
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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 00000002713 15270745342 017635 0 ustar 00ec2-user ec2-user 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/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.iphoneos 000444 001752 001752 00000002447 15270745342 016166 0 ustar 00ec2-user ec2-user 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/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux 000444 001752 001752 00000001041 15270745342 015466 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-64bit 000444 001752 001752 00000000764 15270745342 016427 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -m64 -fPIC -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-gdb 000444 001752 001752 00000000752 15270745342 016230 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-gdb-no-std-lib 000444 001752 001752 00000001062 15270745342 020171 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -I. -O -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DNO_STD_LIB=1 -g
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-gdb-sanitize 000444 001752 001752 00000001026 15270745342 020047 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -I. -O -DSOCKLEN_T=socklen_t -g -fsanitize=address,undefined -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) -std=c++20 -Wall -DBSD=1
OBJ = o
LINK = c++ -o
LINK_OPTS = -L. -lasan -lubsan
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = ar cr
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-no-openssl 000444 001752 001752 00000001014 15270745342 017561 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DNO_OPENSSL=1
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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-no-std-lib 000444 001752 001752 00000001060 15270745342 017435 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DNO_STD_LIB=1
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.linux-with-shared-libraries 000444 001752 001752 00000004610 15270745342 021662 0 ustar 00ec2-user ec2-user 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=122
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=3
libBasicUsageEnvironment_VERSION_REVISION=0
libBasicUsageEnvironment_VERSION_AGE=1
libBasicUsageEnvironment_LIB_SUFFIX=so.$(shell expr $(libBasicUsageEnvironment_VERSION_CURRENT) - $(libBasicUsageEnvironment_VERSION_AGE)).$(libBasicUsageEnvironment_VERSION_AGE).$(libBasicUsageEnvironment_VERSION_REVISION)
libUsageEnvironment_VERSION_CURRENT=5
libUsageEnvironment_VERSION_REVISION=0
libUsageEnvironment_VERSION_AGE=2
libUsageEnvironment_LIB_SUFFIX=so.$(shell expr $(libUsageEnvironment_VERSION_CURRENT) - $(libUsageEnvironment_VERSION_AGE)).$(libUsageEnvironment_VERSION_AGE).$(libUsageEnvironment_VERSION_REVISION)
libgroupsock_VERSION_CURRENT=33
libgroupsock_VERSION_REVISION=3
libgroupsock_VERSION_AGE=0
libgroupsock_LIB_SUFFIX=so.$(shell expr $(libgroupsock_VERSION_CURRENT) - $(libgroupsock_VERSION_AGE)).$(libgroupsock_VERSION_AGE).$(libgroupsock_VERSION_REVISION)
#####
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
LIBS_FOR_LIVEMEDIA_LIB = -lssl -lcrypto
EXE =
INSTALL2 = install_shared_libraries
live/config.macosx-bigsur 000444 001752 001752 00000001042 15270745342 017113 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -I/usr/local/include $(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) -std=c++20 -Wall
OBJ = o
LINK = c++ -o
LINK_OPTS = -L. -L/usr/local/ssl/lib
CONSOLE_LINK_OPTS = $(LINK_OPTS)
LIBRARY_LINK = libtool -s -o
LIBRARY_LINK_OPTS =
LIB_SUFFIX = a
LIBS_FOR_CONSOLE_APPLICATION = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.macosx-catalina 000444 001752 001752 00000001063 15270745342 017377 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -I/usr/local/include $(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) -std=c++20 -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 = /usr/lib/libssl.46.dylib /usr/lib/libcrypto.44.dylib
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.macosx-no-openssl 000444 001752 001752 00000001016 15270745342 017716 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -I/usr/local/include $(EXTRA_LDFLAGS) -DBSD=1 -O -DSOCKLEN_T=socklen_t -DHAVE_SOCKADDR_LEN=1 -DTIME_BASE=int -DNEED_XLOCALE_H=1 -DNO_OPENSSL=1
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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.mingw 000444 001752 001752 00000001277 15270745342 015463 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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__ -std=c++20 -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 -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION = -lws2_32
EXE = .exe
live/config.openbsd 000444 001752 001752 00000000740 15270745342 015766 0 ustar 00ec2-user ec2-user 000000 000000 .SUFFIXES: .cpp
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 = -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION =
EXE =
live/config.qnx4 000444 001752 001752 00000001134 15270745342 015224 0 ustar 00ec2-user ec2-user 000000 000000 #
# Requires:
# QNX 4.25
# Watcom 10.6
# TCP/IP 5.0
#
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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 -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/config.raspberrypi 000444 001752 001752 00000001033 15270745342 016672 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DNO_OPENSSL=1 -DNO_STD_LIB=1
C = c
C_COMPILER = cc
C_FLAGS = $(COMPILE_OPTS) $(CPPFLAGS) $(CFLAGS)
CPP = cpp
CPLUSPLUS_COMPILER = c++
CPLUSPLUS_FLAGS = $(COMPILE_OPTS) -std=c++20 -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.solaris-32bit 000444 001752 001752 00000001022 15270745342 016723 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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 -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/config.solaris-64bit 000444 001752 001752 00000001266 15270745342 016742 0 ustar 00ec2-user ec2-user 000000 000000 COMPILE_OPTS = $(INCLUDES) -m64 -I/usr/local/include -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) -std=c++20 -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 -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/config.uClinux 000444 001752 001752 00000001436 15270745342 015766 0 ustar 00ec2-user ec2-user 000000 000000 CROSS_COMPILE= arc-linux-uclibc-
COMPILE_OPTS = $(INCLUDES) -I/usr/local/include -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) -std=c++20 -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) -lssl -lcrypto
LIBS_FOR_GUI_APPLICATION = $(LIBS_FOR_CONSOLE_APPLICATION)
EXE =
live/genMakefiles 000551 001752 001752 00000001000 15270745342 015447 0 ustar 00ec2-user ec2-user 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 hlsProxy"
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 15270745342 014203 0 ustar 00ec2-user ec2-user 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 15270745342 015206 0 ustar 00ec2-user ec2-user 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 00000000722 15270745342 014025 0 ustar 00ec2-user ec2-user 000000 000000 For documentation and instructions for building this software,
see
NOTE: You should download the "LIVE555 Streaming Media" software only from the official site:
http://www.live555.com/liveMedia/
Old versions of the code, and versions of the code from other sites
(including 'GitHub' repositories) are NOT SUPPORTED, and should not be used.
For more information, see:
http://live555.com/liveMedia/faq.html#latest-version
live/Makefile.head 000444 001752 001752 00000000061 15270745342 015501 0 ustar 00ec2-user ec2-user 000000 000000 ##### Change the following for your environment:
live/Makefile.tail 000444 001752 001752 00000003271 15270745342 015537 0 ustar 00ec2-user ec2-user 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
HLS_PROXY_DIR = hlsProxy
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)
cd $(HLS_PROXY_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
cd $(HLS_PROXY_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
cd $(HLS_PROXY_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 \
$(HLS_PROXY_DIR)/Makefile \
Makefile
live/fix-makefile 000555 001752 001752 00000001040 15270745342 015426 0 ustar 00ec2-user ec2-user 000000 000000 #!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$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 00000003231 15270745342 015216 0 ustar 00ec2-user ec2-user 000000 000000 # Comment out the following line to produce Makefiles that generate debuggable code:
NODEBUG=1
# If for some reason you wish to use the deprecated "gethostbyname()" function instead of
# "getaddrinfo()", uncomment the following line:
#USE_DEPRECATED_GETHOSTBYNAME = -DUSE_GETHOSTBYNAME=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" -DNO_OPENSSL=1 $(USE_DEPRECATED_GETHOSTBYNAME)
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 00000002635 15270745342 016605 0 ustar 00ec2-user ec2-user 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 -DNO_OPENSSL=1
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 00000002061 15270745342 017036 0 ustar 00ec2-user ec2-user 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
cd ../hlsProxy
/bin/rm -f hlsProxy.mak
/bin/rm -f Makefile
cat Makefile.head ../win32config Makefile.tail > hlsProxy.mak
live/genWindowsMakefiles.cmd 000444 001752 001752 00000001673 15270745342 017605 0 ustar 00ec2-user ec2-user 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
cd ../hlsProxy
del /Q hlsProxy.mak
type Makefile.head ..\win32config Makefile.tail > hlsProxy.mak
ENDLOCAL
live/WindowsAudioInputDevice/showAudioInputPorts.cpp 000444 001752 001752 00000002505 15270745314 024437 0 ustar 00ec2-user ec2-user 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-2026, 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 15270745314 025155 0 ustar 00ec2-user ec2-user 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 00000025256 15270745314 026561 0 ustar 00ec2-user ec2-user 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());
}
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() {
nextTask() = NULL;
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 15270745314 026363 0 ustar 00ec2-user ec2-user 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-2026 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 15270745314 026413 0 ustar 00ec2-user ec2-user 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 15270745314 026227 0 ustar 00ec2-user ec2-user 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-2026 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 15270745314 026701 0 ustar 00ec2-user ec2-user 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 15270745314 026517 0 ustar 00ec2-user ec2-user 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-2026 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/hlsProxy/COPYING 000755 001752 001752 00000000000 15270745314 017267 2../COPYING ustar 00ec2-user ec2-user 000000 000000 live/hlsProxy/COPYING.LESSER 000755 001752 001752 00000000000 15270745314 021257 2../COPYING.LESSER ustar 00ec2-user ec2-user 000000 000000 live/hlsProxy/Makefile.head 000440 001752 001752 00000000753 15270745314 017334 0 ustar 00ec2-user ec2-user 000000 000000 PREFIX = /usr/local
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/hlsProxy/Makefile.tail 000444 001752 001752 00000002415 15270745314 017365 0 ustar 00ec2-user ec2-user 000000 000000 ##### End of variables to change
HLS_PROXY = live555HLSProxy$(EXE)
ALL = $(HLS_PROXY)
all: $(ALL)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
HLS_PROXY_OBJS = live555HLSProxy.$(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)
live555HLSProxy$(EXE): $(HLS_PROXY_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(HLS_PROXY_OBJS) $(LIBS)
clean:
-rm -rf *.$(OBJ) $(ALL) core *.core *~ include/*~
install: $(HLS_PROXY)
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $(HLS_PROXY) $(DESTDIR)$(PREFIX)/bin
##### Any additional, platform-specific rules come here:
live/hlsProxy/live555HLSProxy.cpp 000444 001752 001752 00000046137 15270745314 020321 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A program that acts as a proxy for a RTSP stream, converting it into a sequence of
// HLS (HTTP Live Streaming) segments, plus a ".m3u8" file that can be accessed via a web browser.
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#define RTSP_CLIENT_VERBOSITY_LEVEL 0 // set to 1 for more verbose output from the "RTSPClient"
#define OUR_HLS_SEGMENTATION_DURATION 6 /*seconds*/
#define OUR_HLS_REWIND_DURATION 60 /*seconds: How far back in time a browser can seek*/
UsageEnvironment* env;
char const* programName;
char* username = NULL;
char* password = NULL;
Authenticator* ourAuthenticator = NULL;
Boolean streamUsingTCP = False;
portNumBits tunnelOverHTTPPortNum = 0;
char const* hlsPrefix;
MediaSession* session;
MPEG2TransportStreamFromESSource* transportStream;
MediaSubsessionIterator* iter;
MediaSubsession* subsession;
unsigned numUsableSubsessions = 0;
double duration = 0.0;
Boolean createHandlerServerForREGISTERCommand = False;
portNumBits handlerServerForREGISTERCommandPortNum = 0;
HandlerServerForREGISTERCommand* handlerServerForREGISTERCommand;
char* usernameForREGISTER = NULL;
char* passwordForREGISTER = NULL;
UserAuthenticationDatabase* authDBForREGISTER = NULL;
void usage() {
*env << "usage:\t" << programName << " [-u ] [-t|-T ] \n";
*env << " or:\t" << programName << " -R [] [-U ] \n";
exit(1);
}
// Forward function definitions:
void continueAfterClientCreation0(RTSPClient* rtspClient, Boolean requestStreamingOverTCP);
void continueAfterClientCreation1(RTSPClient* rtspClient);
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString);
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler);
// Output information about the program (and LIVE555 version):
*env << "LIVE555 HLS Proxy, documented at http://www.live555.com/hlsProxy/\n";
*env << "\t(LIVE555 Streaming Media version " << LIVEMEDIA_LIBRARY_VERSION_STRING << ")\n";
// Parse the command line:
programName = argv[0];
while (argc > 1) {
char* const opt = argv[1];
if (opt[0] != '-') {
if (argc <= 3) break; // only the URL + prefix is left
usage();
}
switch (opt[1]) {
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;
ourAuthenticator = new Authenticator(username, password);
break;
}
case 't': { // stream RTP and RTCP over the TCP 'control' connection
streamUsingTCP = True;
break;
}
case 'T': {
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 '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 'U': { // 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;
}
default: {
*env << "Invalid option: " << opt << "\n";
usage();
break;
}
}
++argv; --argc;
}
// Create (or arrange to create) our RTSP client object:
if (createHandlerServerForREGISTERCommand) {
if (argc != 2) usage();
hlsPrefix = argv[1];
handlerServerForREGISTERCommand
= HandlerServerForREGISTERCommand::createNew(*env, continueAfterClientCreation0,
handlerServerForREGISTERCommandPortNum, authDBForREGISTER,
RTSP_CLIENT_VERBOSITY_LEVEL, programName);
if (handlerServerForREGISTERCommand == NULL) {
*env << "Failed to create a server for handling incoming \"REGISTER\" commands: " << env->getResultMsg() << "\n";
exit(1);
} else {
*env << "Awaiting an incoming \"REGISTER\" command on port " << handlerServerForREGISTERCommand->serverPortNum() << "\n";
}
} else { // Normal case
if (argc != 3) usage();
if (usernameForREGISTER != NULL) {
*env << "The '-U ' option can be used only with -R\n";
usage();
}
char const* rtspURL = argv[1];
hlsPrefix = argv[2];
RTSPClient* rtspClient
= RTSPClient::createNew(*env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, programName, tunnelOverHTTPPortNum);
if (rtspClient == NULL) {
*env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env->getResultMsg() << "\n";
exit(1);
}
continueAfterClientCreation1(rtspClient);
}
// All further processing will be done from 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;
// 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(newRTSPClient);
}
void continueAfterClientCreation1(RTSPClient* rtspClient) {
// Having created a "RTSPClient" object, send a RTSP "DESCRIBE" command for the URL:
rtspClient->sendDescribeCommand(continueAfterDESCRIBE, ourAuthenticator);
}
// A function that outputs a string that identifies each stream (for debugging output).
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).
UsageEnvironment& operator<<(UsageEnvironment& env, const MediaSubsession& subsession) {
return env << subsession.mediumName() << "/" << subsession.codecName();
}
void setupNextSubsession(RTSPClient* rtspClient); // forward
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
if (resultCode != 0) {
*env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n";
delete[] resultString;
break;
}
// Create a Transport Stream to multiplex the Elementary Stream data from each subsession:
transportStream = MPEG2TransportStreamFromESSource::createNew(*env);
// Create a media session object from the SDP description.
// Then iterate over it, to look for subsession(s) that we can handle:
session = MediaSession::createNew(*env, resultString);
delete[] resultString;
if (session == NULL) {
*env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";
break;
} else if (!session->hasSubsessions()) {
*env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
break;
}
iter = new MediaSubsessionIterator(*session);
setupNextSubsession(rtspClient);
return;
} while (0);
// An error occurred:
exit(1);
}
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString); // forward
void startPlayingSession(RTSPClient* rtspClient); // forward
void setupNextSubsession(RTSPClient* rtspClient) {
subsession = iter->next();
if (subsession != NULL) {
// Check whether this subsession is a codec that we support.
// We support H.264 or H.265 video, and AAC audio.
if ((strcmp(subsession->mediumName(), "video") == 0 &&
(strcmp(subsession->codecName(), "H264") == 0 ||
strcmp(subsession->codecName(), "H265") == 0)) ||
(strcmp(subsession->mediumName(), "audio") == 0 &&
strcmp(subsession->codecName(), "MPEG4-GENERIC"/*aka. AAC*/) == 0)) {
// Use this subsession.
++numUsableSubsessions;
if (!subsession->initiate()) {
*env << *rtspClient << "Failed to initiate the \"" << *subsession << "\" subsession: " << env->getResultMsg() << "\n";
} else {
*env << *rtspClient << "Initiated the \"" << *subsession << "\" subsession\n";
// Continue setting up this subsession, by sending a RTSP "SETUP" command:
rtspClient->sendSetupCommand(*subsession, continueAfterSETUP, False, streamUsingTCP,
False, ourAuthenticator);
return;
}
}
setupNextSubsession(rtspClient); // give up on this subsession; go to the next one
return;
}
// We've gone through all of the subsessions.
if (numUsableSubsessions == 0) {
*env << *rtspClient << "This stream has no usable subsessions\n";
exit(0);
}
startPlayingSession(rtspClient);
}
void continueAfterSETUP(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
if (resultCode != 0) {
*env << *rtspClient << "Failed to set up the \"" << *subsession << "\" subsession: " << resultString << "\n";
break;
}
delete[] resultString;
*env << *rtspClient << "Set up the \"" << *subsession << "\" subsession\n";
// Feed this subsession's input source into the Transport Stream:
if (strcmp(subsession->mediumName(), "video") == 0) {
// Create a 'framer' filter for the input source, to put the stream of NAL units into a
// form that's usable to output to the Transport Stream.
// (Note that we use a *DiscreteFramer*, because the input source is a stream of discrete
// NAL units - i.e., one at a time.)
H264or5VideoStreamDiscreteFramer* framer;
int mpegVersion;
if (strcmp(subsession->codecName(), "H264") == 0) {
mpegVersion = 5; // for H.264
framer = H264VideoStreamDiscreteFramer::createNew(*env, subsession->readSource(),
True/*includeStartCodeInOutput*/,
True/*insertAccessUnitDelimiters*/);
// Add any known SPS and PPS NAL units to the framer, so they'll get output ASAP:
u_int8_t* sps = NULL; unsigned spsSize = 0;
u_int8_t* pps = NULL; unsigned ppsSize = 0;
unsigned numSPropRecords;
SPropRecord* sPropRecords
= parseSPropParameterSets(subsession->fmtp_spropparametersets(), numSPropRecords);
if (numSPropRecords > 0) {
sps = sPropRecords[0].sPropBytes;
spsSize = sPropRecords[0].sPropLength;
}
if (numSPropRecords > 1) {
pps = sPropRecords[1].sPropBytes;
ppsSize = sPropRecords[1].sPropLength;
}
framer->setVPSandSPSandPPS(NULL, 0, sps, spsSize, pps, ppsSize);
delete[] sPropRecords;
} else { // H.265
mpegVersion = 6; // for H.265
framer = H265VideoStreamDiscreteFramer::createNew(*env, subsession->readSource(),
True/*includeStartCodeInOutput*/,
True/*insertAccessUnitDelimiters*/);
// Add any known VPS, SPS and PPS NAL units to the framer, so they'll get output ASAP:
u_int8_t* vps = NULL; unsigned vpsSize = 0;
u_int8_t* sps = NULL; unsigned spsSize = 0;
u_int8_t* pps = NULL; unsigned ppsSize = 0;
unsigned numSPropRecords;
SPropRecord *sPropRecordsVPS, *sPropRecordsSPS, *sPropRecordsPPS;
sPropRecordsVPS = parseSPropParameterSets(subsession->fmtp_spropvps(), numSPropRecords);
if (numSPropRecords > 0) {
vps = sPropRecordsVPS[0].sPropBytes;
vpsSize = sPropRecordsVPS[0].sPropLength;
}
sPropRecordsSPS = parseSPropParameterSets(subsession->fmtp_spropsps(), numSPropRecords);
if (numSPropRecords > 0) {
sps = sPropRecordsSPS[0].sPropBytes;
spsSize = sPropRecordsSPS[0].sPropLength;
}
sPropRecordsPPS = parseSPropParameterSets(subsession->fmtp_sproppps(), numSPropRecords);
if (numSPropRecords > 0) {
pps = sPropRecordsPPS[0].sPropBytes;
ppsSize = sPropRecordsPPS[0].sPropLength;
}
framer->setVPSandSPSandPPS(vps, vpsSize, sps, spsSize, pps, ppsSize);
delete[] sPropRecordsVPS; delete[] sPropRecordsSPS; delete[] sPropRecordsPPS;
}
transportStream->addNewVideoSource(framer, mpegVersion);
} else { // audio (AAC)
// Create a 'framer' filter for the input source, to add a ADTS header to each AAC frame,
// to make the audio playable.
ADTSAudioStreamDiscreteFramer* framer
= ADTSAudioStreamDiscreteFramer::createNew(*env, subsession->readSource(),
subsession->fmtp_config());
transportStream->addNewAudioSource(framer, 4/*mpegVersion: AAC*/);
}
// Also set up BYE handler//#####@@@@@
} while (0);
// Set up the next subsession, if any:
setupNextSubsession(rtspClient);
}
void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString); // forward
void segmentationCallback(void* clientData, char const* segmentFileName, double segmentDuration); // forward
void afterPlaying(void* clientData); // forward
void startPlayingSession(RTSPClient* rtspClient) {
// Having successfully setup the session, 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.)
MediaSink* sink
= HLSSegmenter::createNew(*env, OUR_HLS_SEGMENTATION_DURATION, hlsPrefix, segmentationCallback);
// Start playing the sink object:
*env << "Beginning to read...\n";
sink->startPlaying(*transportStream, afterPlaying, NULL);
// Now, send a RTSP "PLAY" command to start the streaming:
if (session->absStartTime() != NULL) {
// Special case: The stream is indexed by 'absolute' time, so send an appropriate "PLAY" command:
rtspClient->sendPlayCommand(*session, continueAfterPLAY, session->absStartTime(), session->absEndTime(), 1.0f, ourAuthenticator);
} else {
duration = session->playEndTime() - session->playStartTime();
rtspClient->sendPlayCommand(*session, continueAfterPLAY, 0.0f, -1.0f, 1.0f, ourAuthenticator);
}
}
// A record that defines a segment that has been written. These records are kept in a list:
class SegmentRecord {
public:
SegmentRecord(char const* segmentFileName, double segmentDuration)
: fNext(NULL), fSegmentFileName(strDup(segmentFileName)), fSegmentDuration(segmentDuration) {
}
virtual ~SegmentRecord() {
delete[] fSegmentFileName;
delete fNext;
}
SegmentRecord*& next() { return fNext; }
char const* fileName() const { return fSegmentFileName; }
double duration() const { return fSegmentDuration; }
private:
SegmentRecord* fNext;
char* fSegmentFileName;
double fSegmentDuration;
};
SegmentRecord* head = NULL;
SegmentRecord* tail = NULL;
double totalDuration = 0.0;
char* ourM3U8FileName = NULL;
void segmentationCallback(void* /*clientData*/,
char const* segmentFileName, double segmentDuration) {
// Begin by updating our list of segments:
SegmentRecord* newSegment = new SegmentRecord(segmentFileName, segmentDuration);
if (tail != NULL) {
tail->next() = newSegment;
} else {
head = newSegment;
}
tail = newSegment;
totalDuration += segmentDuration;
fprintf(stderr, "Wrote segment \"%s\" (duration: %f seconds) -> %f seconds of data stored\n",
segmentFileName, segmentDuration, totalDuration);
static unsigned firstSegmentCounter = 1;
while (totalDuration > OUR_HLS_REWIND_DURATION) {
// Remove segments from the head of the list:
SegmentRecord* segmentToRemove = head;
if (segmentToRemove == NULL) exit(1); // should not happen
head = segmentToRemove->next();
if (tail == segmentToRemove) { // should not happen
tail = NULL;
}
segmentToRemove->next() = NULL;
totalDuration -= segmentToRemove->duration();
fprintf(stderr, "\tDeleting segment \"%s\" (duration: %f seconds) -> %f seconds of data stored\n",
segmentToRemove->fileName(), segmentToRemove->duration(), totalDuration);
if (unlink(segmentToRemove->fileName()) != 0) {
*env << "\t\tunlink(\"" << segmentToRemove->fileName() << "\") failed: " << env->getResultMsg() << "\n";
}
delete segmentToRemove;
++firstSegmentCounter;
}
// Then, rewrite our ".m3u8" file with the new list of segments:
if (ourM3U8FileName == NULL) {
size_t const fileNameSize = strlen(hlsPrefix) + 5/*strlen(".m3u8")*/ + 1;
ourM3U8FileName = new char[fileNameSize];
if (ourM3U8FileName == NULL) exit(1);
snprintf(ourM3U8FileName, fileNameSize, "%s.m3u8", hlsPrefix);
}
// Open our ".m3u8" file for output, and write the prefix:
FILE* ourM3U8Fid = fopen(ourM3U8FileName, "wb");
if (ourM3U8Fid == NULL) {
*env << "Failed to open file \"" << ourM3U8FileName << "\": " << env->getResultMsg();
exit(1);
}
fprintf(ourM3U8Fid,
"#EXTM3U\n"
"#EXT-X-VERSION:3\n"
"#EXT-X-INDEPENDENT-SEGMENTS\n"
"#EXT-X-TARGETDURATION:%u\n"
"#EXT-X-MEDIA-SEQUENCE:%u\n",
OUR_HLS_SEGMENTATION_DURATION,
firstSegmentCounter);
// Write the list of segments:
for (SegmentRecord* segment = head; segment != NULL; segment = segment->next()) {
fprintf(ourM3U8Fid,
"#EXTINF:%f,\n"
"%s\n",
segment->duration(),
segment->fileName());
}
// Close our ".m3u8" file:
fclose(ourM3U8Fid);
static Boolean isFirstTime = True;
if (isFirstTime) {
fprintf(stderr, "Wrote index file \"%s\"; the stream can now be played from a URL pointing to this file.\007\n", ourM3U8FileName);
isFirstTime = False;
}
}
void afterPlaying(void* /*clientData*/) {
*env << "...Done reading\n";
exit(0);
}
void continueAfterPLAY(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
if (resultCode != 0) {
*env << *rtspClient << "Failed to start playing session: " << resultString << "\n";
break;
}
delete[] resultString;
// Set timer based on duration #####@@@@@
*env << *rtspClient << "Started playing session";
if (duration > 0) {
*env << " (for up to " << duration << " seconds)";
}
*env << "...\n";
return;
} while (0);
// An error occurred:
exit(1);
}
live/proxyServer/COPYING 000755 001752 001752 00000000000 15270745314 020007 2../COPYING ustar 00ec2-user ec2-user 000000 000000 live/proxyServer/live555ProxyServer.cpp 000444 001752 001752 00000021552 15270745314 021653 0 ustar 00ec2-user ec2-user 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-2026, 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 URL at the end
// Make sure that the remaining arguments appear to be "rtsp://" (or "rtsps://") URLs:
int i;
for (i = 1; i < argc; ++i) {
if (strncmp(argv[i], "rtsp://", 7) != 0 && strncmp(argv[i], "rtsps://", 8) != 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) {
snprintf(streamName, sizeof streamName, "%s", "proxyStream"); // there's just one stream; give it this name
} else {
snprintf(streamName, sizeof 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 00000000753 15270745314 020054 0 ustar 00ec2-user ec2-user 000000 000000 PREFIX = /usr/local
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 00000002453 15270745314 020107 0 ustar 00ec2-user ec2-user 000000 000000 ##### End of variables to change
PROXY_SERVER = live555ProxyServer$(EXE)
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 15270745314 021777 2../COPYING.LESSER ustar 00ec2-user ec2-user 000000 000000 live/mediaServer/COPYING 000755 001752 001752 00000000000 15270745314 017705 2../COPYING ustar 00ec2-user ec2-user 000000 000000 live/mediaServer/DynamicRTSPServer.cpp 000444 001752 001752 00000025255 15270745314 021412 0 ustar 00ec2-user ec2-user 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-2026, 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 ourSocketIPv4 = setUpOurSocket(env, ourPort, AF_INET);
int ourSocketIPv6 = setUpOurSocket(env, ourPort, AF_INET6);
if (ourSocketIPv4 < 0 && ourSocketIPv6 < 0) return NULL;
return new DynamicRTSPServer(env, ourSocketIPv4, ourSocketIPv6, ourPort,
authDatabase, reclamationTestSeconds);
}
DynamicRTSPServer::DynamicRTSPServer(UsageEnvironment& env, int ourSocketIPv4, int ourSocketIPv6,
Port ourPort,
UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)
: RTSPServer(env, ourSocketIPv4, ourSocketIPv6, ourPort, authDatabase, reclamationTestSeconds) {
}
DynamicRTSPServer::~DynamicRTSPServer() {
}
static ServerMediaSession* createNewSMS(UsageEnvironment& env,
char const* fileName, FILE* fid); // forward
void DynamicRTSPServer
::lookupServerMediaSession(char const* streamName,
lookupServerMediaSessionCompletionFunc* completionFunc,
void* completionClientData,
Boolean isFirstLookupInSession) {
// First, check whether the specified "streamName" exists as a local file:
FILE* fid = fopen(streamName, "rb");
Boolean const fileExists = fid != NULL;
// Next, check whether we already have a "ServerMediaSession" for this file:
ServerMediaSession* sms = getServerMediaSession(streamName);
Boolean const 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;
} 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);
}
if (completionFunc != NULL) {
(*completionFunc)(completionClientData, sms);
}
}
// Special code for handling Matroska files:
struct MatroskaDemuxCreationState {
MatroskaFileServerDemux* demux;
EventLoopWatchVariable 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;
EventLoopWatchVariable 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":
size_t indexFileNameLen = strlen(fileName) + 2; // allow for trailing "x\0"
char* indexFileName = new char[indexFileNameLen];
snprintf(indexFileName, indexFileNameLen, "%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 = 300000; // 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 00000003504 15270745314 021220 0 ustar 00ec2-user ec2-user 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-2026, 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_HH
#include "RTSPServer.hh"
#endif
class DynamicRTSPServer: public RTSPServer {
public:
static DynamicRTSPServer* createNew(UsageEnvironment& env, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationTestSeconds = 65);
protected:
DynamicRTSPServer(UsageEnvironment& env, int ourSocketIPv4, int ourSocketIPv6, Port ourPort,
UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds);
// called only by createNew();
virtual ~DynamicRTSPServer();
protected: // redefined virtual functions
virtual void lookupServerMediaSession(char const* streamName,
lookupServerMediaSessionCompletionFunc* completionFunc,
void* completionClientData,
Boolean isFirstLookupInSession);
};
#endif
live/mediaServer/live555MediaServer.cpp 000444 001752 001752 00000011173 15270745314 021445 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// LIVE555 Media Server
// main program
#include
#include "DynamicRTSPServer.hh"
#include "version.hh"
#include // for "weHaveAnIPv*Address()"
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";
*env << "Play streams from this server using the URL\n";
if (weHaveAnIPv4Address(*env)) {
char* rtspURLPrefix = rtspServer->ipv4rtspURLPrefix();
*env << "\t" << rtspURLPrefix << "\n";
delete[] rtspURLPrefix;
if (weHaveAnIPv6Address(*env)) *env << "or\n";
}
if (weHaveAnIPv6Address(*env)) {
char* rtspURLPrefix = rtspServer->ipv6rtspURLPrefix();
*env << "\t" << rtspURLPrefix << "\n";
delete[] rtspURLPrefix;
}
*env << "where 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).)\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 00000000753 15270745314 017752 0 ustar 00ec2-user ec2-user 000000 000000 PREFIX = /usr/local
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 00000002657 15270745314 020013 0 ustar 00ec2-user ec2-user 000000 000000 ##### End of variables to change
MEDIA_SERVER = live555MediaServer$(EXE)
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 00000002021 15270745314 017412 0 ustar 00ec2-user ec2-user 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-2026, 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 "1.16"
#endif
live/mediaServer/COPYING.LESSER 000755 001752 001752 00000000000 15270745314 021675 2../COPYING.LESSER ustar 00ec2-user ec2-user 000000 000000 live/testProgs/COPYING 000755 001752 001752 00000000000 15270745314 017431 2../COPYING ustar 00ec2-user ec2-user 000000 000000 live/testProgs/Makefile.head 000440 001752 001752 00000000753 15270745314 017476 0 ustar 00ec2-user ec2-user 000000 000000 PREFIX = /usr/local
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 00000024555 15270745314 017540 0 ustar 00ec2-user ec2-user 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)
HLS_APPS = testH264VideoToHLSSegments$(EXE)
MISC_APPS = testMPEG1or2Splitter$(EXE) testMPEG1or2ProgramToTransportStream$(EXE) testH264VideoToTransportStream$(EXE) testH265VideoToTransportStream$(EXE) MPEG2TransportStreamIndexer$(EXE) testMPEG2TransportStreamTrickPlay$(EXE) registerRTSPStream$(EXE) testMKVSplitter$(EXE) testMPEG2TransportStreamSplitter$(EXE) mikeyParse$(EXE)
ALL = $(MULTICAST_APPS) $(UNICAST_APPS) $(HLS_APPS) $(MISC_APPS)
all: $(ALL)
extra: testGSMStreamer$(EXE)
.$(C).$(OBJ):
$(C_COMPILER) -c $(C_FLAGS) $<
.$(CPP).$(OBJ):
$(CPLUSPLUS_COMPILER) -c $(CPLUSPLUS_FLAGS) $<
HELPER_OBJS = announceURL.$(OBJ)
MP3_STREAMER_OBJS = testMP3Streamer.$(OBJ)
MP3_RECEIVER_OBJS = testMP3Receiver.$(OBJ)
RELAY_OBJS = testRelay.$(OBJ)
REPLICATOR_OBJS = testReplicator.$(OBJ)
H264_VIDEO_TO_HLS_SEGMENTS_OBJS = testH264VideoToHLSSegments.$(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)
TEST_MKV_SPLITTER_OBJS = testMKVSplitter.$(OBJ)
TEST_MPEG2_TRANSPORT_STREAM_SPLITTER_OBJS = testMPEG2TransportStreamSplitter.$(OBJ)
MIKEY_PARSE_OBJS = mikeyParse.$(OBJ)
GSM_STREAMER_OBJS = testGSMStreamer.$(OBJ) testGSMEncoder.$(OBJ)
announceURL.$(CPP): announceURL.hh
testMP3Streamer.$(CPP): announceURL.hh
testMPEG1or2VideoStreamer.$(CPP): announceURL.hh
testMPEG1or2AudioVideoStreamer.$(CPP): announceURL.hh
testMPEG2TransportStreamer.$(CPP): announceURL.hh
testMPEG4VideoStreamer.$(CPP): announceURL.hh
testH264VideoStreamer.$(CPP): announceURL.hh
testH265VideoStreamer.$(CPP): announceURL.hh
testDVVideoStreamer.$(CPP): announceURL.hh
testWAVAudioStreamer.$(CPP): announceURL.hh
testAMRAudioStreamer.$(CPP): announceURL.hh
testOnDemandRTSPServer.$(CPP): announceURL.hh
testMKVStreamer.$(CPP): announceURL.hh
testOggStreamer.$(CPP): announceURL.hh
vobStreamer.$(CPP): announceURL.hh
testGSMStreamer.$(CPP): announceURL.hh
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) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MP3_STREAMER_OBJS) $(HELPER_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)
testH264VideoToHLSSegments$(EXE): $(H264_VIDEO_TO_HLS_SEGMENTS_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(H264_VIDEO_TO_HLS_SEGMENTS_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) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG_1OR2_VIDEO_STREAMER_OBJS) $(HELPER_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) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG_1OR2_AUDIO_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testMPEG2TransportStreamer$(EXE): $(MPEG2_TRANSPORT_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG2_TRANSPORT_STREAMER_OBJS) $(HELPER_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) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MPEG4_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testH264VideoStreamer$(EXE): $(H264_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(H264_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testH265VideoStreamer$(EXE): $(H265_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(H265_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testDVVideoStreamer$(EXE): $(DV_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(DV_VIDEO_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testWAVAudioStreamer$(EXE): $(WAV_AUDIO_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(WAV_AUDIO_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testAMRAudioStreamer$(EXE): $(AMR_AUDIO_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(AMR_AUDIO_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testOnDemandRTSPServer$(EXE): $(ON_DEMAND_RTSP_SERVER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(ON_DEMAND_RTSP_SERVER_OBJS) $(HELPER_OBJS) $(LIBS)
testMKVStreamer$(EXE): $(MKV_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MKV_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
testOggStreamer$(EXE): $(OGG_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(OGG_STREAMER_OBJS) $(HELPER_OBJS) $(LIBS)
vobStreamer$(EXE): $(VOB_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(VOB_STREAMER_OBJS) $(HELPER_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)
testMKVSplitter$(EXE): $(TEST_MKV_SPLITTER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(TEST_MKV_SPLITTER_OBJS) $(LIBS)
testMPEG2TransportStreamSplitter$(EXE): $(TEST_MPEG2_TRANSPORT_STREAM_SPLITTER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(TEST_MPEG2_TRANSPORT_STREAM_SPLITTER_OBJS) $(LIBS)
mikeyParse$(EXE): $(MIKEY_PARSE_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(MIKEY_PARSE_OBJS) $(LIBS)
testGSMStreamer$(EXE): $(GSM_STREAMER_OBJS) $(HELPER_OBJS) $(LOCAL_LIBS)
$(LINK)$@ $(CONSOLE_LINK_OPTS) $(GSM_STREAMER_OBJS) $(HELPER_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 00000006530 15270745314 023057 0 ustar 00ec2-user ec2-user 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-2026, 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":
size_t const outputFileNameLen = len+2; // allow for trailing x\0
char* outputFileName = new char[outputFileNameLen];
snprintf(outputFileName, outputFileNameLen, "%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";
delete[] outputFileName;
exit(1);
}
// Start playing, to generate the output index file:
*env << "Writing index file \"" << outputFileName << "\"...";
delete[] 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 15270745314 017316 0 ustar 00ec2-user ec2-user 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-2026, 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.hh 000444 001752 001752 00000004327 15270745314 017602 0 ustar 00ec2-user ec2-user 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-2026, 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 00000015307 15270745314 017170 0 ustar 00ec2-user ec2-user 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-2026, 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 {
struct sockaddr_storage proxyServerAddress;
copyAddress(proxyServerAddress, addresses.firstAddress());
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):
struct sockaddr_storage destAddress;
subsession->getConnectionEndpointAddress(destAddress);
if (!addressIsNull(destAddress)) {
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 15270745314 021351 0 ustar 00ec2-user ec2-user 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-2026, 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 00000005555 15270745314 017425 0 ustar 00ec2-user ec2-user 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-2026, 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";
NetAddressList sessionAddresses(sessionAddressStr);
struct sockaddr_storage sessionAddress;
copyAddress(sessionAddress, sessionAddresses.firstAddress());
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_storage 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 00000010536 15270745314 021652 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.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 sockaddr_storage destinationAddress;
destinationAddress.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
announceURL(rtspServer, sms);
// 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 00000011123 15270745314 021542 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.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 sockaddr_storage destinationAddress;
destinationAddress.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
announceURL(rtspServer, sms);
// 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 00000013232 15270745314 020663 0 ustar 00ec2-user ec2-user 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-2026, 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 "BasicUsageEnvironment.hh"
#include "announceURL.hh"
#include "GroupsockHelper.hh"
////////// Main program //////////
// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
// To stream using IPv6 multicast, rather than IPv4 multicast, uncomment the following:
//#define USE_IPV6_MULTICAST 1
// 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 USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
#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 const* destinationAddressStr
#ifdef USE_IPV6_MULTICAST
#ifdef USE_SSM
= "FF3E::FFFF:2A2A";
#else
= "FF1E::FFFF:2A2A";
#endif
#else
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
#endif
#endif
// 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.)
const unsigned short rtpPortNum = 6666;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 1; // low, in case routers don't admin scope
NetAddressList destinationAddresses(destinationAddressStr);
struct sockaddr_storage destinationAddress;
copyAddress(destinationAddress, destinationAddresses.firstAddress());
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);
announceURL(rtspServer, sms);
#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 00000011750 15270745314 021662 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.hh"
#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 sockaddr_storage destinationAddress;
destinationAddress.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
announceURL(rtspServer, sms);
// 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 15270745314 023553 0 ustar 00ec2-user ec2-user 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-2026, 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 15270745314 021421 2../COPYING.LESSER ustar 00ec2-user ec2-user 000000 000000 live/testProgs/testMKVStreamer.cpp 000444 001752 001752 00000014746 15270745314 020715 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.hh"
#include
UsageEnvironment* env;
char const* inputFileName = "test.mkv";
struct sockaddr_storage 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.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
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);
announceURL(rtspServer, sms);
// 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/announceURL.cpp 000444 001752 001752 00000003123 15270745314 020031 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A common function that outputs the URL(s) that can be used to access a stream
// served by a RTSP server.
// Implementation
#include "announceURL.hh"
#include // for "weHaveAnIPv*Address()"
void announceURL(RTSPServer* rtspServer, ServerMediaSession* sms) {
if (rtspServer == NULL || sms == NULL) return; // sanity check
UsageEnvironment& env = rtspServer->envir();
env << "Play this stream using the URL ";
if (weHaveAnIPv4Address(env)) {
char* url = rtspServer->ipv4rtspURL(sms);
env << "\"" << url << "\"";
delete[] url;
if (weHaveAnIPv6Address(env)) env << " or ";
}
if (weHaveAnIPv6Address(env)) {
char* url = rtspServer->ipv6rtspURL(sms);
env << "\"" << url << "\"";
delete[] url;
}
env << "\n";
}
live/testProgs/testH265VideoToTransportStream.cpp 000444 001752 001752 00000005270 15270745314 023554 0 ustar 00ec2-user ec2-user 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-2026, 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 00000015062 15270745314 020764 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.hh"
#include
UsageEnvironment* env;
char const* inputFileName = "test.ogg";
struct sockaddr_storage 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.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
announceURL(rtspServer, sms);
// 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 15270745314 021064 0 ustar 00ec2-user ec2-user 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 15270745314 017137 0 ustar 00ec2-user ec2-user 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 00000013324 15270745314 020630 0 ustar 00ec2-user ec2-user 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-2026, 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
// To stream using IPv6 multicast, rather than IPv4 multicast, uncomment the following:
//#define USE_IPV6_MULTICAST 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_IPV6_MULTICAST
#ifdef USE_SSM
= "FF3E::FFFF:2A2A";
#else
= "FF1E::FFFF:2A2A";
#endif
#else
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
#endif
#endif
// Note: If the session is unicast rather than multicast,
// then replace this string with "0.0.0.0"
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
NetAddressList sessionAddresses(sessionAddressStr);
struct sockaddr_storage sessionAddress;
copyAddress(sessionAddress, sessionAddresses.firstAddress());
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
#ifdef USE_SSM
char const* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
NetAddressList sourceFilterAddresses(sourceAddressStr);
struct sockaddr_storage sourceFilterAddress;
copyAddress(sourceFilterAddress, sourceFilterAddresses.firstAddress());
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 00000015267 15270745314 020646 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A test program that streams a MP3 file via RTP/RTCP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "announceURL.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
// To stream using IPv6 multicast, rather than IPv4 multicast, uncomment the following:
//#define USE_IPV6_MULTICAST 1
// 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 USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
#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_IPV6_MULTICAST
#ifdef USE_SSM
= "FF3E::FFFF:2A2A";
#else
= "FF1E::FFFF:2A2A";
#endif
#else
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
#endif
#endif
// 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.)
const unsigned short rtpPortNum = 6666;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 1; // low, in case routers don't admin scope
NetAddressList destinationAddresses(destinationAddressStr);
struct sockaddr_storage destinationAddress;
copyAddress(destinationAddress, destinationAddresses.firstAddress());
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, 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 \"testMP3Streamer\"", isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*sessionState.sink, sessionState.rtcpInstance));
rtspServer->addServerMediaSession(sms);
announceURL(rtspServer, sms);
#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 15270745314 021757 0 ustar 00ec2-user ec2-user 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 00000016730 15270745314 023450 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.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
// To stream using IPv6 multicast, rather than IPv4 multicast, uncomment the following:
//#define USE_IPV6_MULTICAST 1
// 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;
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
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_IPV6_MULTICAST
#ifdef USE_SSM
= "FF3E::FFFF:2A2A";
#else
= "FF1E::FFFF:2A2A";
#endif
#else
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
#endif
#endif
// 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.)
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
NetAddressList destinationAddresses(destinationAddressStr);
struct sockaddr_storage destinationAddress;
copyAddress(destinationAddress, destinationAddresses.firstAddress());
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, 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 \"testMPEG1or2AudioVideoStreamer\"",
isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*audioSink, audioRTCP));
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, videoRTCP));
rtspServer->addServerMediaSession(sms);
announceURL(rtspServer, sms);
#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 15270745314 024726 0 ustar 00ec2-user ec2-user 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-2026, 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 15270745314 021524 0 ustar 00ec2-user ec2-user 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-2026, 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 15270745314 020771 0 ustar 00ec2-user ec2-user 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 00000010673 15270745314 022460 0 ustar 00ec2-user ec2-user 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-2026, 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
NetAddressList sessionAddresses(sessionAddressStr);
struct sockaddr_storage sessionAddress;
copyAddress(sessionAddress, sessionAddresses.firstAddress());
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
#ifdef USE_SSM
char const* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
NetAddressList sourceFilterAddresses(sourceAddressStr);
struct sockaddr_storage sourceFilterAddress;
copyAddress(sourceFilterAddress, sourceFilterAddresses.firstAddress());
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 00000013504 15270745314 022472 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.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
// To stream using IPv6 multicast, rather than IPv4 multicast, uncomment the following:
//#define USE_IPV6_MULTICAST 1
// 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;
#ifdef USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
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
NetAddressList destinationAddresses(destinationAddressStr);
struct sockaddr_storage destinationAddress;
copyAddress(destinationAddress, destinationAddresses.firstAddress());
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, 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 \"testMPEG1or2VideoStreamer\"",
isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
announceURL(rtspServer, sms);
#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 15270745314 021276 0 ustar 00ec2-user ec2-user 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 00000011051 15270745314 022753 0 ustar 00ec2-user ec2-user 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-2026, 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
NetAddressList sessionAddresses(sessionAddressStr);
struct sockaddr_storage sessionAddress;
copyAddress(sessionAddress, sessionAddresses.firstAddress());
const Port rtpPort(rtpPortNum);
const Port rtcpPort(rtcpPortNum);
#ifdef USE_SSM
char const* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
NetAddressList sourceFilterAddresses(sourceAddressStr);
struct sockaddr_storage sourceFilterAddress;
copyAddress(sourceFilterAddress, sourceFilterAddresses.firstAddress());
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 00000013027 15270745314 022776 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.hh"
#include "GroupsockHelper.hh"
// To stream using "source-specific multicast" (SSM), uncomment the following:
//#define USE_SSM 1
// To stream using IPv6 multicast, rather than IPv4 multicast, uncomment the following:
//#define USE_IPV6_MULTICAST 1
// 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 USE_SSM
Boolean const isSSM = True;
#else
Boolean const isSSM = False;
#endif
#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_IPV6_MULTICAST
#ifdef USE_SSM
= "FF3E::FFFF:2A2A";
#else
= "FF1E::FFFF:2A2A";
#endif
#else
#ifdef USE_SSM
= "232.255.42.42";
#else
= "239.255.42.42";
#endif
#endif
// 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.)
const unsigned short rtpPortNum = 1234;
const unsigned short rtcpPortNum = rtpPortNum+1;
const unsigned char ttl = 7; // low, in case routers don't admin scope
NetAddressList destinationAddresses(destinationAddressStr);
struct sockaddr_storage destinationAddress;
copyAddress(destinationAddress, destinationAddresses.firstAddress());
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, 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 \"testMPEG2TransportStreamer\"",
isSSM);
sms->addSubsession(PassiveServerMediaSubsession::createNew(*videoSink, rtcp));
rtspServer->addServerMediaSession(sms);
announceURL(rtspServer, sms);
#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 00000012536 15270745314 024266 0 ustar 00ec2-user ec2-user 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-2026, 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":
size_t const indexFileNameLen = len+2; // allow for trailing x\0
char* indexFileName = new char[indexFileNameLen]; // allow for trailing x\0
snprintf(indexFileName, indexFileNameLen, "%sx", inputFileName);
MPEG2TransportStreamIndexFile* indexFile
= MPEG2TransportStreamIndexFile::createNew(*env, indexFileName);
if (indexFile == NULL) {
*env << "Failed to open index file \"" << indexFileName << "\" (does it exist?)\n";
delete[] indexFileName;
exit(1);
}
delete[] indexFileName;
// 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 00000010711 15270745314 022047 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.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 sockaddr_storage destinationAddress;
destinationAddress.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
announceURL(rtspServer, sms);
// 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/announceURL.hh 000444 001752 001752 00000002173 15270745314 017652 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A common function that outputs the URL(s) that can be used to access a stream
// served by a RTSP server.
// C++ header
#ifndef _ANNOUNCE_URL_HH
#define _ANNOUNCE_URL_HH
#ifndef _LIVEMEDIA_HH
#include "liveMedia.hh"
#endif
void announceURL(RTSPServer* rtspServer, ServerMediaSession* sms);
#endif
live/testProgs/testRelay.cpp 000444 001752 001752 00000006727 15270745314 017631 0 ustar 00ec2-user ec2-user 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-2026, 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
NetAddressList inputAddresses(inputAddressStr);
struct sockaddr_storage inputAddress;
copyAddress(inputAddress, inputAddresses.firstAddress());
Port const inputPort(8888);
unsigned char const inputTTL = 0; // we're only reading from this mcast group
#ifdef USE_SSM
char const* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
NetAddressList sourceFilterAddresses(sourceAddressStr);
struct sockaddr_storage sourceFilterAddress;
copyAddress(sourceFilterAddress, sourceFilterAddresses.firstAddress());
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".
NetAddressList outputAddresses(outputAddressStr);
struct sockaddr_storage outputAddress;
copyAddress(outputAddress, outputAddresses.firstAddress());
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 00000012004 15270745314 020642 0 ustar 00ec2-user ec2-user 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-2026, 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
NetAddressList inputAddresses(inputAddressStr);
struct sockaddr_storage inputAddress;
copyAddress(inputAddress, inputAddresses.firstAddress());
Port const inputPort(8888);
unsigned char const inputTTL = 0; // we're only reading from this mcast group
#ifdef USE_SSM
char const* sourceAddressStr = "aaa.bbb.ccc.ddd";
// replace this with the real source address
NetAddressList sourceFilterAddresses(sourceAddressStr);
struct sockaddr_storage sourceFilterAddress;
copyAddress(sourceFilterAddress, sourceFilterAddresses.firstAddress());
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:
NetAddressList outputAddresses(outputAddressStr);
struct sockaddr_storage outputAddress;
copyAddress(outputAddress, outputAddresses.firstAddress());
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 00000054224 15270745314 020477 0 ustar 00ec2-user ec2-user 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-2026, 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, char const* reason);
// 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";
}
EventLoopWatchVariable 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 one 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()->setByeWithReasonHandler(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, char const* reason) {
MediaSubsession* subsession = (MediaSubsession*)clientData;
RTSPClient* rtspClient = (RTSPClient*)subsession->miscPtr;
UsageEnvironment& env = rtspClient->envir(); // alias
env << *rtspClient << "Received RTCP \"BYE\"";
if (reason != NULL) {
env << " (reason:\"" << reason << "\")";
delete[] (char*)reason;
}
env << " 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
snprintf(uSecsStr, sizeof 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 00000024167 15270745314 021665 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A test program that streams a WAV audio file via RTP/RTCP
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "announceURL.hh"
#include "GroupsockHelper.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 sockaddr_storage destinationAddress;
destinationAddress.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
announceURL(sessionState.rtspServer, sms);
// 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 00000024016 15270745314 020125 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.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 sockaddr_storage destinationAddress;
destinationAddress.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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 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";
announceURL(rtspServer, sms);
}
// 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 00000011764 15270745314 021670 0 ustar 00ec2-user ec2-user 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-2026, 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 "announceURL.hh"
#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 sockaddr_storage destinationAddress;
destinationAddress.ss_family = AF_INET;
((struct sockaddr_in&)destinationAddress).sin_addr.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);
announceURL(rtspServer, sms);
// 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/testProgs/testMKVSplitter.cpp 000444 001752 001752 00000011203 15270745314 020722 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A test program that reads a ".mkv" (i.e., Matroska) file, demultiplexes each track
// (video, audio, subtitles), and outputs each track to a file.
// main program
#include
#include
UsageEnvironment* env;
char const* programName;
char const* inputFileName;
// An array of structures representing the state of the video, audio, and subtitle tracks:
static struct {
unsigned trackNumber;
FramedSource* source;
FileSink* sink;
} trackState[3];
void onMatroskaFileCreation(MatroskaFile* newFile, void* clientData); // forward
void usage() {
*env << "usage: " << programName << " \n";
exit(1);
}
int main(int argc, char** 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();
inputFileName = argv[1];
// 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);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void play(); // forward
void onMatroskaFileCreation(MatroskaFile* matroskaFile, void* /*clientData*/) {
// Create a new demultiplexor for the file:
MatroskaDemux* matroskaDemux = matroskaFile->newDemux();
// Create source streams and file sinks for each preferred track;
unsigned numActiveTracks = 0;
for (unsigned i = 0; i < 3; ++i) {
unsigned trackNumber;
trackState[i].source = matroskaDemux->newDemuxedTrack(trackNumber);
trackState[i].trackNumber = trackNumber;
trackState[i].sink = NULL; // by default; may get changed below
if (trackState[i].source == NULL) continue;
char const* mimeType = matroskaFile->trackMIMEType(trackNumber);
if (mimeType == NULL || mimeType[0] == '\0') continue;
// Create the file name from "mimeType" by replacing "/" with "-", and adding the
// track number at the end:
size_t const fileNameLen = strlen(mimeType) + 100/*more than enough space*/;
char* fileName = new char[fileNameLen];
snprintf(fileName, fileNameLen, "%s-%d", mimeType, trackNumber);
for (unsigned j = 0; fileName[j] != '\0'; ++j) {
if (fileName[j] == '/') {
fileName[j] = '-';
break;
}
}
trackState[i].sink
= matroskaFile->createFileSinkForTrackNumber(trackNumber, fileName);
if (trackState[i].sink != NULL) {
++numActiveTracks;
fprintf(stderr, "Created output file \"%s\" for track %d\n", fileName, trackNumber);
}
delete[] fileName;
}
if (numActiveTracks == 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);
}
// Start the streaming:
play();
}
void afterPlaying(void* /*clientData*/) {
*env << "...done reading from file\n";
// Stop playing all sinks, 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;
}
// Finally, close the sinks:
for (i = 0; i < 3; ++i) Medium::close(trackState[i].sink);
exit(0);
}
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/playCommon.cpp 000444 001752 001752 00000154063 15270745314 017770 0 ustar 00ec2-user ec2-user 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-2026, 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, char const* reason);
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;
Boolean applicationOnly = 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;
}
struct sockaddr_storage interfaceAddress;
copyAddress(interfaceAddress, addresses.firstAddress());
if (interfaceAddress.ss_family == AF_INET) {
ReceivingInterfaceAddr = ((sockaddr_in&)interfaceAddress).sin_addr.s_addr;
}
if (interfaceAddress.ss_family == AF_INET6) {
ReceivingInterfaceAddr6 = ((sockaddr_in6&)interfaceAddress).sin6_addr;
}
++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 'L': { // receive/record an 'application' (e.g., metadata) stream only
applicationOnly = True;
singleMedium = "application";
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 (audioOnly && applicationOnly) {
*env << "The -a and -L options cannot both be used!\n";
usage();
}
if (videoOnly && applicationOnly) {
*env << "The -v and -L 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";
shutdown();
} 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':
snprintf(outFileName, sizeof 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):
snprintf(outFileName, sizeof 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;
} else if (strcmp(subsession->codecName(), "MPEG4-GENERIC") == 0) {
// For AAC audio, we use a regular file sink, but add a 'ADTS framer' filter
// to the end of the data source, so that the resulting file is playable:
FramedFilter* adtsFramer
= ADTSAudioStreamDiscreteFramer::createNew(*env, subsession->readSource(),
subsession->fmtp_config());
subsession->addFilter(adtsFramer);
}
}
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()->setByeWithReasonHandler(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, char const* reason) {
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
unsigned secsDiff = timeNow.tv_sec - startTime.tv_sec;
MediaSubsession* subsession = (MediaSubsession*)clientData;
*env << "Received RTCP \"BYE\"";
if (reason != NULL) {
*env << " (reason:\"" << reason << "\")";
delete[] (char*)reason;
}
*env << " 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) {
// Keep this running: 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*/) {
periodicFileOutputTask = NULL;
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*/) {
arrivalCheckTimerTask = NULL;
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];
snprintf(timestampStr, sizeof 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*/) {
interPacketGapCheckTimerTask = NULL;
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/mikeyParse.cpp 000444 001752 001752 00000031053 15270745314 017754 0 ustar 00ec2-user ec2-user 000000 000000 // Parses MIKEY data (from Base64)
#include
#include
#include
#include
static u_int32_t get4Bytes(u_int8_t const*& ptr) {
u_int32_t result = (ptr[0]<<24)|(ptr[1]<<16)|(ptr[2]<<8)|ptr[3];
ptr += 4;
return result;
}
static u_int16_t get2Bytes(u_int8_t const*& ptr) {
u_int16_t result = (ptr[0]<<8)|ptr[1];
ptr += 2;
return result;
}
static u_int8_t getByte(u_int8_t const*& ptr) {
u_int8_t result = ptr[0];
ptr += 1;
return result;
}
Boolean parseMikeyUnknown(u_int8_t const*& /*ptr*/, u_int8_t const* /*endPtr*/, u_int8_t& /*nextPayloadType*/) {
fprintf(stderr, "\tUnknown or unhandled payload type\n");
return False;
}
char const* payloadTypeName[256];
char const* dataTypeComment[256];
#define testSize(n) do {if (ptr + (n) > endPtr) return False; } while (0)
Boolean parseMikeyHDR(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
testSize(10); // up to the start of "CS ID map info"
fprintf(stderr, "\tversion: %d\n", getByte(ptr));
u_int8_t const dataType = getByte(ptr);
fprintf(stderr, "\tdata type: %d (%s)\n", dataType, dataTypeComment[dataType]);
nextPayloadType = getByte(ptr);
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
u_int8_t const V_PRF = getByte(ptr);
u_int8_t const PRF = V_PRF&0x7F;
fprintf(stderr, "\tV:%d; PRF:%d (%s)\n", V_PRF>>7, PRF, PRF == 0 ? "MIKEY-1" : "unknown");
fprintf(stderr, "\tCSB ID:0x%08x\n", get4Bytes(ptr));
u_int8_t numCryptoSessions = getByte(ptr);
fprintf(stderr, "\t#CS:%d\n", numCryptoSessions);
u_int8_t const CS_ID_map_type = getByte(ptr);
fprintf(stderr, "\tCS ID map type:%d (%s)\n",
CS_ID_map_type, CS_ID_map_type == 0 ? "SRTP-ID" : "unknown");
if (CS_ID_map_type != 0) return False;
fprintf(stderr, "\tCS ID map info:\n");
testSize(numCryptoSessions * (1+4+4)); // the size of the "CS ID map info"
for (u_int8_t i = 1; i <= numCryptoSessions; ++i) {
u_int8_t policy_no = getByte(ptr);
u_int32_t ssrc = get4Bytes(ptr);
u_int32_t roc = get4Bytes(ptr);
fprintf(stderr, "\tPolicy_no_%d: %d;\tSSRC_%d: 0x%08x; ROC_%d: 0x%08x\n",
i, policy_no,
i, ssrc,
i, roc);
}
return True;
}
static Boolean parseKeyDataSubPayload(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
fprintf(stderr, "\tEncr data:\n");
testSize(4); // up to the start of "Key data"
nextPayloadType = getByte(ptr);
fprintf(stderr, "\t\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
u_int8_t Type_KV = getByte(ptr);
u_int8_t Type = Type_KV>>4;
u_int8_t KV = Type_KV&0x0F;
fprintf(stderr, "\t\tType: %d (%s)\n", Type,
Type == 0 ? "TGK" : Type == 1 ? "TGK+SALT" : Type == 2 ? "TEK" : Type == 3 ? "TEK+SALT" : "unknown");
if (Type > 3) return False;
Boolean hasSalt = Type == 1 || Type == 3;
fprintf(stderr, "\t\tKey Validity: %d (%s)\n", KV,
KV == 0 ? "NULL" : KV == 1 ? "SPI/MKI" : KV == 2 ? "Interval" : "unknown");
Boolean hasKV = KV != 0;
u_int16_t keyDataLen = get2Bytes(ptr);
fprintf(stderr, "\t\tKey data len: %d\n", keyDataLen);
testSize(keyDataLen);
fprintf(stderr, "\t\tKey data: ");
for (unsigned i = 0; i < keyDataLen; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
if (hasSalt) {
testSize(2);
u_int16_t saltLen = get2Bytes(ptr);
fprintf(stderr, "\t\tSalt len: %d\n", saltLen);
testSize(saltLen);
fprintf(stderr, "\t\tSalt data: ");
for (unsigned i = 0; i < saltLen; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
}
if (hasKV) {
fprintf(stderr, "\t\tKV (key validity) data:\n");
if (KV == 1) { // SPI/MKI
testSize(1);
u_int8_t SPILength = getByte(ptr);
fprintf(stderr, "\t\t\tSPI Length: %d\n", SPILength);
testSize(SPILength);
fprintf(stderr, "\t\t\tSPI: ");
for (unsigned i = 0; i < SPILength; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
} else if (KV == 2) { // Interval
testSize(1);
u_int8_t VFLength = getByte(ptr);
fprintf(stderr, "\t\t\tVF Length: %d\n", VFLength);
testSize(VFLength);
fprintf(stderr, "\t\t\tVF: ");
for (unsigned i = 0; i < VFLength; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
testSize(1);
u_int8_t VTLength = getByte(ptr);
fprintf(stderr, "\t\t\tVT Length: %d\n", VTLength);
testSize(VTLength);
fprintf(stderr, "\t\t\tVT: ");
for (unsigned i = 0; i < VTLength; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
}
}
return True;
}
Boolean parseMikeyKEMAC(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
testSize(4); // up to the start of "Encr data"
nextPayloadType = getByte(ptr);
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
u_int8_t encrAlg = getByte(ptr);
fprintf(stderr, "\tEncr alg: %d (%s)\n", encrAlg,
encrAlg == 0 ? "NULL" : encrAlg == 1 ? "AES-CM-128" : encrAlg == 2 ? "AES-KW-128" : "unknown");
u_int16_t encrDataLen = get2Bytes(ptr);
fprintf(stderr, "\tencr data len: %d\n", encrDataLen);
testSize(encrDataLen + 1/*allow for "Mac alg"*/);
u_int8_t const* endOfKeyData = ptr + encrDataLen;
// Allow for multiple key data sub-payloads
while (ptr < endOfKeyData) {
if (!parseKeyDataSubPayload(ptr, endOfKeyData, nextPayloadType)) return False;
}
u_int8_t macAlg = getByte(ptr);
fprintf(stderr, "\tMAC alg: %d (%s)\n", macAlg,
macAlg == 0 ? "NULL" : macAlg == 1 ? "HMAC-SHA-1-160" : "unknown");
if (macAlg > 1) return False;
if (macAlg == 1) { // HMAC-SHA-1-160
unsigned const macLen = 160/8; // bytes
fprintf(stderr, "\t\tMAC: ");
for (unsigned i = 0; i < macLen; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
}
return True;
}
Boolean parseMikeyT(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
testSize(2); // up to the start of "TS value"
nextPayloadType = getByte(ptr);
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
u_int8_t TS_type = getByte(ptr);
unsigned TS_value_len;
fprintf(stderr, "\tTS type: %d (", TS_type);
switch (TS_type) {
case 0: {
fprintf(stderr, "NTP-UTC)\n");
TS_value_len = 8; // 64 bits
break;
}
case 1: {
fprintf(stderr, "NTP)\n");
TS_value_len = 8; // 64 bits
break;
}
case 2: {
fprintf(stderr, "COUNTER)\n");
TS_value_len = 4; // 32 bits
break;
}
default: {
fprintf(stderr, "unknown)\n");
return False;
}
}
testSize(TS_value_len);
fprintf(stderr, "\tTS value:");
for (unsigned i = 0; i < TS_value_len; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
return True;
}
#define MAX_SRTP_POLICY_PARAM_TYPE 12
static char const* SRTPPolicyParamTypeExplanation[] = {
"Encryption algorithm",
"Session Encryption key length",
"Authentication algorithm",
"Session Authentication key length",
"Session Salt key length",
"SRTP Pseudo Random Function",
"Key derivation rate",
"SRTP encryption off/on",
"SRTCP encryption off/on",
"Sender's FEC order",
"SRTP authentication off/on",
"Authentication tag length",
"SRTP prefix length",
};
static Boolean parseSRTPPolicyParam(u_int8_t const*& ptr, u_int8_t const* endPtr) {
fprintf(stderr, "\tPolicy param:\n");
while (ptr < endPtr) {
testSize(2);
u_int8_t ppType = getByte(ptr);
fprintf(stderr, "\t\ttype: %d (%s); ", ppType,
ppType > MAX_SRTP_POLICY_PARAM_TYPE ? "unknown" : SRTPPolicyParamTypeExplanation[ppType]);
u_int8_t ppLen = getByte(ptr);
fprintf(stderr, "length: %d; value: ", ppLen);
testSize(ppLen);
u_int8_t ppVal = 0xFF;
if (ppLen == 1) {
ppVal = getByte(ptr);
fprintf(stderr, "%d", ppVal);
} else {
for (unsigned j = 0; j < ppLen; ++j) fprintf(stderr, ":%02x", getByte(ptr));
}
switch (ppType) {
case 0: { // Encryption algorithm
fprintf(stderr, " (%s)",
ppVal == 0 ? "NULL" : ppVal == 1 ? "AES-CM" : ppVal == 2 ? "AES-F8" : "unknown");
break;
}
case 2: { // Authentication algorithm
fprintf(stderr, " (%s)",
ppVal == 0 ? "NULL" : ppVal == 1 ? "HMAC-SHA-1" : "unknown");
break;
}
case 5: { // SRTP Pseudo Random Function
fprintf(stderr, " (%s)",
ppVal == 0 ? "AES-CM" : "unknown");
break;
}
case 9: { // sender's FEC order
fprintf(stderr, " (%s)",
ppVal == 0 ? "First FEC, then SRTP" : "unknown");
break;
}
}
fprintf(stderr, "\n");
}
return True;
}
Boolean parseMikeySP(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
testSize(2); // up to the start of "Policy param"
nextPayloadType = getByte(ptr);
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
fprintf(stderr, "\tPolicy number: %d\n", getByte(ptr));
u_int8_t protocolType = getByte(ptr);
fprintf(stderr, "\tProtocol type: %d (%s)\n", protocolType, protocolType == 0 ? "SRTP" : "unknown");
if (protocolType != 0) return False;
u_int16_t policyParam_len = get2Bytes(ptr);
fprintf(stderr, "\tPolicy param len: %d\n", policyParam_len);
testSize(policyParam_len);
return parseSRTPPolicyParam(ptr, ptr + policyParam_len);
}
Boolean parseMikeyRAND(u_int8_t const*& ptr, u_int8_t const* endPtr, u_int8_t& nextPayloadType) {
testSize(2); // up to the start of "RAND"
nextPayloadType = getByte(ptr);
fprintf(stderr, "\tnext payload: %d (%s)\n", nextPayloadType, payloadTypeName[nextPayloadType]);
u_int8_t RAND_len = getByte(ptr);
fprintf(stderr, "\tRAND len: %d", RAND_len);
testSize(RAND_len);
fprintf(stderr, "\tRAND:");
for (unsigned i = 0; i < RAND_len; ++i) fprintf(stderr, ":%02x", getByte(ptr));
fprintf(stderr, "\n");
return True;
}
typedef Boolean (parseMikeyPayloadFunc)(u_int8_t const*& ptr, u_int8_t const* endPtr,
u_int8_t& nextPayloadType);
parseMikeyPayloadFunc* payloadParser[256];
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s \n", argv[0]);
exit(1);
}
char const* base64Data = argv[1];
unsigned mikeyDataSize;
u_int8_t* mikeyData = base64Decode(base64Data, mikeyDataSize);
fprintf(stderr, "Base64Data \"%s\" produces %d bytes of MIKEY data:\n", base64Data, mikeyDataSize);
for (unsigned i = 0; i < mikeyDataSize; ++i) fprintf(stderr, ":%02x", mikeyData[i]);
fprintf(stderr, "\n");
for (unsigned i = 0; i < 256; ++i) {
payloadTypeName[i] = "unknown or unhandled";
payloadParser[i] = parseMikeyUnknown;
dataTypeComment[i] = "unknown";
}
// Populate known payload types:
payloadTypeName[0] = "Last payload";
payloadTypeName[1] = "KEMAC";
payloadParser[1] = parseMikeyKEMAC;
payloadTypeName[2] = "PKE";
payloadTypeName[3] = "DH";
payloadTypeName[4] = "SIGN";
payloadTypeName[5] = "T";
payloadParser[5] = parseMikeyT;
payloadTypeName[6] = "ID";
payloadTypeName[7] = "CERT";
payloadTypeName[8] = "CHASH";
payloadTypeName[9] = "V";
payloadTypeName[10] = "SP";
payloadParser[10] = parseMikeySP;
payloadTypeName[11] = "RAND";
payloadParser[11] = parseMikeyRAND;
payloadTypeName[12] = "ERR";
payloadTypeName[20] = "Key data";
payloadTypeName[21] = "General Ext.";
// Populate known data types:
dataTypeComment[0] = "Initiator's pre-shared key message";
dataTypeComment[1] = "Verification message of a pre-shared key message";
dataTypeComment[2] = "Initiator's public-key transport message";
dataTypeComment[3] = "Verification message of a public-key message";
dataTypeComment[4] = "Initiator's DH exchange message";
dataTypeComment[5] = "Responder's DH exchange message";
dataTypeComment[6] = "Error message";
u_int8_t const* ptr = mikeyData;
u_int8_t* const endPtr = &mikeyData[mikeyDataSize];
u_int8_t nextPayloadType;
do {
// Begin by parsing an initial "HDR":
fprintf(stderr, "HDR:\n");
if (!parseMikeyHDR(ptr, endPtr, nextPayloadType)) break;
// Then parse each successive payload:
while (nextPayloadType != 0 /* Last payload */) {
fprintf(stderr, "%s:\n", payloadTypeName[nextPayloadType]);
if (!(*payloadParser[nextPayloadType])(ptr, endPtr, nextPayloadType)) break;
}
} while (0);
if (ptr < endPtr) {
fprintf(stderr, "+%ld bytes of unparsed data: ", endPtr-ptr);
while (ptr < endPtr) fprintf(stderr, ":%02x", *ptr++);
fprintf(stderr, "\n");
}
delete[] mikeyData;
return 0;
}
live/testProgs/testH264VideoToHLSSegments.cpp 000444 001752 001752 00000010005 15270745314 022527 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A program that converts a H.264 (Elementary Stream) video file into a sequence of
// HLS (HTTP Live Streaming) segments, plus a ".m3u8" file that can be accessed via a web browser.
// main program
#include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#define OUR_HLS_SEGMENTATION_DURATION 6
#define OUR_HLS_FILENAME_PREFIX "hlsTest"
char const* inputFileName = "in.264";
FILE* ourM3U8Fid = NULL;
void segmentationCallback(void* clientData, char const* segmentFileName, double segmentDuration); // forward
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*/,
True/*insertAccessUnitDelimiters*/);
// 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*/);
// Create a 'HLS Segmenter' as the media sink:
MediaSink* outputSink
= HLSSegmenter::createNew(*env, OUR_HLS_SEGMENTATION_DURATION, OUR_HLS_FILENAME_PREFIX,
segmentationCallback);
// 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 segmentationCallback(void* /*clientData*/,
char const* segmentFileName, double segmentDuration) {
if (ourM3U8Fid == NULL) {
// Open our ".m3u8" file for output, and write the prefix:
size_t const ourM3U8FileNameLen = strlen(OUR_HLS_FILENAME_PREFIX) + 5/*strlen(".m3u8")*/ + 1;
char* ourM3U8FileName = new char[ourM3U8FileNameLen];
snprintf(ourM3U8FileName, ourM3U8FileNameLen, "%s.m3u8", OUR_HLS_FILENAME_PREFIX);
ourM3U8Fid = fopen(ourM3U8FileName, "wb");
fprintf(ourM3U8Fid,
"#EXTM3U\n"
"#EXT-X-VERSION:3\n"
"#EXT-X-INDEPENDENT-SEGMENTS\n"
"#EXT-X-TARGETDURATION:%u\n"
"#EXT-X-MEDIA-SEQUENCE:0\n",
OUR_HLS_SEGMENTATION_DURATION);
}
// Update our ".m3u8" file with information about this most recent segment:
fprintf(ourM3U8Fid,
"#EXTINF:%f,\n"
"%s\n",
segmentDuration,
segmentFileName);
fprintf(stderr, "Wrote segment \"%s\" (duration: %f seconds)\n", segmentFileName, segmentDuration);
}
void afterPlaying(void* /*clientData*/) {
*env << "...Done reading\n";
// Complete and close our ".m3u8" file:
fprintf(ourM3U8Fid, "#EXT-X-ENDLIST\n");
fprintf(stderr, "Wrote %s.m3u8\n", OUR_HLS_FILENAME_PREFIX);
exit(0);
}
live/testProgs/testMPEG2TransportStreamSplitter.cpp 000444 001752 001752 00000004376 15270745314 024205 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// A test program that splits a MPEG Transport Stream input (on 'stdin')
// into separate video and audio output files.
// main program
#include
#include
UsageEnvironment* env;
char const* programName;
char const* inputFileName = "stdin";
MPEG2TransportStreamDemux* baseDemultiplexor = NULL;
void usage() {
*env << "usage: " << programName << " takes no arguments (it reads from \"stdin\")\n";
exit(1);
}
void afterReading(void*);
int main(int argc, char** 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 != 1) usage();
// 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 demultiplexor that reads from that source, creating new 'demultiplexed tracks'
// as they appear:
baseDemultiplexor = MPEG2TransportStreamDemux::createNew(*env, inputSource, afterReading, NULL);
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
void afterReading(void* /*clientData*/) {
*env << "...done\n";
exit(0);
}
live/testProgs/testOnDemandRTSPServer.cpp 000444 001752 001752 00000043372 15270745314 022137 0 ustar 00ec2-user ec2-user 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-2026, 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"
#include "announceURL.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); // forward
static EventLoopWatchVariable 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:
#ifdef SERVER_USE_TLS
// Serve RTSPS: RTSP over a TLS connection:
RTSPServer* rtspServer = RTSPServer::createNew(*env, 322, authDB);
#else
// Serve regular RTSP (over a TCP connection):
RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
#endif
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
#ifdef SERVER_USE_TLS
#ifndef STREAM_USING_SRTP
#define STREAM_USING_SRTP True
#endif
rtspServer->setTLSState(PATHNAME_TO_CERTIFICATE_FILE, PATHNAME_TO_PRIVATE_KEY_FILE,
STREAM_USING_SRTP);
#endif
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);
*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";
announceURL(rtspServer, sms);
}
// 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).
#ifdef SERVER_USE_TLS
// (Attempt to) use the default HTTPS port (443) instead:
char const* httpProtocolStr = "HTTPS";
if (rtspServer->setUpTunnelingOverHTTP(443)) {
#else
char const* httpProtocolStr = "HTTP";
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
#endif
*env << "\n(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-" << httpProtocolStr << " tunneling.)\n";
} else {
*env << "\n(RTSP-over-" << httpProtocolStr << " 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) {
UsageEnvironment& env = rtspServer->envir();
env << "\n\"" << streamName << "\" stream, from the file \""
<< inputFileName << "\"\n";
announceURL(rtspServer, sms);
}
live/BasicUsageEnvironment/BasicHashTable.cpp 000444 001752 001752 00000016756 15270745314 022716 0 ustar 00ec2-user ec2-user 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-2026 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 00000024726 15270745314 023620 0 ustar 00ec2-user ec2-user 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-2026 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
// REMINDER: You should not make major changes to this code. This code is intended to
// implement just a basic, simple task scheduler. If you want a more complex task scheduler,
// then you should use subclassing, with separate source code files.
// See: http://live555.com/liveMedia/faq.html#modifying-and-extending
#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 <
#if defined(FD_SETSIZE)
FD_SETSIZE;
#else
fMaxNumSockets;
#endif
++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 (fEventTriggersAreBeingUsed) {
// 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 = EVENT_TRIGGER_ID_HIGH_BIT;
#ifndef NO_STD_LIB
if (fTriggersAwaitingHandling[i].test()) {
fTriggersAwaitingHandling[i].clear();
#else
if (fTriggersAwaitingHandling[i]) {
fTriggersAwaitingHandling[i] = False;
#endif
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 00000017035 15270745314 023673 0 ustar 00ec2-user ec2-user 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-2026 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, intptr_t token)
: DelayQueueEntry(timeToDelay, token), fProc(proc), fClientData(clientData) {
}
private: // redefined virtual functions
virtual void handleTimeout() {
(*fProc)(fClientData);
DelayQueueEntry::handleTimeout();
}
private:
TaskFunc* fProc;
void* fClientData;
};
////////// BasicTaskScheduler0 //////////
BasicTaskScheduler0::BasicTaskScheduler0()
: fTokenCounter(0), fLastHandledSocketNum(-1),
fLastUsedTriggerMask(1), fLastUsedTriggerNum(MAX_NUM_EVENT_TRIGGERS-1),
fEventTriggersAreBeingUsed(False) {
fHandlers = new HandlerSet;
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
#ifndef NO_STD_LIB
fTriggersAwaitingHandling[i].clear();
#else
fTriggersAwaitingHandling[i] = False;
#endif
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, ++fTokenCounter);
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(EventLoopWatchVariable* 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;
u_int32_t mask = fLastUsedTriggerMask;
do {
i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
mask >>= 1;
if (mask == 0) mask = EVENT_TRIGGER_ID_HIGH_BIT;
if (fTriggeredEventHandlers[i] == NULL) {
// This trigger number is free; use it:
fTriggeredEventHandlers[i] = eventHandlerProc;
fTriggeredEventClientDatas[i] = NULL; // sanity
fLastUsedTriggerMask = mask;
fLastUsedTriggerNum = i;
fEventTriggersAreBeingUsed = True;
return mask;
}
} while (i != fLastUsedTriggerNum);
// All available event triggers are allocated; return 0 instead:
return 0;
}
void BasicTaskScheduler0::deleteEventTrigger(EventTriggerId eventTriggerId) {
// "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 = EVENT_TRIGGER_ID_HIGH_BIT;
Boolean eventTriggersAreBeingUsed = False;
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
if ((eventTriggerId&mask) != 0) {
#ifndef NO_STD_LIB
fTriggersAwaitingHandling[i].clear();
#else
fTriggersAwaitingHandling[i] = False;
#endif
fTriggeredEventHandlers[i] = NULL;
fTriggeredEventClientDatas[i] = NULL;
} else if (fTriggeredEventHandlers[i] != NULL) {
eventTriggersAreBeingUsed = True;
}
mask >>= 1;
}
fEventTriggersAreBeingUsed = eventTriggersAreBeingUsed;
}
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 = EVENT_TRIGGER_ID_HIGH_BIT;
for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
if ((eventTriggerId&mask) != 0) {
fTriggeredEventClientDatas[i] = clientData;
#ifndef NO_STD_LIB
(void)fTriggersAwaitingHandling[i].test_and_set();
#else
fTriggersAwaitingHandling[i] = True;
#endif
}
mask >>= 1;
}
}
////////// 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 00000005717 15270745314 024347 0 ustar 00ec2-user ec2-user 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-2026 Live Networks, Inc. All rights reserved.
// Basic Usage Environment: for a simple, non-scripted, console application
// Implementation
// REMINDER: You should not make major changes to this code. This code is intended to
// implement just a basic, simple "UsageEnvironment". If you want a more complex implementation,
// then you should use subclassing, with separate source code files.
// See: http://live555.com/liveMedia/faq.html#modifying-and-extending
#include "BasicUsageEnvironment.hh"
#include
////////// library version constants //////////
extern char const* const BasicUsageEnvironmentLibraryVersionStr = BASICUSAGEENVIRONMENT_LIBRARY_VERSION_STRING;
extern int const BasicUsageEnvironmentLibraryVersionInt = BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT;
////////// 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 15270745314 024417 0 ustar 00ec2-user ec2-user 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-2026 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 15270745314 021672 2../COPYING ustar 00ec2-user ec2-user 000000 000000 live/BasicUsageEnvironment/DelayQueue.cpp 000444 001752 001752 00000014355 15270745314 022155 0 ustar 00ec2-user ec2-user 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-2026, 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 /////
DelayQueueEntry::DelayQueueEntry(DelayInterval delay, intptr_t token)
: fDeltaTimeRemaining(delay), fToken(token) {
fNext = fPrev = this;
}
DelayQueueEntry::~DelayQueueEntry() {
}
void DelayQueueEntry::handleTimeout() {
delete this;
}
///// DelayQueue /////
DelayQueue::DelayQueue()
: DelayQueueEntry(ETERNITY, 0) {
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 15270745314 021023 5 ustar 00ec2-user ec2-user 000000 000000 live/BasicUsageEnvironment/Makefile.head 000440 001752 001752 00000000246 15270745314 021734 0 ustar 00ec2-user ec2-user 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 00000003225 15270745314 021770 0 ustar 00ec2-user ec2-user 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: install1
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 15270745314 023662 2../COPYING.LESSER ustar 00ec2-user ec2-user 000000 000000 live/BasicUsageEnvironment/include/BasicHashTable.hh 000444 001752 001752 00000006621 15270745314 024144 0 ustar 00ec2-user ec2-user 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-2026 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 15270745314 025603 0 ustar 00ec2-user ec2-user 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-2026 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 00000010070 15270745314 025653 0 ustar 00ec2-user ec2-user 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-2026 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
// Note: You may redefine MAX_NUM_EVENT_TRIGGERS,
// but it must be <= the number of bits in an "EventTriggerId"
#ifndef MAX_NUM_EVENT_TRIGGERS
#define MAX_NUM_EVENT_TRIGGERS 32
#endif
#define EVENT_TRIGGER_ID_HIGH_BIT (1 << (MAX_NUM_EVENT_TRIGGERS-1))
// 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(EventLoopWatchVariable* 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:
intptr_t fTokenCounter;
DelayQueue fDelayQueue;
// To implement background reads:
HandlerSet* fHandlers;
int fLastHandledSocketNum;
// To implement event triggers:
#ifndef NO_STD_LIB
std::atomic_flag fTriggersAwaitingHandling[MAX_NUM_EVENT_TRIGGERS];
#else
Boolean volatile fTriggersAwaitingHandling[MAX_NUM_EVENT_TRIGGERS];
#endif
u_int32_t 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)
Boolean fEventTriggersAreBeingUsed;
};
#endif
live/BasicUsageEnvironment/include/BasicUsageEnvironment_version.hh 000444 001752 001752 00000002343 15270745314 027344 0 ustar 00ec2-user ec2-user 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
**********/
// Version information for the "BasicUsageEnvironment" library
// Copyright (c) 1996-2026 Live Networks, Inc. All rights reserved.
#ifndef _BASICUSAGEENVIRONMENT_VERSION_HH
#define _BASICUSAGEENVIRONMENT_VERSION_HH
#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_STRING "2026.10.29"
#define BASICUSAGEENVIRONMENT_LIBRARY_VERSION_INT 1793232000
extern char const* const BasicUsageEnvironmentLibraryVersionStr;
extern int const BasicUsageEnvironmentLibraryVersionInt;
#endif
live/BasicUsageEnvironment/include/DelayQueue.hh 000444 001752 001752 00000011036 15270745314 023406 0 ustar 00ec2-user ec2-user 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-2026, 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, intptr_t token);
virtual void handleTimeout();
private:
friend class DelayQueue;
DelayQueueEntry* fNext;
DelayQueueEntry* fPrev;
DelayInterval fDeltaTimeRemaining;
intptr_t fToken;
};
///// 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 15270745314 023372 0 ustar 00ec2-user ec2-user 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-2026 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 15270745314 020730 2../COPYING ustar 00ec2-user ec2-user 000000 000000 live/UsageEnvironment/HashTable.cpp 000444 001752 001752 00000002621 15270745314 020774 0 ustar 00ec2-user ec2-user 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-2026 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 15270745314 020061 5 ustar 00ec2-user ec2-user 000000 000000 live/UsageEnvironment/Makefile.head 000440 001752 001752 00000000210 15270745314 020761 0 ustar 00ec2-user ec2-user 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 00000002363 15270745314 021030 0 ustar 00ec2-user ec2-user 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: install1
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 15270745314 020433 0 ustar 00ec2-user ec2-user 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-2026 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 00000004477 15270745314 022445 0 ustar 00ec2-user ec2-user 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-2026 Live Networks, Inc. All rights reserved.
// Usage Environment
// Implementation
#include "UsageEnvironment.hh"
////////// library version constants //////////
extern char const* const UsageEnvironmentLibraryVersionStr = USAGEENVIRONMENT_LIBRARY_VERSION_STRING;
extern int const UsageEnvironmentLibraryVersionInt = USAGEENVIRONMENT_LIBRARY_VERSION_INT;
////////// UsageEnvironment //////////
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 15270745314 022720 2../COPYING.LESSER ustar 00ec2-user ec2-user 000000 000000 live/UsageEnvironment/include/Boolean.hh 000444 001752 001752 00000002361 15270745314 021761 0 ustar 00ec2-user ec2-user 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 15270745314 022244 0 ustar 00ec2-user ec2-user 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-2026 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 15270745314 021667 0 ustar 00ec2-user ec2-user 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-2026 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 00000016432 15270745314 023677 0 ustar 00ec2-user ec2-user 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-2026 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
#ifndef NO_STD_LIB
#ifndef _LIBCPP_ATOMIC
#include
#endif
#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;
#ifndef NO_STD_LIB
typedef std::atomic_char EventLoopWatchVariable;
#else
typedef char volatile EventLoopWatchVariable;
#endif
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(EventLoopWatchVariable* 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.
// (In fact, this is the *only* LIVE555 function that can be called from a non-LIVE555 thread.)
// (However, "triggerEvent()" should not be called with the same 'event trigger id' from
// different threads. Also, once "triggerEvent()" is called with one 'event trigger id',
// it should not be called again with the same 'event trigger id' until after its event
// has been handled.)
// 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 00000002300 15270745314 025431 0 ustar 00ec2-user ec2-user 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
**********/
// Version information for the "UsageEnvironment" library
// Copyright (c) 1996-2026 Live Networks, Inc. All rights reserved.
#ifndef _USAGEENVIRONMENT_VERSION_HH
#define _USAGEENVIRONMENT_VERSION_HH
#define USAGEENVIRONMENT_LIBRARY_VERSION_STRING "2026.10.29"
#define USAGEENVIRONMENT_LIBRARY_VERSION_INT 1793232000
extern char const* const UsageEnvironmentLibraryVersionStr;
extern int const UsageEnvironmentLibraryVersionInt;
#endif
live/groupsock/COPYING 000755 001752 001752 00000000000 15270745314 017453 2../COPYING ustar 00ec2-user ec2-user 000000 000000 live/groupsock/GroupEId.cpp 000444 001752 001752 00000003504 15270745314 017343 0 ustar 00ec2-user ec2-user 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-2026, Live Networks, Inc. All rights reserved
// "Group Endpoint Id"
// Implementation
#include "GroupEId.hh"
GroupEId::GroupEId(struct sockaddr_storage const& groupAddr,
portNumBits portNum, u_int8_t ttl) {
init(groupAddr, nullAddress(), portNum, ttl);
}
GroupEId::GroupEId(struct sockaddr_storage const& groupAddr,
struct sockaddr_storage const& sourceFilterAddr,
portNumBits portNum) {
init(groupAddr, sourceFilterAddr, portNum, 255);
}
GroupEId::GroupEId() {
init(nullAddress(), nullAddress(), 0, 255);
}
Boolean GroupEId::isSSM() const {
// We're a SSM group if "fSourceFilterAddress" is not a 'null' address:
return !addressIsNull(fSourceFilterAddress);
}
portNumBits GroupEId::portNum() const { return ::portNum(fGroupAddress); }
void GroupEId::init(struct sockaddr_storage const& groupAddr,
struct sockaddr_storage const& sourceFilterAddr,
portNumBits portNum,
u_int8_t ttl) {
fGroupAddress = groupAddr;
setPortNum(fGroupAddress, portNum);
fSourceFilterAddress = sourceFilterAddr;
fTTL = ttl;
}
live/groupsock/Groupsock.cpp 000444 001752 001752 00000042525 15270745314 017647 0 ustar 00ec2-user ec2-user 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-2026 Live Networks, Inc. All rights reserved.
// 'Group sockets'
// Implementation
#include "Groupsock.hh"
#include "GroupsockHelper.hh"
#ifndef NO_SSTREAM
#include
#endif
#include
////////// library version constants //////////
extern char const* const groupsockLibraryVersionStr = GROUPSOCK_LIBRARY_VERSION_STRING;
extern int const groupsockLibraryVersionInt = GROUPSOCK_LIBRARY_VERSION_INT;
///////// OutputSocket //////////
OutputSocket::OutputSocket(UsageEnvironment& env, int family)
: Socket(env, 0 /* let kernel choose port */, family),
fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
}
OutputSocket::OutputSocket(UsageEnvironment& env, Port port, int family)
: Socket(env, port, family),
fSourcePort(0), fLastSentTTL(256/*hack: a deliberately invalid value*/) {
}
OutputSocket::~OutputSocket() {
}
Boolean OutputSocket::write(struct sockaddr_storage const& addressAndPort, u_int8_t ttl,
unsigned char* buffer, unsigned bufferSize) {
if ((unsigned)ttl == fLastSentTTL) {
// Optimization: Don't do a 'set TTL' system call again
if (!writeSocket(env(), socketNum(), addressAndPort, buffer, bufferSize)) return False;
} else {
if (!writeSocket(env(), socketNum(), addressAndPort, 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(), addressAndPort.ss_family, 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_storage& /*fromAddressAndPort*/) {
return True;
}
///////// destRecord //////////
destRecord
::destRecord(struct sockaddr_storage 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;
// Constructor for a source-independent multicast group
Groupsock::Groupsock(UsageEnvironment& env, struct sockaddr_storage const& groupAddr,
Port port, u_int8_t ttl)
: OutputSocket(env, port, groupAddr.ss_family),
fDests(new destRecord(groupAddr, port, ttl, 0, NULL)),
fIncomingGroupEId(groupAddr, port.num(), ttl) {
if (!socketJoinGroup(env, socketNum(), groupAddr)) {
if (DebugLevel >= 1) {
env << *this << ": failed to join group: "
<< env.getResultMsg() << "\n";
}
}
// Make sure we can get our source address:
if (!weHaveAnIPAddress(env)) {
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 sockaddr_storage const& groupAddr,
struct sockaddr_storage const& sourceFilterAddr,
Port port)
: OutputSocket(env, port, groupAddr.ss_family),
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, sourceFilterAddr)) {
if (DebugLevel >= 3) {
env << *this << ": SSM join failed: "
<< env.getResultMsg();
env << " - trying regular join instead\n";
}
if (!socketJoinGroup(env, socketNum(), groupAddr)) {
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(), sourceFilterAddress())) {
socketLeaveGroup(env(), socketNum(), groupAddress());
}
} else {
socketLeaveGroup(env(), socketNum(), groupAddress());
}
delete fDests;
if (DebugLevel >= 2) env() << *this << ": deleting\n";
}
destRecord* Groupsock
::createNewDestRecord(struct sockaddr_storage 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 sockaddr_storage 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 sockaddr_storage destAddr = dest->fGroupEId.groupAddress();
if (!addressIsNull(newDestAddr)) {
// Replace "destAddr" with "newDestAddr"
if (!(newDestAddr == destAddr) && IsMulticastAddress(newDestAddr)) {
// 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);
socketJoinGroup(env(), socketNum(), newDestAddr);
}
destAddr = newDestAddr;
}
portNumBits destPortNum = dest->fGroupEId.portNum();
if (newDestPort.num() != 0) {
// Replace "destPort" with "newDestPort"
if (newDestPort.num() != destPortNum && IsMulticastAddress(destAddr)) {
// Also bind to the new port number:
changePort(newDestPort);
// And rejoin the multicast group:
socketJoinGroup(env(), socketNum(), destAddr);
}
destPortNum = newDestPort.num();
}
u_int8_t destTTL = ttl();
if (newDestTTL != ~0) {
// Replace "destTTL" with "newDestTTL"
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_storage const& destAddrAndPort) const {
destRecord* dest = lookupDestRecordFromDestination(destAddrAndPort);
if (dest == NULL) return 0;
return dest->fSessionId;
}
void Groupsock::addDestination(struct sockaddr_storage 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 (dest->fSessionId == sessionId &&
dest->fGroupEId.groupAddress() == addr &&
dest->fGroupEId.portNum() == port.num()) {
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(), groupAddress());
for (destRecord* dests = fDests; dests != NULL; dests = dests->fNext) {
socketLeaveGroup(env(), socketNum(), dests->fGroupEId.groupAddress());
}
#endif
}
Boolean Groupsock::output(UsageEnvironment& env, unsigned char* buffer, unsigned bufferSize) {
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(), dests->fGroupEId.ttl(), buffer, bufferSize)) {
writeSuccess = False;
break;
}
}
if (!writeSuccess) break;
statsOutgoing.countPacket(bufferSize);
statsGroupOutgoing.countPacket(bufferSize);
if (DebugLevel >= 3) {
env << *this << ": wrote " << bufferSize << " bytes, ttl " << (unsigned)ttl() << "\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_storage& fromAddressAndPort) {
bytesRead = 0; // initially
int numBytes = readSocket(env(), socketNum(),
buffer, bufferMaxSize, 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 == sourceFilterAddress())) return True;
// We'll handle this data.
bytesRead = numBytes;
if (!wasLoopedBackFromUs(env(), fromAddressAndPort)) {
statsIncoming.countPacket(bytesRead);
statsGroupIncoming.countPacket(bytesRead);
}
if (DebugLevel >= 3) {
env() << *this << ": read " << bytesRead << " bytes from " << AddressString(fromAddressAndPort).val() << ", port " << ntohs(portNum(fromAddressAndPort)) << "\n";
}
return True;
}
Boolean Groupsock::wasLoopedBackFromUs(UsageEnvironment& env,
struct sockaddr_storage const& fromAddressAndPort) {
if (fromAddressAndPort.ss_family != AF_INET) return False; // later update for IPv6
struct sockaddr_in const& fromAddressAndPort4 = (struct sockaddr_in const&)fromAddressAndPort;
if (fromAddressAndPort4.sin_addr.s_addr == ourIPv4Address(env) ||
fromAddressAndPort4.sin_addr.s_addr == 0x7F000001/*127.0.0.1*/) {
if (portNum(fromAddressAndPort) == 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_storage const& targetAddrAndPort) const {
for (destRecord* dest = fDests; dest != NULL; dest = dest->fNext) {
if (dest->fGroupEId.groupAddress() == targetAddrAndPort &&
dest->fGroupEId.portNum() == portNum(targetAddrAndPort)) {
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);
}
}
}
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];
snprintf(buf, sizeof 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];
snprintf(buf, sizeof 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,
struct sockaddr_storage const& groupAddress,
Port port, u_int8_t ttl,
Boolean& isNew) {
isNew = False;
Groupsock* groupsock;
do {
groupsock = (Groupsock*) fTable.Lookup(groupAddress, port);
if (groupsock == NULL) { // we need to create one:
groupsock = AddNew(env, groupAddress, nullAddress(), port, ttl);
if (groupsock == NULL) break;
isNew = True;
}
} while (0);
return groupsock;
}
Groupsock*
GroupsockLookupTable::Fetch(UsageEnvironment& env,
struct sockaddr_storage const& groupAddress,
struct sockaddr_storage const& 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(struct sockaddr_storage const& groupAddress, Port port) {
return (Groupsock*) fTable.Lookup(groupAddress, port);
}
Groupsock*
GroupsockLookupTable::Lookup(struct sockaddr_storage const& groupAddress,
struct sockaddr_storage const& 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(), groupsock->sourceFilterAddress(), groupsock->port());
}
Groupsock* GroupsockLookupTable::AddNew(UsageEnvironment& env,
struct sockaddr_storage const& groupAddress,
struct sockaddr_storage const& sourceFilterAddress,
Port port, u_int8_t ttl) {
Groupsock* groupsock;
do {
if (addressIsNull(sourceFilterAddress)) {
// regular, ISM groupsock
groupsock = new Groupsock(env, groupAddress, port, ttl);
} else {
// SSM groupsock
groupsock = new Groupsock(env, groupAddress, sourceFilterAddress, 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 00000107335 15270745314 021010 0 ustar 00ec2-user ec2-user 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"
// Copyright (c) 1996-2026 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
#if !defined(_WIN32)
#include
#ifdef __ANDROID_NDK__
#include
#define ANDROID_OLD_NDK __NDK_MAJOR__ < 17
#endif
#endif
#include
#define initializeWinsockIfNecessary() 1
#endif
#if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4)
#else
#include
#define USE_SIGNALS 1
#endif
#ifndef NO_GETIFADDRS
#include
#include
#endif
#include
// By default, use INADDR_ANY for the sending and receiving interfaces (IPv4 only):
ipv4AddressBits SendingInterfaceAddr = INADDR_ANY;
ipv4AddressBits ReceivingInterfaceAddr = INADDR_ANY;
in6_addr ReceivingInterfaceAddr6 = IN6ADDR_ANY_INIT;
#ifdef SO_BINDTODEVICE
char InterfaceBindToDevice[128] = {0};
#endif
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 domain, int type) {
// Call "socket()" to create a socket of the specified type.
// But also set it to have the 'close on exec' property (if we can)
int sock;
// In case PF_INET(6) is not defined to be AF_INET(6):
int const domain2 = domain == AF_INET ? PF_INET : domain == AF_INET6 ? PF_INET6 : domain;
#ifdef SOCK_CLOEXEC
sock = socket(domain2, 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(domain2, type, 0);
#ifdef FD_CLOEXEC
if (sock != -1) fcntl(sock, F_SETFD, FD_CLOEXEC);
#endif
return sock;
}
int setupDatagramSocket(UsageEnvironment& env, Port port, int domain) {
if (!initializeWinsockIfNecessary()) {
socketErr(env, "Failed to initialize 'winsock': ");
return -1;
}
int newSocket = createSocket(domain, SOCK_DGRAM);
if (newSocket < 0) {
socketErr(env, "unable to create datagram socket: ");
return newSocket;
}
#ifdef SO_BINDTODEVICE
if (InterfaceBindToDevice[0] != '\0') {
if (setsockopt(newSocket, SOL_SOCKET, SO_BINDTODEVICE, InterfaceBindToDevice, strlen(InterfaceBindToDevice)) < 0) {
socketErr(env, "setsockopt(SO_BINDTODEVICE) error: ");
closeSocket(newSocket);
return -1;
}
}
#endif
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,
domain == AF_INET ? IPPROTO_IP : IPPROTO_IPV6,
domain == AF_INET ? IP_MULTICAST_LOOP : IPV6_MULTICAST_LOOP,
(const char*)&loop, sizeof loop) < 0) {
if (domain == AF_INET) { // For some unknown reason, this might not work for IPv6
socketErr(env, "setsockopt(IP_MULTICAST_LOOP) error: ");
closeSocket(newSocket);
return -1;
}
}
#endif
#endif
if (domain == AF_INET) {
// Note: Windoze requires binding, even if the port number is 0
ipv4AddressBits 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];
snprintf(tmpBuffer, sizeof tmpBuffer, "IPv4 bind() error (port number: %d): ", ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
#else
}
#endif
} else { // IPv6
in6_addr addr = IN6ADDR_ANY_INIT;
if (port.num() != 0) {
// For IPv6 sockets, we need the IPV6_V6ONLY flag set to 1, otherwise we would not
// be able to have an IPv4 socket and an IPv6 socket bound to the same port:
int const one = 1;
(void)setsockopt(newSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&one, sizeof one);
MAKE_SOCKADDR_IN6(name, addr, port.num());
if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
char tmpBuffer[100];
snprintf(tmpBuffer, sizeof tmpBuffer, "IPv6 bind() error (port number: %d): ", ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
}
}
// Set the sending interface for multicasts, if it's not the default:
if (SendingInterfaceAddr != INADDR_ANY) { // later, fix for IPv6
struct in_addr addr;
addr.s_addr = SendingInterfaceAddr;
if (setsockopt(newSocket,
domain == AF_INET ? IPPROTO_IP : IPPROTO_IPV6,
domain == AF_INET ? IP_MULTICAST_IF : IPV6_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;
}
Boolean setSocketKeepAlive(int sock) {
#if defined(__WIN32__) || defined(_WIN32)
// How do we do this in Windows? For now, just make this a no-op in Windows:
#else
int const keepalive_enabled = 1;
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepalive_enabled, sizeof keepalive_enabled) < 0) {
return False;
}
#ifdef TCP_KEEPIDLE
int const keepalive_time = 180;
if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepalive_time, sizeof keepalive_time) < 0) {
return False;
}
#endif
#ifdef TCP_KEEPCNT
int const keepalive_count = 5;
if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepalive_count, sizeof keepalive_count) < 0) {
return False;
}
#endif
#ifdef TCP_KEEPINTVL
int const keepalive_interval = 20;
if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepalive_interval, sizeof keepalive_interval) < 0) {
return False;
}
#endif
#endif
return True;
}
int setupStreamSocket(UsageEnvironment& env, Port port, int domain,
Boolean makeNonBlocking, Boolean setKeepAlive) {
if (!initializeWinsockIfNecessary()) {
socketErr(env, "Failed to initialize 'winsock': ");
return -1;
}
int newSocket = createSocket(domain, SOCK_STREAM);
if (newSocket < 0) {
socketErr(env, "unable to create stream socket: ");
return newSocket;
}
#ifdef SO_BINDTODEVICE
if (InterfaceBindToDevice[0] != '\0') {
if (setsockopt(newSocket, SOL_SOCKET, SO_BINDTODEVICE, InterfaceBindToDevice, strlen(InterfaceBindToDevice)) < 0) {
socketErr(env, "setsockopt(SO_BINDTODEVICE) error: ");
closeSocket(newSocket);
return -1;
}
}
#endif
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
if (domain == AF_INET) {
// 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];
snprintf(tmpBuffer, sizeof tmpBuffer, "IPv4 bind() error (port number: %d): ", ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
#if defined(__WIN32__) || defined(_WIN32)
#else
}
#endif
} else { // IPv6
if (port.num() != 0) {
// For IPv6 sockets, we need the IPV6_V6ONLY flag set to 1, otherwise we would not
// be able to have an IPv4 socket and an IPv6 socket bound to the same port:
int const one = 1;
(void)setsockopt(newSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&one, sizeof one);
MAKE_SOCKADDR_IN6(name, ReceivingInterfaceAddr6, port.num());
if (bind(newSocket, (struct sockaddr*)&name, sizeof name) != 0) {
char tmpBuffer[100];
snprintf(tmpBuffer, sizeof tmpBuffer, "IPv6 bind() error (port number: %d): ", ntohs(port.num()));
socketErr(env, tmpBuffer);
closeSocket(newSocket);
return -1;
}
}
}
if (makeNonBlocking) {
if (!makeSocketNonBlocking(newSocket)) {
socketErr(env, "failed to make non-blocking: ");
closeSocket(newSocket);
return -1;
}
}
// Set the keep alive mechanism for the TCP socket, to avoid "ghost sockets"
// that remain after an interrupted communication.
if (setKeepAlive) {
if (!setSocketKeepAlive(newSocket)) {
socketErr(env, "failed to set keep alive: ");
closeSocket(newSocket);
return -1;
}
}
return newSocket;
}
int readSocket(UsageEnvironment& env,
int socket, unsigned char* buffer, unsigned bufferSize,
struct sockaddr_storage& 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 == 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 == EHOSTUNREACH /*(Linux)*/) { // Why does Linux return this for datagram sock?
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 sockaddr_storage const& addressAndPort,
u_int8_t ttlArg,
unsigned char* buffer, unsigned bufferSize) {
// Before sending, set the socket's TTL (IPv4 only):
if (addressAndPort.ss_family == AF_INET) {
#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, addressAndPort, buffer, bufferSize);
}
Boolean writeSocket(UsageEnvironment& env,
int socket, struct sockaddr_storage const& addressAndPort,
unsigned char* buffer, unsigned bufferSize) {
do {
SOCKLEN_T dest_len = addressSize(addressAndPort);
int bytesSent = sendto(socket, (char*)buffer, bufferSize, MSG_NOSIGNAL,
(struct sockaddr const*)&addressAndPort, dest_len);
if (bytesSent != (int)bufferSize) {
char tmpBuffer[100];
snprintf(tmpBuffer, sizeof tmpBuffer, "writeSocket(%d), sendTo() error: wrote %d bytes instead of %u: ", socket, bytesSent, bufferSize);
socketErr(env, tmpBuffer);
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, int domain) {
#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,
domain == AF_INET ? IPPROTO_IP : IPPROTO_IPV6,
IP_MULTICAST_ALL, // is this the same for IPv6?
(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,
struct sockaddr_storage const& groupAddress){
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
int level, option_name;
void const* option_value;
SOCKLEN_T option_len;
struct ip_mreq imr4;
struct ipv6_mreq imr6;
switch (groupAddress.ss_family) {
case AF_INET: {
imr4.imr_multiaddr.s_addr = ((struct sockaddr_in&)groupAddress).sin_addr.s_addr;
imr4.imr_interface.s_addr = ReceivingInterfaceAddr;
level = IPPROTO_IP;
option_name = IP_ADD_MEMBERSHIP;
option_value = &imr4;
option_len = sizeof imr4;
break;
}
case AF_INET6: {
imr6.ipv6mr_multiaddr = ((struct sockaddr_in6&)groupAddress).sin6_addr;
imr6.ipv6mr_interface = 0; // ???
level = IPPROTO_IPV6;
option_name = IPV6_JOIN_GROUP;
option_value = &imr6;
option_len = sizeof imr6;
break;
}
default: {
return False;
}
}
if (setsockopt(socket, level, option_name, (const char*)option_value, option_len) < 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, groupAddress.ss_family);
return True;
}
Boolean socketLeaveGroup(UsageEnvironment&, int socket,
struct sockaddr_storage const& groupAddress) {
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
int level, option_name;
void const* option_value;
SOCKLEN_T option_len;
struct ip_mreq imr4;
struct ipv6_mreq imr6;
switch (groupAddress.ss_family) {
case AF_INET: {
imr4.imr_multiaddr.s_addr = ((struct sockaddr_in&)groupAddress).sin_addr.s_addr;
imr4.imr_interface.s_addr = ReceivingInterfaceAddr;
level = IPPROTO_IP;
option_name = IP_DROP_MEMBERSHIP;
option_value = &imr4;
option_len = sizeof imr4;
break;
}
case AF_INET6: {
imr6.ipv6mr_multiaddr = ((struct sockaddr_in6&)groupAddress).sin6_addr;
imr6.ipv6mr_interface = 0; // ???
level = IPPROTO_IPV6;
option_name = IPV6_LEAVE_GROUP;
option_value = &imr6;
option_len = sizeof imr6;
break;
}
default: {
return False;
}
}
if (setsockopt(socket, level, option_name, (const char*)option_value, option_len) < 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,
struct sockaddr_storage const& groupAddress,
struct sockaddr_storage const& sourceFilterAddr) {
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
if (groupAddress.ss_family != AF_INET) return False; // later, support IPv6
struct ip_mreq_source imr;
#if ANDROID_OLD_NDK
imr.imr_multiaddr = ((struct sockaddr_in&)groupAddress).sin_addr.s_addr;
imr.imr_sourceaddr = ((struct sockaddr_in&)sourceFilterAddr).sin_addr.s_addr;
imr.imr_interface = ReceivingInterfaceAddr;
#else
imr.imr_multiaddr.s_addr = ((struct sockaddr_in&)groupAddress).sin_addr.s_addr;
imr.imr_sourceaddr.s_addr = ((struct sockaddr_in&)sourceFilterAddr).sin_addr.s_addr;
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, groupAddress.ss_family);
return True;
}
Boolean socketLeaveGroupSSM(UsageEnvironment& /*env*/, int socket,
struct sockaddr_storage const& groupAddress,
struct sockaddr_storage const& sourceFilterAddr) {
if (!IsMulticastAddress(groupAddress)) return True; // ignore this case
if (groupAddress.ss_family != AF_INET) return False; // later, support IPv6
struct ip_mreq_source imr;
#if ANDROID_OLD_NDK
imr.imr_multiaddr = ((struct sockaddr_in&)groupAddress).sin_addr.s_addr;
imr.imr_sourceaddr = ((struct sockaddr_in&)sourceFilterAddr).sin_addr.s_addr;
imr.imr_interface = ReceivingInterfaceAddr;
#else
imr.imr_multiaddr.s_addr = ((struct sockaddr_in&)groupAddress).sin_addr.s_addr;
imr.imr_sourceaddr.s_addr = ((struct sockaddr_in&)sourceFilterAddr).sin_addr.s_addr;
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_storage testAddr;
setPortNum(testAddr, 0);
SOCKLEN_T len = sizeof testAddr;
if (getsockname(socket, (struct sockaddr*)&testAddr, &len) < 0) return False;
resultPortNum = ntohs(portNum(testAddr));
return True;
}
Boolean getSourcePort(UsageEnvironment& env, int socket, int domain, Port& port) {
portNumBits portNum = 0;
if (!getSourcePort0(socket, portNum) || portNum == 0) {
// Hack - call bind(), then try again:
if (domain == AF_INET) { // IPv4
MAKE_SOCKADDR_IN(name, INADDR_ANY, 0);
bind(socket, (struct sockaddr*)&name, sizeof name);
} else { // IPv6
in6_addr const in6addr_any_init = IN6ADDR_ANY_INIT;
MAKE_SOCKADDR_IN6(name, in6addr_any_init, 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 isBadIPv4AddressForUs(ipv4AddressBits addr) {
// Check for some possible erroneous addresses:
ipv4AddressBits nAddr = htonl(addr);
return (nAddr == 0x7F000001 /* 127.0.0.1 */
|| nAddr == 0
|| nAddr == (ipv4AddressBits)(~0));
}
static Boolean isBadIPv6AddressForUs(ipv6AddressBits addr) {
// We consider an IPv6 address bad if:
// - the first 10 bits are 0xFE8, indicating a link-local or site-local address, or
// - the first 15 bytes are 0, and the 16th byte is 0 (unspecified) or 1 (loopback)
if (addr[0] == 0xFE) return (addr[1]&0x80) != 0;
for (unsigned i = 0; i < 15; ++i) {
if (addr[i] != 0) return False;
}
return addr[15] == 0 || addr[15] == 1;
}
static Boolean isBadAddressForUs(struct sockaddr const& addr) {
switch (addr.sa_family) {
case AF_INET: {
return isBadIPv4AddressForUs(((sockaddr_in&)addr).sin_addr.s_addr);
}
case AF_INET6: {
return isBadIPv6AddressForUs(((sockaddr_in6&)addr).sin6_addr.s6_addr);
}
default: {
return True;
}
}
}
static Boolean isBadAddressForUs(NetAddress const& addr) {
if (addr.length() == sizeof (ipv4AddressBits)) {
return isBadIPv4AddressForUs(*(ipv4AddressBits*)(addr.data()));
} else if (addr.length() == sizeof (ipv6AddressBits)) {
return isBadIPv6AddressForUs(*(ipv6AddressBits*)(addr.data()));
} else {
return True;
}
}
static void getOurIPAddresses(UsageEnvironment& env); // forward
static ipv4AddressBits _ourIPv4Address = 0;
#define _weHaveAnIPv4Address (_ourIPv4Address != 0)
ipv4AddressBits ourIPv4Address(UsageEnvironment& env) {
if (ReceivingInterfaceAddr != INADDR_ANY) {
// Hack: If we were told to receive on a specific interface address, then
// define this to be our ip address:
_ourIPv4Address = ReceivingInterfaceAddr;
}
if (!_weHaveAnIPv4Address) {
getOurIPAddresses(env);
}
return _ourIPv4Address;
}
static ipv6AddressBits _ourIPv6Address;
static Boolean _weHaveAnIPv6Address = False;
ipv6AddressBits const& ourIPv6Address(UsageEnvironment& env) {
if (!_weHaveAnIPv6Address) {
getOurIPAddresses(env);
}
return _ourIPv6Address;
}
Boolean weHaveAnIPv4Address(UsageEnvironment& env) {
if (_weHaveAnIPv4Address || _weHaveAnIPv6Address) return _weHaveAnIPv4Address;
getOurIPAddresses(env);
return _weHaveAnIPv4Address;
}
Boolean weHaveAnIPv6Address(UsageEnvironment& env) {
if (_weHaveAnIPv4Address || _weHaveAnIPv6Address) return _weHaveAnIPv6Address;
getOurIPAddresses(env);
return _weHaveAnIPv6Address;
}
Boolean weHaveAnIPAddress(UsageEnvironment& env) {
if (_weHaveAnIPv4Address || _weHaveAnIPv6Address) return True;
getOurIPAddresses(env);
return _weHaveAnIPv4Address || _weHaveAnIPv6Address;
}
static void copyAddress(struct sockaddr_storage& to, struct sockaddr const* from) {
// Copy a "struct sockaddr" to a "struct sockaddr_storage" (assumed to be large enough)
if (from == NULL) return;
switch (from->sa_family) {
case AF_INET: {
#ifdef HAVE_SOCKADDR_LEN
if (from->sa_len < sizeof (struct sockaddr_in)) return; // sanity check
to.ss_len = sizeof (struct sockaddr_in);
#endif
to.ss_family = AF_INET;
((sockaddr_in&)to).sin_addr.s_addr = ((sockaddr_in const*)from)->sin_addr.s_addr;
break;
}
case AF_INET6: {
#ifdef HAVE_SOCKADDR_LEN
if (from->sa_len < sizeof (struct sockaddr_in6)) return; // sanity check
to.ss_len = sizeof (struct sockaddr_in6);
#endif
to.ss_family = AF_INET6;
for (unsigned i = 0; i < 16; ++i) {
((sockaddr_in6&)to).sin6_addr.s6_addr[i] = ((sockaddr_in6 const*)from)->sin6_addr.s6_addr[i];
}
break;
}
}
}
void getOurIPAddresses(UsageEnvironment& env) {
// We use two methods to (try to) get our IP addresses.
// First, we use "getifaddrs()". But if that doesn't work
// (or if "getifaddrs()" is not defined), then we use an alternative (more old-fashioned)
// mechanism: First get our host name, then try resolving this host name.
struct sockaddr_storage foundIPv4Address = nullAddress(AF_INET);
struct sockaddr_storage foundIPv6Address = nullAddress(AF_INET6);
Boolean getifaddrsWorks = False; // until we learn otherwise
#ifndef NO_GETIFADDRS
struct ifaddrs* ifap;
if (getifaddrs(&ifap) == 0) {
// Look through all interfaces:
for (struct ifaddrs* p = ifap; p != NULL; p = p->ifa_next) {
// Ignore an interface if it's not up, or is a loopback interface:
if ((p->ifa_flags&IFF_UP) == 0 || (p->ifa_flags&IFF_LOOPBACK) != 0) continue;
// Also ignore the interface if the address is considered 'bad' for us:
if (p->ifa_addr == NULL || isBadAddressForUs(*p->ifa_addr)) continue;
// We take the first IPv4 and first IPv6 addresses:
if (p->ifa_addr->sa_family == AF_INET && addressIsNull(foundIPv4Address)) {
copyAddress(foundIPv4Address, p->ifa_addr);
getifaddrsWorks = True;
} else if (p->ifa_addr->sa_family == AF_INET6 && addressIsNull(foundIPv6Address)) {
copyAddress(foundIPv6Address, p->ifa_addr);
getifaddrsWorks = True;
}
}
freeifaddrs(ifap);
}
#endif
if (!getifaddrsWorks) do {
// We couldn't find our address using "getifaddrs()",
// 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 one or more IP addresses:
NetAddressList addresses(hostname);
NetAddressList::Iterator iter(addresses);
// Look at each address, rejecting any that are bad.
// We take the first IPv4 and first IPv6 addresses, if any.
NetAddress const* address;
while ((address = iter.nextAddress()) != NULL) {
if (isBadAddressForUs(*address)) continue;
if (address->length() == sizeof (ipv4AddressBits) && addressIsNull(foundIPv4Address)) {
copyAddress(foundIPv4Address, address);
} else if (address->length() == sizeof (ipv6AddressBits) && addressIsNull(foundIPv6Address)) {
copyAddress(foundIPv6Address, address);
}
}
} while (0);
// Note the IPv4 and IPv6 addresses that we found:
_ourIPv4Address = ((sockaddr_in&)foundIPv4Address).sin_addr.s_addr;
for (unsigned i = 0; i < 16; ++i) {
_ourIPv6Address[i] = ((sockaddr_in6&)foundIPv6Address).sin6_addr.s6_addr[i];
if (_ourIPv6Address[i] != 0) _weHaveAnIPv6Address = True;
}
if (!_weHaveAnIPv4Address && !_weHaveAnIPv6Address) {
env.setResultMsg("This computer does not have a valid IP (v4 or v6) address!");
}
// Use our newly-discovered IP addresses, and the current time,
// to initialize the random number generator's seed:
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
unsigned seed = _ourIPv4Address^timeNow.tv_sec^timeNow.tv_usec;
for (unsigned i = 0; i < 16; i += 4) {
seed ^= (_ourIPv6Address[i]<<24)|(_ourIPv6Address[i+1]<<16)|(_ourIPv6Address[i+2]<<8)|_ourIPv6Address[i+3];
}
our_srandom(seed);
}
ipv4AddressBits chooseRandomIPv4SSMAddress(UsageEnvironment& env) {
// First, a hack to ensure that our random number generator is seeded:
(void)ourIPv4Address(env);
// Choose a random address in the range [232.0.1.0, 232.255.255.255)
// i.e., [0xE8000100, 0xE8FFFFFF)
ipv4AddressBits const first = 0xE8000100, lastPlus1 = 0xE8FFFFFF;
ipv4AddressBits const range = lastPlus1 - first;
return ntohl(first + ((ipv4AddressBits)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) {
snprintf(timeString, sizeof 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];
snprintf(timeString, sizeof 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
#undef ANDROID_OLD_NDK
live/groupsock/include/ 000755 001752 001752 00000000000 15270745314 016604 5 ustar 00ec2-user ec2-user 000000 000000 live/groupsock/inet.c 000444 001752 001752 00000034126 15270745314 016270 0 ustar 00ec2-user ec2-user 000000 000000 #ifndef _NET_COMMON_H
#include "NetCommon.h"
#endif
#include
#ifdef VXWORKS
#include
#endif
#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();
}
}
#if 0
/*
* 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);
}
#endif
/*
* 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 likelihood 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 00000003563 15270745314 017662 0 ustar 00ec2-user ec2-user 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"
// Copyright (c) 1996-2026 Live Networks, Inc. All rights reserved.
// IO event handlers
// Implementation
#include "IOHandlers.hh"
//##### TEMP: Use a single buffer.
//##### This assumes that the I/O handlers are non-reentrant
static unsigned const maxPacketLength = 0x10000; // 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 undergo 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;
static unsigned char ioBuffer[ioBufferSize];
void socketReadHandler(Socket* sock, int /*mask*/) {
unsigned bytesRead;
struct sockaddr_storage dummy; // not used
UsageEnvironment& saveEnv = sock->env();
// because handleRead(), if it fails, may delete "sock"
if (!sock->handleRead(ioBuffer, ioBufferSize, bytesRead, dummy)) {
saveEnv.reportBackgroundError();
}
}
live/groupsock/Makefile.head 000440 001752 001752 00000000217 15270745314 017513 0 ustar 00ec2-user ec2-user 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 00000003327 15270745314 017554 0 ustar 00ec2-user ec2-user 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/Groupsock.hh: include/groupsock_version.hh include/NetInterface.hh include/GroupEId.hh
include/NetInterface.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
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: install1
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 00000036540 15270745314 017727 0 ustar 00ec2-user ec2-user 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"
// Copyright (c) 1996-2026 Live Networks, Inc. All rights reserved.
// Network Addresses
// Implementation
#include "NetAddress.hh"
#include "GroupsockHelper.hh"
#include
#include
#ifndef INADDR_NONE
#define INADDR_NONE 0xFFFFFFFF
#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;
}
static struct sockaddr_storage _nullIPv4Address, _nullIPv6Address;
struct sockaddr_storage const& nullAddress(int addressFamily) {
switch (addressFamily) {
case AF_INET: {
_nullIPv4Address.ss_family = AF_INET;
((sockaddr_in&)_nullIPv4Address).sin_addr.s_addr = 0;
return _nullIPv4Address;
}
default: { // assume AF_INET6
_nullIPv6Address.ss_family = AF_INET6;
for (unsigned i = 0; i < 16; ++i) {
((sockaddr_in6&)_nullIPv6Address).sin6_addr.s6_addr[i] = 0;
}
return _nullIPv6Address;
}
}
}
Boolean addressIsNull(sockaddr_storage const& address) {
switch (address.ss_family) {
case AF_INET: {
return ((sockaddr_in const&)address).sin_addr.s_addr == 0;
}
case AF_INET6: {
for (unsigned i = 0; i < 16; ++i) {
if (((sockaddr_in6 const&)address).sin6_addr.s6_addr[i] != 0) return False;
}
return True;
}
default: {
return False;
}
}
}
SOCKLEN_T addressSize(sockaddr_storage const& address) {
switch (address.ss_family) {
case AF_INET: {
return sizeof (struct sockaddr_in);
}
case AF_INET6: {
return sizeof (struct sockaddr_in6);
}
default: {
return 0;
}
}
}
void copyAddress(struct sockaddr_storage& to, NetAddress const* from) {
if (from == NULL) return;
if (from->length() == sizeof (ipv4AddressBits)) {
#ifdef HAVE_SOCKADDR_LEN
to.ss_len = sizeof (struct sockaddr_in);
#endif
to.ss_family = AF_INET;
((sockaddr_in&)to).sin_addr.s_addr = *(ipv4AddressBits*)(from->data());
} else { // IPv6
#ifdef HAVE_SOCKADDR_LEN
to.ss_len = sizeof (struct sockaddr_in6);
#endif
to.ss_family = AF_INET6;
for (unsigned i = 0; i < 16; ++i) {
((sockaddr_in6&)to).sin6_addr.s6_addr[i] = (from->data())[i];
}
((sockaddr_in6&)to).sin6_scope_id = 0; // instead, somehow look this up from the address?
}
}
Boolean operator==(struct sockaddr_storage const& left, struct sockaddr_storage const& right) {
if (left.ss_family != right.ss_family) return False;
switch (left.ss_family) {
case AF_INET: {
return ((struct sockaddr_in const&)left).sin_addr.s_addr == ((struct sockaddr_in const&)right).sin_addr.s_addr;
}
case AF_INET6: {
return ((struct sockaddr_in6 const&)left).sin6_addr.s6_addr == ((struct sockaddr_in6 const&)right).sin6_addr.s6_addr;
}
default: {
return False;
}
}
}
////////// NetAddressList //////////
NetAddressList::NetAddressList(char const* hostname, int addressFamily)
: fNumAddresses(0), fAddressArray(NULL) {
if (hostname == NULL) return;
// First, check whether "hostname" is an IP address literal (check IPv4, then IPv6).
// If so, return a 1-element list with this address:
ipv4AddressBits addr4;
if (addressFamily != AF_INET6 && inet_pton(AF_INET, hostname, (u_int8_t*)&addr4) == 1) {
fNumAddresses = 1;
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
fAddressArray[0] = new NetAddress((u_int8_t*)&addr4, sizeof addr4);
return;
}
ipv6AddressBits addr6;
if (addressFamily != AF_INET && inet_pton(AF_INET6, hostname, (u_int8_t*)&addr6) == 1) {
fNumAddresses = 1;
fAddressArray = new NetAddress*[fNumAddresses];
if (fAddressArray == NULL) return;
fAddressArray[0] = new NetAddress((u_int8_t*)&addr6, sizeof addr6);
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_flags = AI_ADDRCONFIG; // We only care about addresses that we can handle
struct addrinfo* addrinfoResultPtr = NULL;
int result = -1;
if (addressFamily != AF_INET6) {
// First, look up an IPv4 address for the name
addrinfoHints.ai_family = AF_INET; // First, look up an IPv4 address for the name
result = getaddrinfo(hostname, NULL, &addrinfoHints, &addrinfoResultPtr);
}
if (addressFamily != AF_INET && (result != 0 || addrinfoResultPtr == NULL)) {
// Try again, looking up an IPv6 address for the name instead:
addrinfoHints.ai_family = AF_INET6;
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_family != AF_INET && p->ai_family != AF_INET6) continue; // unsupported address
if (addressFamily != AF_UNSPEC && p->ai_family != addressFamily) continue; // unwanted
// Sanity check: Also check the address length:
if (p->ai_family == AF_INET && p->ai_addrlen < sizeof (struct sockaddr_in)) continue;
if (p->ai_family == AF_INET6 && p->ai_addrlen < sizeof (struct sockaddr_in6)) continue;
++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) {
// Same sanity checks as before:
if (p->ai_family != AF_INET && p->ai_family != AF_INET6) continue; // unsupported address
if (addressFamily != AF_UNSPEC && p->ai_family != addressFamily) continue; // unwanted
if (p->ai_family == AF_INET && p->ai_addrlen < sizeof (struct sockaddr_in)) continue;
if (p->ai_family == AF_INET6 && p->ai_addrlen < sizeof (struct sockaddr_in6)) continue;
if (p->ai_family == AF_INET) { // IPv4
fAddressArray[i++] = new NetAddress((u_int8_t const*)&(((struct sockaddr_in*)p->ai_addr)->sin_addr.s_addr), sizeof (ipv4AddressBits));
} else { // IPv6
fAddressArray[i++] = new NetAddress((u_int8_t const*)&(((struct sockaddr_in6*)p->ai_addr)->sin6_addr.s6_addr), sizeof (ipv6AddressBits));
}
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 //////////
#define NUM_RECORDS_IN_KEY_FOR_EACH_ADDRESS (sizeof (struct in6_addr)/sizeof (int))
#define NUM_RECORDS_IN_KEY_TOTAL (2*NUM_RECORDS_IN_KEY_FOR_EACH_ADDRESS + 1)
AddressPortLookupTable::AddressPortLookupTable()
: fTable(HashTable::create(NUM_RECORDS_IN_KEY_TOTAL)) {
}
AddressPortLookupTable::~AddressPortLookupTable() {
delete fTable;
}
static void setKeyFromAddress(int*& key, struct sockaddr_storage const& address) {
if (address.ss_family == AF_INET) {
// For an IPv4 address, set the upper 3 words of the key to zero, with the address last:
*key++ = 0;
*key++ = 0;
*key++ = 0;
*key++ = ((sockaddr_in const&)address).sin_addr.s_addr;
} else {
// Assume that the address is IPv6; use all 128 bits (4 words) for the key:
struct sockaddr_in6 const& address6 = (struct sockaddr_in6&)address;
u_int8_t const* s6a = address6.sin6_addr.s6_addr; // alias
*key++ = (s6a[0]<<24)|(s6a[1]<<16)|(s6a[2]<<8)|s6a[3];
*key++ = (s6a[4]<<24)|(s6a[5]<<16)|(s6a[6]<<8)|s6a[7];
*key++ = (s6a[8]<<24)|(s6a[9]<<16)|(s6a[10]<<8)|s6a[11];
*key++ = (s6a[12]<<24)|(s6a[13]<<16)|(s6a[14]<<8)|s6a[15];
}
}
static void setKey(int* key,
struct sockaddr_storage const& address1,
struct sockaddr_storage const& address2,
Port port) {
setKeyFromAddress(key, address1);
setKeyFromAddress(key, address2);
*key = (int)port.num();
}
void* AddressPortLookupTable::Add(struct sockaddr_storage const& address1,
struct sockaddr_storage const& address2,
Port port, void* value) {
int key[NUM_RECORDS_IN_KEY_TOTAL];
setKey(key, address1, address2, port);
return fTable->Add((char*)key, value);
}
void* AddressPortLookupTable::Lookup(struct sockaddr_storage const& address1,
struct sockaddr_storage const& address2,
Port port) {
int key[NUM_RECORDS_IN_KEY_TOTAL];
setKey(key, address1, address2, port);
return fTable->Lookup((char*)key);
}
Boolean AddressPortLookupTable::Remove(struct sockaddr_storage const& address1,
struct sockaddr_storage const& address2,
Port port) {
int key[NUM_RECORDS_IN_KEY_TOTAL];
setKey(key, address1, address2, port);
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(struct sockaddr_storage const& address) {
switch (address.ss_family) {
case AF_INET: {
ipv4AddressBits addressInNetworkOrder
= htonl(((sockaddr_in const&)address).sin_addr.s_addr);
// Note: We return False for addresses in the range 224.0.0.0
// through 224.0.0.255, because these are non-routable
return addressInNetworkOrder > 0xE00000FF &&
addressInNetworkOrder <= 0xEFFFFFFF;
}
case AF_INET6: {
// An IPv6 address is multicast if the first byte is 0xFF:
return ((sockaddr_in6 const&)address).sin6_addr.s6_addr[0] == 0xFF;
}
default: {
return False;
}
}
}
////////// 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(ipv4AddressBits const& addr) {
init(addr);
}
AddressString::AddressString(struct sockaddr_in6 const& addr) {
init(addr.sin6_addr.s6_addr);
}
AddressString::AddressString(struct in6_addr const& addr) {
init(addr.s6_addr);
}
AddressString::AddressString(ipv6AddressBits const& addr) {
init(addr);
}
AddressString::AddressString(struct sockaddr_storage const& addr) {
switch (addr.ss_family) {
case AF_INET: {
init(((sockaddr_in&)addr).sin_addr.s_addr);
break;
}
case AF_INET6: {
init(((sockaddr_in6&)addr).sin6_addr.s6_addr);
break;
}
default: {
size_t const msgMaxSize = 200; // more than enough for this error message
fVal = new char[msgMaxSize]; // more than enough for this error message
snprintf(fVal, msgMaxSize, "(unknown address family %d)", addr.ss_family);
break;
}
}
}
void AddressString::init(ipv4AddressBits const& addr) {
fVal = new char[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr, fVal, INET_ADDRSTRLEN);
}
void AddressString::init(ipv6AddressBits const& addr) {
fVal = new char[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &addr, fVal, INET6_ADDRSTRLEN);
}
AddressString::~AddressString() {
delete[] fVal;
}
portNumBits portNum(struct sockaddr_storage const& address) {
switch (address.ss_family) {
case AF_INET: {
return ((sockaddr_in&)address).sin_port;
}
case AF_INET6: {
return ((sockaddr_in6&)address).sin6_port;
}
default: {
return 0;
}
}
}
void setPortNum(struct sockaddr_storage& address, portNumBits portNum/*in network order*/) {
((sockaddr_in&)address).sin_port = portNum; // position will be the same for "sockaddr_in6"
}
live/groupsock/NetInterface.cpp 000444 001752 001752 00000007143 15270745314 020237 0 ustar 00ec2-user ec2-user 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"
// Copyright (c) 1996-2026 Live Networks, Inc. All rights reserved.
// Network Interfaces
// Implementation
#include "NetInterface.hh"
#include "GroupsockHelper.hh"
#ifndef NO_SSTREAM
#include