diff --git a/.github/scripts/Windows/install_spout.sh b/.github/scripts/Windows/install_spout.sh new file mode 100755 index 000000000..dff5bc204 --- /dev/null +++ b/.github/scripts/Windows/install_spout.sh @@ -0,0 +1,15 @@ +#!/bin/sh -eux +# Install SPOUT + +git clone --depth 1 https://github.com/leadedge/Spout2.git +mkdir Spout2/SpoutSDK/Source/build +cd Spout2/SpoutSDK/Source/build +cmake -DBUILD_SHARED_LIBS=ON -G 'MSYS Makefiles' .. +cmake --build . +cp libSpout.dll /usr/local/bin +cp libSpout.dll.a /usr/local/lib +cd - +mkdir /usr/local/include/SpoutSDK +cp Spout2/SpoutSDK/Source/*.h /usr/local/include/SpoutSDK +rm -rf Spout2 + diff --git a/.github/scripts/Windows/prepare_msys.sh b/.github/scripts/Windows/prepare_msys.sh index 8272833d0..16be551b2 100644 --- a/.github/scripts/Windows/prepare_msys.sh +++ b/.github/scripts/Windows/prepare_msys.sh @@ -6,7 +6,7 @@ mkdir -p /usr/local/lib /usr/local/bin /usr/local/include cat >> ~/.bash_profile <<'EOF' export PATH=/mingw64/bin:/usr/local/bin:$PATH export CPATH=/usr/local/include -export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/mingw64/lib/pkgconfig +export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/mingw64/lib/pkgconfig export LIBRARY_PATH=/usr/local/lib CUDA_D=$(ls -d /c/Program\ Files/NVIDIA\ GPU\ Computing\ Toolkit/CUDA/*) @@ -27,7 +27,10 @@ if test -d "$JACK_D"; then export LIBRARY_PATH=$LIBRARY_PATH:$JACK_D/lib fi +unset temp tmp # defined by /etc/profile, causes CineForm MSBuild fail (GitHub issue #99) + cd `cygpath $GITHUB_WORKSPACE` + EOF . ~/.bash_profile @@ -61,11 +64,7 @@ cd /c/live555 make install cd - -# Install SPOUT -wget --no-verbose https://frakira.fi.muni.cz/~xpulec/SpoutSDK.zip # this is the SDK subdirectory installed by Spout installer -unzip SpoutSDK.zip -d src -MSBuild.exe -p:PlatformToolset=v142 -p:Configuration=Release -p:Platform=x64 src/SpoutSDK/VS2012 -data/scripts/build_spout64.sh src/SpoutSDK/VS2012/x64/Release +$GITHUB_WORKSPACE/.github/scripts/Windows/install_spout.sh # Install FFMPEG wget --no-verbose https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.7z && 7z x ffmpeg-release-full-shared.7z && cp -r ffmpeg-*build-shared/{bin,lib,include} /usr/local && rm -rf ffmpeg-* || exit 1 @@ -74,5 +73,5 @@ wget --no-verbose https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared. ( wget --no-verbose https://github.com/CESNET/GPUJPEG/releases/download/continuous/GPUJPEG.zip && unzip GPUJPEG.zip && cp -r GPUJPEG/* /usr/local || exit 1 ) # Build CineForm -( git submodule update --init cineform-sdk && cd cineform-sdk && cmake -DBUILD_STATIC=false -DBUILD_TOOLS=false -A x64 && MSBuild.exe CineFormSDK.sln -property:Configuration=Release && cp Release/CFHDCodec.dll /usr/local/bin && cp Release/CFHDCodec.lib /usr/local/lib && cp Common/* /usr/local/include && cp libcineformsdk.pc /usr/local/lib/pkgconfig || exit 1 ) +( git submodule update --init cineform-sdk && cd cineform-sdk && cmake -DBUILD_STATIC=false -DBUILD_TOOLS=false -A x64 . && MSBuild.exe CineFormSDK.sln -property:Configuration=Release && cp Release/CFHDCodec.dll /usr/local/bin && cp Release/CFHDCodec.lib /usr/local/lib && cp Common/* /usr/local/include && cp libcineformsdk.pc /usr/local/lib/pkgconfig || exit 1 ) diff --git a/COPYRIGHT b/COPYRIGHT index 1148178a2..d19de2864 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,5 +1,5 @@ - UltraGrid - A High Definition Collaboratory +=========================================== Copyright (c) 2005-2018 CESNET z.s.p.o. Copyright (c) 2013-2014 Fundació i2CAT, Internet I Innovació Digital a Catalunya @@ -60,3 +60,71 @@ UltraGrid - A High Definition Collaboratory for use in the OpenSSL Toolkit. (http://www.openssl.org/) This product includes EmbeddableWebServer created by Forrest Heller. +External libraries +------------------ + +### libnatpmp + +Copyright (c) 2007-2011, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +### Speex + +Copyright 2002-2008 Xiph.org Foundation +Copyright 2002-2008 Jean-Marc Valin +Copyright 2005-2007 Analog Devices Inc. +Copyright 2005-2008 Commonwealth Scientific and Industrial Research + Organisation (CSIRO) +Copyright 1993, 2002, 2006 David Rowe +Copyright 2003 EpicGames +Copyright 1992-1994 Jutta Degener, Carsten Bormann + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile.in b/Makefile.in index 97241521f..ab6bcc6fe 100644 --- a/Makefile.in +++ b/Makefile.in @@ -139,6 +139,7 @@ COMMON_OBJS = \ src/utils/jpeg_reader.o \ src/utils/list.o \ src/utils/misc.o \ + src/utils/nat.o \ src/utils/net.o \ src/utils/packet_counter.o \ src/utils/resource_manager.o \ @@ -195,6 +196,7 @@ COMMON_OBJS = \ ldgm/matrix-gen/matrix-generator.o \ ldgm/matrix-gen/ldpc-matrix.o \ rs/fec.o \ + ext-deps/libnatpmp-20150609/libnatpmp.a \ OBJS = @OBJS@ \ $(COMMON_OBJS) @@ -269,6 +271,17 @@ lib/libug$(DLEXT): src/dir-stamp $(OBJS) $(GENERATED_HEADERS) src/libug.o %.cu.lib: %.cu $(ALL_INCLUDES) $(MKDIR_P) $(dir $@) "$(CUDA_COMPILER)" $(CUDA_FLAGS) -DEXPORT_DLL_SYMBOLS $(INC) --shared $< -o $<.dll + touch $@ + +ext-deps/libnatpmp-20150609/libnatpmp.a: + $(MKDIR_P) $(dir $@) +ifeq ($(SYSTEM),Windows) + cd $(srcdir)/ext-deps/libnatpmp-20150609 && cmd /c build.bat + mv $(srcdir)/ext-deps/libnatpmp-20150609/natpmp.a $(dir $@)/libnatpmp.a +else + make -C $(srcdir)/ext-deps/libnatpmp-20150609 libnatpmp.a + [ -f $@ ] || mv $(srcdir)/ext-deps/libnatpmp-20150609/libnatpmp.a $(dir $@) +endif src/libavcodec_common.o: src/libavcodec_common.c $(ALL_INCLUDES) $(MKDIR_P) $(dir $@) @@ -673,7 +686,6 @@ install: all $(INSTALL) -d -m 755 $(DESTDIR)$(docdir) $(INSTALL) -m 644 $(DOCS) $(DESTDIR)$(docdir) $(INSTALL) -m 644 $(srcdir)/CONTRIBUTING.md $(srcdir)/COPYRIGHT $(srcdir)/INSTALL $(srcdir)/NEWS $(srcdir)/README.md $(DESTDIR)$(docdir) - $(CP) $(srcdir)/speex-1.2rc1/COPYING $(DESTDIR)$(docdir)/COPYING.speex $(INSTALL) -m 755 $(srcdir)/data/ultragrid-bugreport-collect.sh $(DESTDIR)$(docdir) $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) $(INSTALL) -m 644 $(srcdir)/data/uv.1 $(srcdir)/data/hd-rum-transcode.1 $(DESTDIR)$(man1dir) @@ -685,7 +697,6 @@ uninstall: if [ -n "@MODULES@" ]; then for n in @MODULES@; do $(RM) $(DESTDIR)$(libdir)/ultragrid/`basename $$n`; done; fi for n in $(DOCS); do $(RM) $(DESTDIR)$(docdir)$$n; done; $(RM) $(DESTDIR)$(docdir)/CONTRIBUTING.md $(DESTDIR)$(docdir)/COPYRIGHT $(DESTDIR)$(docdir)/INSTALL $(DESTDIR)$(docdir)/NEWS $(DESTDIR)$(docdir)/README.md - $(RM) $(DESTDIR)$(docdir)/COPYING.speex $(RM) $(DESTDIR)$(docdir)/ultragrid-bugreport-collect.sh if [ -f "$(GUI_TARGET)" ]; then \ $(RM) $(DESTDIR)$(bindir)/`basename $(GUI_TARGET)`;\ @@ -693,3 +704,4 @@ uninstall: $(RM) $(DESTDIR)$(datadir)/pixmaps/ultragrid.png;\ fi +# vim: set noexpandtab diff --git a/configure.ac b/configure.ac index 77e80df51..1076885cf 100644 --- a/configure.ac +++ b/configure.ac @@ -2201,9 +2201,16 @@ AC_ARG_ENABLE(gpujpeg, [gpujpeg_req=$build_default]) -PKG_CHECK_MODULES([LIBGPUJPEG], [libgpujpeg >= 0.0.2 ], [found_gpujpeg=yes], [found_gpujpeg=no]) +PKG_CHECK_MODULES([LIBGPUJPEG_ANY], [ libgpujpeg ], [ found_gpujpeg_any=yes ], [ found_gpujpeg_any=no ]) +PKG_CHECK_MODULES([LIBGPUJPEG], [ libgpujpeg >= 0.11 ], [ found_gpujpeg=yes ], [ found_gpujpeg=no ]) -if test "$found_gpujpeg" != yes +if test "$found_gpujpeg_any" = yes -a "$found_gpujpeg" = no; then + AC_MSG_WARN([Old GPUJPEG version found, please upgrade]); +fi + +# try to find without pkg-config (but only if old version was not detected - to avoid side-loading +# that version without pkg-config version check) +if test "$found_gpujpeg_any" != yes -a "$found_gpujpeg" != yes then GPUJPEG_LIB="$GPUJPEG_LIB -lgpujpeg" SAVED_LIBS=$LIBS @@ -2896,17 +2903,24 @@ AC_ARG_ENABLE(spout, [spout_req=$build_default] ) -FOUND_SPOUT_WRAPPER=no +FOUND_SPOUT=no if test $system = Windows then - AC_CHECK_LIB(spout_wrapper, spout_create_receiver, [FOUND_SPOUT_WRAPPER=yes]) + AC_LANG_PUSH([C++]) + SAVED_LIBS="$LIBS" + LIBS="$LIBS -lSpout" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[SpoutSender *sender = new SpoutSender;]])], + FOUND_SPOUT=yes, FOUND_SPOUT=no) + LIBS=$SAVED_LIBS + AC_LANG_POP([C++]) fi -if test $spout_req != no -a "$OPENGL" = yes -a $FOUND_SPOUT_WRAPPER = yes +if test $spout_req != no -a "$OPENGL" = yes -a $FOUND_SPOUT = yes then AC_DEFINE([HAVE_SPOUT], [1], [Build with Spout support]) - LIBS="$LIBS -lspout_wrapper" - OBJS="$OBJS src/video_capture/spout.o" + LIBS="$LIBS -lSpout" + OBJS="$OBJS src/spout_sender.o src/video_capture/spout.o" spout=yes fi diff --git a/data/README.md b/data/README.md new file mode 100644 index 000000000..5f4f34bf5 --- /dev/null +++ b/data/README.md @@ -0,0 +1 @@ +screen-capture-recorder-x64.dll is taken from https://sourceforge.net/projects/screencapturer/ diff --git a/data/screen-capture-recorder-x64.dll b/data/screen-capture-recorder-x64.dll old mode 100755 new mode 100644 diff --git a/data/scripts/build_spout64.sh b/data/scripts/build_spout64.sh deleted file mode 100755 index 00f0b3f26..000000000 --- a/data/scripts/build_spout64.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -eu - -####################################### -# USAGE -# -# 1) Copy SpoutSDK to src -# 2) run build_spout.sh (this script) -####################################### - -function run_in_vs_env -{ - eval vssetup=\$$1'\\..\\..\\VC\\bin\\amd64\\vcvars64.bat' - cmd //Q //C call "$vssetup" "&&" "${@:2}" -} - -function run_vs16 -{ - eval vssetup='C:\\Program\ Files\ \(x86\)\\Microsoft\ Visual\ Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat' - cmd //Q //C call "$vssetup" "&&" "$@" -} - -function run_vs12 -{ - run_in_vs_env VS120COMNTOOLS "$@" -} - - -function run_vs11 -{ - run_in_vs_env VS110COMNTOOLS "$@" -} - -function run_vs10 -{ - run_in_vs_env VS100COMNTOOLS "$@" -} - -LIBDIR=${1:-src/SpoutSDK/Binaries/x64} -MSVS_PATH=`/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/Installer/vswhere.exe -latest -property installationPath` - -eval vssetup=\"$MSVS_PATH\"'\\VC\\Auxiliary\\Build\\vcvars64.bat' -cmd //Q //C call "$vssetup" "&&" cl //DEXPORT_DLL_SYMBOLS src/spout_sender.cpp src/spout_receiver.cpp //LD $LIBDIR/Spout.lib //Fespout_wrapper - -cp spout_wrapper.dll /usr/local/bin -cp spout_wrapper.lib /usr/local/lib -cp $LIBDIR/Spout.dll /usr/local/bin - diff --git a/ext-deps/libnatpmp-20150609/.gitignore b/ext-deps/libnatpmp-20150609/.gitignore new file mode 100644 index 000000000..f63281e14 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/.gitignore @@ -0,0 +1,7 @@ +!* +*.a +*.o +*.so +natpmpc-shared +natpmpc-static +testgetgateway diff --git a/ext-deps/libnatpmp-20150609/Changelog.txt b/ext-deps/libnatpmp-20150609/Changelog.txt new file mode 100644 index 000000000..be75a0b6d --- /dev/null +++ b/ext-deps/libnatpmp-20150609/Changelog.txt @@ -0,0 +1,98 @@ +$Id: Changelog.txt,v 1.33 2013/11/26 08:47:36 nanard Exp $ + +2013/11/26: + enforce strict aliasing rules. + +2013/09/10: + small patch for MSVC >= 16 + rename win32 implementation of gettimeofday() to natpmp_gettimeofday() + +2012/08/21: + Little change in Makefile + removed warnings in testgetgateway.c + Fixed bugs in command line argumentparsing in natpmpc.c + +2011/08/07: + Patch to build on debian/kFreeBSD. + +2011/07/15: + Put 3 clauses BSD licence at the top of source files. + +2011/06/18: + --no-undefined => -Wl,--no-undefined + adding a natpmpc.1 man page + +2011/05/19: + Small fix in libnatpmpmodule.c thanks to Manuel Mausz + +2011/01/03: + Added an argument to initnatpmp() in order to force the gateway to be used + +2011/01/01: + fix in make install + +2010/05/21: + make install now working under MacOSX (and BSD) + +2010/04/12: + cplusplus stuff in natpmp.h + +2010/02/02: + Fixed compilation under Mac OS X + +2009/12/19: + improve and fix building under Windows. + Project files for MS Visual Studio 2008 + More simple (and working) code for Win32. + More checks in the /proc/net/route parsing. Add some comments. + +2009/08/04: + improving getgateway.c for windows + +2009/07/13: + Adding Haiku code in getgateway.c + +2009/06/04: + Adding Python module thanks to David Wu + +2009/03/10: + Trying to have windows get gateway working if not using DHCP + +2009/02/27: + dont include declspec.h if not under WIN32. + +2009/01/23: + Prefixed the libraries name with lib + +2008/10/06: + Fixed a memory leak in getdefaultgateway() (USE_SYSCTL_NET_ROUTE) + +2008/07/03: + Adding WIN32 code from Robbie Hanson + +2008/06/30: + added a Solaris implementation for getgateway(). + added a LICENCE file to the distribution + +2008/05/29: + Anonymous unions are forbidden in ANSI C. That was causing problems with + non-GCC compilers. + +2008/04/28: + introduced strnatpmperr() + improved natpmpc.c sample + make install now install the binary + +2007/12/13: + Fixed getgateway.c for working under OS X ;) + Fixed values for NATPMP_PROTOCOL_TCP and NATPMP_PROTOCOL_UDP + +2007/12/11: + Fixed getgateway.c for compilation under Mac OS X + +2007/12/01: + added some comments in .h + +2007/11/30: + implemented almost everything + diff --git a/ext-deps/libnatpmp-20150609/JavaTest.java b/ext-deps/libnatpmp-20150609/JavaTest.java new file mode 100644 index 000000000..0379c1821 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/JavaTest.java @@ -0,0 +1,42 @@ +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; + +import fr.free.miniupnp.libnatpmp.NatPmp; +import fr.free.miniupnp.libnatpmp.NatPmpResponse; + +class JavaTest { + public static void main(String[] args) { + NatPmp natpmp = new NatPmp(); + + natpmp.sendPublicAddressRequest(); + NatPmpResponse response = new NatPmpResponse(); + + int result = -1; + do{ + result = natpmp.readNatPmpResponseOrRetry(response); + try { + Thread.sleep(4000); + } catch (InterruptedException e) { + //fallthrough + } + } while (result != 0); + + byte[] bytes = intToByteArray(response.addr); + + try { + InetAddress inetAddress = InetAddress.getByAddress(bytes); + System.out.println("Public address is " + inetAddress); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } + + public static final byte[] intToByteArray(int value) { + return new byte[] { + (byte)value, + (byte)(value >>> 8), + (byte)(value >>> 16), + (byte)(value >>> 24)}; + } +} diff --git a/ext-deps/libnatpmp-20150609/LICENSE b/ext-deps/libnatpmp-20150609/LICENSE new file mode 100644 index 000000000..7fff2c26a --- /dev/null +++ b/ext-deps/libnatpmp-20150609/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2007-2011, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/ext-deps/libnatpmp-20150609/Makefile b/ext-deps/libnatpmp-20150609/Makefile new file mode 100644 index 000000000..b67b3e85c --- /dev/null +++ b/ext-deps/libnatpmp-20150609/Makefile @@ -0,0 +1,177 @@ +# $Id: Makefile,v 1.23 2013/11/26 16:38:15 nanard Exp $ +# This Makefile is designed for use with GNU make +# libnatpmp +# (c) 2007-2013 Thomas Bernard +# http://miniupnp.free.fr/libnatpmp.html + +OS = $(shell uname -s) +CC = gcc +INSTALL = install -p +ARCH = $(shell uname -m | sed -e s/i.86/i686/) +VERSION = $(shell cat VERSION) + +ifeq ($(OS), Darwin) +JARSUFFIX=mac +endif +ifeq ($(OS), Linux) +JARSUFFIX=linux +endif +ifneq (,$(findstring WIN,$(OS))) +JARSUFFIX=win32 +endif + +# APIVERSION is used in soname +APIVERSION = 1 +#LDFLAGS = -Wl,--no-undefined +CFLAGS ?= -Os +#CFLAGS = -g -O0 +CFLAGS += -fPIC +CFLAGS += -Wall +#CFLAGS += -Wextra +CFLAGS += -DENABLE_STRNATPMPERR +#CFLAGS += -Wstrict-aliasing + +LIBOBJS = natpmp.o getgateway.o + +OBJS = $(LIBOBJS) testgetgateway.o natpmpc.o natpmp-jni.o + +STATICLIB = libnatpmp.a +ifeq ($(OS), Darwin) + SHAREDLIB = libnatpmp.dylib + JNISHAREDLIB = libjninatpmp.dylib + SONAME = $(basename $(SHAREDLIB)).$(APIVERSION).dylib + CFLAGS := -DMACOSX -D_DARWIN_C_SOURCE $(CFLAGS) + SONAMEFLAGS=-Wl,-install_name,$(JNISHAREDLIB) -dynamiclib -framework JavaVM +else +ifneq (,$(findstring WIN,$(OS))) + SHAREDLIB = natpmp.dll + JNISHAREDLIB = jninatpmp.dll + CC = i686-w64-mingw32-gcc + EXTRA_LD = -lws2_32 -lIphlpapi -Wl,--no-undefined -Wl,--enable-runtime-pseudo-reloc --Wl,kill-at +else + SHAREDLIB = libnatpmp.so + JNISHAREDLIB = libjninatpmp.so + SONAME = $(SHAREDLIB).$(APIVERSION) + SONAMEFLAGS=-Wl,-soname,$(JNISHAREDLIB) +endif +endif + +HEADERS = natpmp.h + +EXECUTABLES = testgetgateway natpmpc-shared natpmpc-static + +INSTALLPREFIX ?= $(PREFIX)/usr +INSTALLDIRINC = $(INSTALLPREFIX)/include +INSTALLDIRLIB = $(INSTALLPREFIX)/lib +INSTALLDIRBIN = $(INSTALLPREFIX)/bin + +JAVA ?= java +JAVAC ?= javac +JAVAH ?= javah +JAVAPACKAGE = fr/free/miniupnp/libnatpmp +JAVACLASSES = $(JAVAPACKAGE)/NatPmp.class $(JAVAPACKAGE)/NatPmpResponse.class $(JAVAPACKAGE)/LibraryExtractor.class $(JAVAPACKAGE)/URLUtils.class +JNIHEADERS = fr_free_miniupnp_libnatpmp_NatPmp.h + +.PHONY: all clean depend install cleaninstall installpythonmodule + +all: $(STATICLIB) $(SHAREDLIB) $(EXECUTABLES) + +pythonmodule: $(STATICLIB) libnatpmpmodule.c setup.py + python setup.py build + touch $@ + +installpythonmodule: pythonmodule + python setup.py install + +clean: + $(RM) $(OBJS) $(EXECUTABLES) $(STATICLIB) $(SHAREDLIB) $(JAVACLASSES) $(JNISHAREDLIB) + $(RM) pythonmodule + $(RM) -r build/ dist/ libraries/ + +depend: + makedepend -f$(MAKEFILE_LIST) -Y $(OBJS:.o=.c) 2>/dev/null + +install: $(HEADERS) $(STATICLIB) $(SHAREDLIB) natpmpc-shared + $(INSTALL) -d $(INSTALLDIRINC) + $(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC) + $(INSTALL) -d $(INSTALLDIRLIB) + $(INSTALL) -m 644 $(STATICLIB) $(INSTALLDIRLIB) + $(INSTALL) -m 644 $(SHAREDLIB) $(INSTALLDIRLIB)/$(SONAME) + $(INSTALL) -d $(INSTALLDIRBIN) + $(INSTALL) -m 755 natpmpc-shared $(INSTALLDIRBIN)/natpmpc + ln -s -f $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIB) + +$(JNIHEADERS): fr/free/miniupnp/libnatpmp/NatPmp.class + $(JAVAH) -jni fr.free.miniupnp.libnatpmp.NatPmp + +%.class: %.java + $(JAVAC) -cp . $< + +$(JNISHAREDLIB): $(SHAREDLIB) $(JNIHEADERS) $(JAVACLASSES) +ifeq (,$(JAVA_HOME)) + @echo "Check your JAVA_HOME environement variable" && false +endif +ifneq (,$(findstring WIN,$(OS))) + $(CC) -m32 -D_JNI_Implementation_ -Wl,--kill-at \ + -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" \ + natpmp-jni.c -shared \ + -o $(JNISHAREDLIB) -L. -lnatpmp -lws2_32 -lIphlpapi +else + $(CC) $(CFLAGS) -c -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" natpmp-jni.c + $(CC) $(CFLAGS) -o $(JNISHAREDLIB) -shared $(SONAMEFLAGS) natpmp-jni.o -lc -L. -lnatpmp +endif + +jar: $(JNISHAREDLIB) + find fr -name '*.class' -print > classes.list + $(eval JNISHAREDLIBPATH := $(shell java fr.free.miniupnp.libnatpmp.LibraryExtractor)) + mkdir -p libraries/$(JNISHAREDLIBPATH) + mv $(JNISHAREDLIB) libraries/$(JNISHAREDLIBPATH)/$(JNISHAREDLIB) + jar cf natpmp_$(JARSUFFIX).jar @classes.list libraries/$(JNISHAREDLIBPATH)/$(JNISHAREDLIB) + $(RM) classes.list + +jnitest: $(JNISHAREDLIB) JavaTest.class + $(RM) libjninatpmp.so + $(JAVA) -Djna.nosys=true -cp . JavaTest + +mvn_install: + mvn install:install-file -Dfile=java/natpmp_$(JARSUFFIX).jar \ + -DgroupId=com.github \ + -DartifactId=natpmp \ + -Dversion=$(VERSION) \ + -Dpackaging=jar \ + -Dclassifier=$(JARSUFFIX) \ + -DgeneratePom=true \ + -DcreateChecksum=true + +cleaninstall: + $(RM) $(addprefix $(INSTALLDIRINC), $(HEADERS)) + $(RM) $(INSTALLDIRLIB)/$(SONAME) + $(RM) $(INSTALLDIRLIB)/$(SHAREDLIB) + $(RM) $(INSTALLDIRLIB)/$(STATICLIB) + +testgetgateway: testgetgateway.o getgateway.o + $(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD) + +natpmpc-static: natpmpc.o $(STATICLIB) + $(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD) + +natpmpc-shared: natpmpc.o $(SHAREDLIB) + $(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD) + +$(STATICLIB): $(LIBOBJS) + $(AR) crs $@ $? + +$(SHAREDLIB): $(LIBOBJS) +ifeq ($(OS), Darwin) + $(CC) -dynamiclib -Wl,-install_name,$(SONAME) -o $@ $^ +else + $(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^ $(EXTRA_LD) +endif + + +# DO NOT DELETE + +natpmp.o: natpmp.h getgateway.h declspec.h +getgateway.o: getgateway.h declspec.h +testgetgateway.o: getgateway.h declspec.h +natpmpc.o: natpmp.h diff --git a/ext-deps/libnatpmp-20150609/README b/ext-deps/libnatpmp-20150609/README new file mode 100644 index 000000000..269392d2a --- /dev/null +++ b/ext-deps/libnatpmp-20150609/README @@ -0,0 +1,7 @@ +libnatpmp (c) 2007-2009 Thomas Bernard +contact : miniupnp@free.fr + +see http://miniupnp.free.fr/libnatpmp.html +or http://miniupnp.tuxfamily.org/libnatpmp.html +for some documentation and code samples. + diff --git a/ext-deps/libnatpmp-20150609/build.bat b/ext-deps/libnatpmp-20150609/build.bat new file mode 100644 index 000000000..2d2f27cd7 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/build.bat @@ -0,0 +1,30 @@ +@echo Compiling with MinGW +@SET LIBS=-lws2_32 -liphlpapi + +@echo Compile getgateway +gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR getgateway.c +gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR testgetgateway.c +gcc -o testgetgateway getgateway.o testgetgateway.o %LIBS% +del testgetgateway.o + +@echo Compile natpmp-static: +gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR getgateway.c +gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR natpmp.c +gcc -c -Wall -Os -DWIN32 wingettimeofday.c +ar cr natpmp.a getgateway.o natpmp.o wingettimeofday.o +del getgateway.o natpmp.o +gcc -c -Wall -Os -DWIN32 -DSTATICLIB -DENABLE_STRNATPMPERR natpmpc.c +gcc -o natpmpc-static natpmpc.o natpmp.a %LIBS% +upx --best natpmpc-static.exe +del natpmpc.o + +@echo Create natpmp.dll: +gcc -c -Wall -Os -DWIN32 -DENABLE_STRNATPMPERR -DNATPMP_EXPORTS getgateway.c +gcc -c -Wall -Os -DWIN32 -DENABLE_STRNATPMPERR -DNATPMP_EXPORTS natpmp.c +dllwrap -k --driver-name gcc --def natpmp.def --output-def natpmp.dll.def --implib natpmp.lib -o natpmp.dll getgateway.o natpmp.o wingettimeofday.o %LIBS% + +@echo Compile natpmp-shared: +gcc -c -Wall -Os -DWIN32 -DENABLE_STRNATPMPERR -DNATPMP_EXPORTS natpmpc.c +gcc -o natpmpc-shared natpmpc.o natpmp.lib -lws2_32 +upx --best natpmpc-shared.exe +del *.o diff --git a/ext-deps/libnatpmp-20150609/declspec.h b/ext-deps/libnatpmp-20150609/declspec.h new file mode 100644 index 000000000..a76be021c --- /dev/null +++ b/ext-deps/libnatpmp-20150609/declspec.h @@ -0,0 +1,21 @@ +#ifndef DECLSPEC_H_INCLUDED +#define DECLSPEC_H_INCLUDED + +#if defined(WIN32) && !defined(STATICLIB) + /* for windows dll */ + #ifdef NATPMP_EXPORTS + #define LIBSPEC __declspec(dllexport) + #else + #define LIBSPEC __declspec(dllimport) + #endif +#else + #if defined(__GNUC__) && __GNUC__ >= 4 + /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ + #define LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define LIBSPEC + #endif +#endif + +#endif + diff --git a/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/LibraryExtractor.java b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/LibraryExtractor.java new file mode 100644 index 000000000..5491d9408 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/LibraryExtractor.java @@ -0,0 +1,238 @@ +package fr.free.miniupnp.libnatpmp; + +/** I (Leah X Schmidt) copied this code from jnaerator, because +JNAerator's extractor requires you to buy into the whole JNA +concept. + +JNAErator is +Copyright (c) 2009 Olivier Chafik, All Rights Reserved + +JNAerator 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. + +JNAerator 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 JNAerator. If not, see . + +*/ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class LibraryExtractor { + + private static boolean libPathSet = false; + + public static String getLibraryPath(String libraryName, boolean extractAllLibraries, Class cl) { + try { + String customPath = System.getProperty("library." + libraryName); + if (customPath == null) + customPath = System.getenv(libraryName.toUpperCase() + "_LIBRARY"); + if (customPath != null) { + File f = new File(customPath); + if (!f.exists()) + System.err.println("Library file '" + customPath + "' does not exist !"); + else + return f.getAbsolutePath(); + } + //ClassLoader cl = LibraryExtractor.class.getClassLoader(); + String prefix = "(?i)" + (isWindows() ? "" : "lib") + libraryName + "[^A-Za-z_].*"; + String libsuffix = "(?i).*\\.(so|dll|dylib|jnilib)"; + //String othersuffix = "(?i).*\\.(pdb)"; + + URL sourceURL = null; + List otherURLs = new ArrayList(); + + + String arch = getCurrentOSAndArchString(); + //System.out.println("libURL = " + libURL); + List list = URLUtils.listFiles(URLUtils.getResource(cl, "libraries/" + arch)), + noArchList = URLUtils.listFiles(URLUtils.getResource(cl, "libraries/noarch")); + + Set names = new HashSet(); + for (URL url : list) { + String name = getFileName(url); + names.add(name); + } + for (URL url : noArchList) { + String name = getFileName(url); + if (names.add(name)) + list.add(url); + } + + for (File f : new File(".").listFiles()) + if (f.isFile()) + list.add(f.toURI().toURL()); + + for (URL url : list) { + String name = getFileName(url); + boolean pref = name.matches(prefix), suff = name.matches(libsuffix); + if (pref && suff) + sourceURL = url; + else //if (suff || fileName.matches(othersuffix)) + otherURLs.add(url); + } + List files = new ArrayList(); + if (extractAllLibraries) { + for (URL url : otherURLs) + files.add(extract(url)); + } + + if (System.getProperty("javawebstart.version") != null) { + if (isWindows()) { + //File f = new File("c:\\Windows\\" + (Platform.is64Bit() ? "SysWOW64\\" : "System32\\") + libraryName + ".dll"); + File f = new File("c:\\Windows\\" + "System32\\" + libraryName + ".dll"); + if (f.exists()) + return f.toString(); + } else if (isMac()) { + File f = new File("/System/Library/Frameworks/" + libraryName + ".framework/" + libraryName); + if (f.exists()) + return f.toString(); + } + } + + if (sourceURL == null) + return libraryName; + else { + File file = extract(sourceURL); + files.add(file); + + int lastSize; + do { + lastSize = files.size(); + for (Iterator it = files.iterator(); it.hasNext();) { + File f = it.next(); + if (!f.getName().matches(libsuffix)) + continue; + + try { + System.load(f.toString()); + it.remove(); + } catch (Throwable ex) { + System.err.println("Loading " + f.getName() + " failed (" + ex + ")"); + } + } + } while (files.size() < lastSize); + + return file.getCanonicalPath(); + } + } catch (Throwable ex) { + System.err.println("ERROR: Failed to extract library " + libraryName); + ex.printStackTrace(); + return libraryName; + } + } + + public static final boolean isWindows() { + String osName = System.getProperty("os.name"); + return osName.startsWith("Windows"); + } + + public static final boolean isMac() { + String osName = System.getProperty("os.name"); + return osName.startsWith("Mac") || osName.startsWith("Darwin"); + } + + //this code is from JNA, but JNA has a fallback to some native + //stuff in case this doesn't work. Since sun.arch.data.model is + //defined for Sun and IBM, this should work nearly everywhere. + public static final boolean is64Bit() { + String model = System.getProperty("sun.arch.data.model", + System.getProperty("com.ibm.vm.bitmode")); + if (model != null) { + return "64".equals(model); + } + String arch = System.getProperty("os.arch").toLowerCase(); + if ("x86_64".equals(arch) + || "ia64".equals(arch) + || "ppc64".equals(arch) + || "sparcv9".equals(arch) + || "amd64".equals(arch)) { + return true; + } + + return false; + } + + public static String getCurrentOSAndArchString() { + String os = System.getProperty("os.name"), arch = System.getProperty("os.arch"); + if (os.equals("Mac OS X")) { + os = "darwin"; + arch = "fat"; + } else if (os.startsWith("Windows")) { + return "win" + (is64Bit() ? "64" : "32"); + } else if (os.matches("SunOS|Solaris")) + os = "solaris"; + return os + "-" + arch; + } + + private static File extract(URL url) throws IOException { + File localFile; + if ("file".equals(url.getProtocol())) + localFile = new File(URLDecoder.decode(url.getFile(), "UTF-8")); + else { + File f = new File(System.getProperty("user.home")); + f = new File(f, ".jnaerator"); + f = new File(f, "extractedLibraries"); + if (!f.exists()) + f.mkdirs(); + + if (!libPathSet) { + String path = System.getProperty("java.library.path"); + if (path == null) { + System.setProperty("java.library.path", f.toString()); + } else { + System.setProperty("java.library.path", path + ":" + f); + } + + libPathSet = true; + } + localFile = new File(f, new File(url.getFile()).getName()); + URLConnection c = url.openConnection(); + if (localFile.exists() && localFile.lastModified() > c.getLastModified()) { + c.getInputStream().close(); + } else { + System.out.println("Extracting " + url); + InputStream in = c.getInputStream(); + OutputStream out = new FileOutputStream(localFile); + int len; + byte[] b = new byte[1024]; + while ((len = in.read(b)) > 0) + out.write(b, 0, len); + out.close(); + in.close(); + } + } + return localFile; + } + + private static String getFileName(URL url) { + return new File(url.getFile()).getName(); + } + + public static void main(String[] args) { + System.out.println(getCurrentOSAndArchString()); + } +} \ No newline at end of file diff --git a/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/NatPmp.java b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/NatPmp.java new file mode 100644 index 000000000..2f1ddd3d4 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/NatPmp.java @@ -0,0 +1,50 @@ +package fr.free.miniupnp.libnatpmp; + +import java.nio.ByteBuffer; + + +public class NatPmp { + private static final String JNA_LIBRARY_NAME = LibraryExtractor.getLibraryPath("jninatpmp", true, NatPmp.class); + + static { + String s = JNA_LIBRARY_NAME; + startup(); + } + + public ByteBuffer natpmp; + + public NatPmp() { + init(0, 0); + } + + public NatPmp(int forcedgw) { + init(1, forcedgw); + } + + /** Cleans up the native resources used by this object. + Attempting to use the object after this has been called + will lead to crashes.*/ + public void dispose() { + free(); + } + + + protected void finalize() { + if (natpmp != null) + free(); + } + + private native void init(int forcegw, int forcedgw); + private native void free(); + + private static native void startup(); + + public native int sendPublicAddressRequest(); + public native int sendNewPortMappingRequest(int protocol, int privateport, int publicport, int lifetime); + + //returns a number of milliseconds, in accordance with Java convention + public native long getNatPmpRequestTimeout(); + + public native int readNatPmpResponseOrRetry(NatPmpResponse response); + +} diff --git a/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/NatPmpResponse.java b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/NatPmpResponse.java new file mode 100644 index 000000000..35c87eab0 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/NatPmpResponse.java @@ -0,0 +1,28 @@ +package fr.free.miniupnp.libnatpmp; + +public class NatPmpResponse { + + public static final int TYPE_PUBLICADDRESS=0; + public static final int TYPE_UDPPORTMAPPING=1; + public static final int TYPE_TCPPORTMAPPING=2; + + /** see TYPE_* constants */ + public short type; + /** NAT-PMP response code */ + public short resultcode; + /** milliseconds since start of epoch */ + public long epoch; + + /** only defined if type == 0*/ + public int addr; + + /** only defined if type != 0 */ + public int privateport; + + /** only defined if type != 0 */ + public int mappedpublicport; + + /** only defined if type != 0 */ + public long lifetime; //milliseconds + +} \ No newline at end of file diff --git a/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/URLUtils.java b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/URLUtils.java new file mode 100644 index 000000000..5b419ab3a --- /dev/null +++ b/ext-deps/libnatpmp-20150609/fr/free/miniupnp/libnatpmp/URLUtils.java @@ -0,0 +1,81 @@ +package fr.free.miniupnp.libnatpmp; + +/** I (Leah X Schmidt) copied this code from jnaerator, because +JNAerator's extractor requires you to buy into the whole JNA +concept. + +JNAErator is +Copyright (c) 2009 Olivier Chafik, All Rights Reserved + +JNAerator 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. + +JNAerator 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 JNAerator. If not, see . + +*/ + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +public class URLUtils { + + public static URL getResource(Class cl, String path) throws IOException { + String clp = cl.getName().replace('.', '/') + ".class"; + URL clu = cl.getClassLoader().getResource(clp); + String s = clu.toString(); + if (s.endsWith(clp)) + return new URL(s.substring(0, s.length() - clp.length()) + path); + + if (s.startsWith("jar:")) { + String[] ss = s.split("!"); + return new URL(ss[1] + "!/" + path); + } + return null; + } + + public static List listFiles(URL directory) throws IOException { + List ret = new ArrayList(); + String s = directory.toString(); + if (s.startsWith("jar:")) { + String[] ss = s.substring("jar:".length()).split("!"); + String path = ss[1]; + URL target = new URL(ss[0]); + InputStream tin = target.openStream(); + try { + JarInputStream jin = new JarInputStream(tin); + JarEntry je; + while ((je = jin.getNextJarEntry()) != null) { + String p = "/" + je.getName(); + if (p.startsWith(path) && p.indexOf('/', path.length() + 1) < 0) + + ret.add(new URL("jar:" + target + "!" + p)); + } + } finally { + tin.close(); + } + } else if (s.startsWith("file:")) { + File f = new File(directory.getFile()); + File[] ffs = f.listFiles(); + if (ffs != null) + for (File ff : ffs) + ret.add(ff.toURI().toURL()); + } else + throw new IOException("Cannot list contents of " + directory); + + return ret; + } +} \ No newline at end of file diff --git a/ext-deps/libnatpmp-20150609/getgateway.c b/ext-deps/libnatpmp-20150609/getgateway.c new file mode 100644 index 000000000..dfb9f3e21 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/getgateway.c @@ -0,0 +1,573 @@ +/* $Id: getgateway.c,v 1.25 2014/04/22 10:28:57 nanard Exp $ */ +/* libnatpmp + +Copyright (c) 2007-2014, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#ifndef WIN32 +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +/* There is no portable method to get the default route gateway. + * So below are four (or five ?) differents functions implementing this. + * Parsing /proc/net/route is for linux. + * sysctl is the way to access such informations on BSD systems. + * Many systems should provide route information through raw PF_ROUTE + * sockets. + * In MS Windows, default gateway is found by looking into the registry + * or by using GetBestRoute(). */ +#ifdef __linux__ +#define USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#if defined(BSD) || defined(__FreeBSD_kernel__) +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef __APPLE__ +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#define USE_SYSCTL_NET_ROUTE +#endif + +#if (defined(sun) && defined(__SVR4)) +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef WIN32 +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +//#define USE_WIN32_CODE +#define USE_WIN32_CODE_2 +#endif + +#ifdef __CYGWIN__ +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#define USE_WIN32_CODE +#include +#include +#include +#include +#endif + +#ifdef __HAIKU__ +#include +#include +#include +#include +#define USE_HAIKU_CODE +#endif + +#ifdef USE_SYSCTL_NET_ROUTE +#include +#include +#include +#include +#endif +#ifdef USE_SOCKET_ROUTE +#include +#include +#include +#include +#include +#endif + +#ifdef USE_WIN32_CODE +#include +#include +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_LENGTH 16383 +#endif + +#ifdef USE_WIN32_CODE_2 +#include +#include +#endif + +#include "getgateway.h" + +#ifndef WIN32 +#define SUCCESS (0) +#define FAILED (-1) +#endif + +#ifdef USE_PROC_NET_ROUTE +/* + parse /proc/net/route which is as follow : + +Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT +wlan0 0001A8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 +eth0 0000FEA9 00000000 0001 0 0 0 0000FFFF 0 0 0 +wlan0 00000000 0101A8C0 0003 0 0 0 00000000 0 0 0 +eth0 00000000 00000000 0001 0 0 1000 00000000 0 0 0 + + One header line, and then one line by route by route table entry. +*/ +int getdefaultgateway(in_addr_t * addr) +{ + unsigned long d, g; + char buf[256]; + int line = 0; + FILE * f; + char * p; + f = fopen("/proc/net/route", "r"); + if(!f) + return FAILED; + while(fgets(buf, sizeof(buf), f)) { + if(line > 0) { /* skip the first line */ + p = buf; + /* skip the interface name */ + while(*p && !isspace(*p)) + p++; + while(*p && isspace(*p)) + p++; + if(sscanf(p, "%lx%lx", &d, &g)==2) { + if(d == 0 && g != 0) { /* default */ + *addr = g; + fclose(f); + return SUCCESS; + } + } + } + line++; + } + /* default route not found ! */ + if(f) + fclose(f); + return FAILED; +} +#endif /* #ifdef USE_PROC_NET_ROUTE */ + + +#ifdef USE_SYSCTL_NET_ROUTE + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +int getdefaultgateway(in_addr_t * addr) +{ +#if 0 + /* net.route.0.inet.dump.0.0 ? */ + int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_DUMP, 0, 0/*tableid*/}; +#endif + /* net.route.0.inet.flags.gateway */ + int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_FLAGS, RTF_GATEWAY}; + size_t l; + char * buf, * p; + struct rt_msghdr * rt; + struct sockaddr * sa; + struct sockaddr * sa_tab[RTAX_MAX]; + int i; + int r = FAILED; + if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) { + return FAILED; + } + if(l>0) { + buf = malloc(l); + if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) { + free(buf); + return FAILED; + } + for(p=buf; prtm_msglen) { + rt = (struct rt_msghdr *)p; + sa = (struct sockaddr *)(rt + 1); + for(i=0; irtm_addrs & (1 << i)) { + sa_tab[i] = sa; + sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len)); + } else { + sa_tab[i] = NULL; + } + } + if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY)) + && sa_tab[RTAX_DST]->sa_family == AF_INET + && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) { + if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) { + *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr; + r = SUCCESS; + } + } + } + free(buf); + } + return r; +} +#endif /* #ifdef USE_SYSCTL_NET_ROUTE */ + + +#ifdef USE_SOCKET_ROUTE +/* Thanks to Darren Kenny for this code */ +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\ + } + +#define rtm m_rtmsg.m_rtm + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int getdefaultgateway(in_addr_t *addr) +{ + int s, seq, l, rtm_addrs, i; + pid_t pid; + struct sockaddr so_dst, so_mask; + char *cp = m_rtmsg.m_space; + struct sockaddr *gate = NULL, *sa; + struct rt_msghdr *msg_hdr; + + pid = getpid(); + seq = 0; + rtm_addrs = RTA_DST | RTA_NETMASK; + + memset(&so_dst, 0, sizeof(so_dst)); + memset(&so_mask, 0, sizeof(so_mask)); + memset(&rtm, 0, sizeof(struct rt_msghdr)); + + rtm.rtm_type = RTM_GET; + rtm.rtm_flags = RTF_UP | RTF_GATEWAY; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + + so_dst.sa_family = AF_INET; + so_mask.sa_family = AF_INET; + + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + + s = socket(PF_ROUTE, SOCK_RAW, 0); + + if (write(s, (char *)&m_rtmsg, l) < 0) { + close(s); + return FAILED; + } + + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + + close(s); + + msg_hdr = &rtm; + + cp = ((char *)(msg_hdr + 1)); + if (msg_hdr->rtm_addrs) { + for (i = 1; i; i <<= 1) + if (i & msg_hdr->rtm_addrs) { + sa = (struct sockaddr *)cp; + if (i == RTA_GATEWAY ) + gate = sa; + + cp += sizeof(struct sockaddr); + } + } else { + return FAILED; + } + + + if (gate != NULL ) { + *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr; + return SUCCESS; + } else { + return FAILED; + } +} +#endif /* #ifdef USE_SOCKET_ROUTE */ + +#ifdef USE_WIN32_CODE +LIBSPEC int getdefaultgateway(in_addr_t * addr) +{ + HKEY networkCardsKey; + HKEY networkCardKey; + HKEY interfacesKey; + HKEY interfaceKey; + DWORD i = 0; + DWORD numSubKeys = 0; + TCHAR keyName[MAX_KEY_LENGTH]; + DWORD keyNameLength = MAX_KEY_LENGTH; + TCHAR keyValue[MAX_VALUE_LENGTH]; + DWORD keyValueLength = MAX_VALUE_LENGTH; + DWORD keyValueType = REG_SZ; + TCHAR gatewayValue[MAX_VALUE_LENGTH]; + DWORD gatewayValueLength = MAX_VALUE_LENGTH; + DWORD gatewayValueType = REG_MULTI_SZ; + int done = 0; + + //const char * networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; + //const char * interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; +#ifdef UNICODE + LPCTSTR networkCardsPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; + LPCTSTR interfacesPath = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; +#define STR_SERVICENAME L"ServiceName" +#define STR_DHCPDEFAULTGATEWAY L"DhcpDefaultGateway" +#define STR_DEFAULTGATEWAY L"DefaultGateway" +#else + LPCTSTR networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; + LPCTSTR interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; +#define STR_SERVICENAME "ServiceName" +#define STR_DHCPDEFAULTGATEWAY "DhcpDefaultGateway" +#define STR_DEFAULTGATEWAY "DefaultGateway" +#endif + // The windows registry lists its primary network devices in the following location: + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards + // + // Each network device has its own subfolder, named with an index, with various properties: + // -NetworkCards + // -5 + // -Description = Broadcom 802.11n Network Adapter + // -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D} + // -8 + // -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller + // -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD} + // + // The above service name is the name of a subfolder within: + // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces + // + // There may be more subfolders in this interfaces path than listed in the network cards path above: + // -Interfaces + // -{3a539854-6a70-11db-887c-806e6f6e6963} + // -DhcpIPAddress = 0.0.0.0 + // -[more] + // -{E35A72F8-5065-4097-8DFE-C7790774EE4D} + // -DhcpIPAddress = 10.0.1.4 + // -DhcpDefaultGateway = 10.0.1.1 + // -[more] + // -{86226414-5545-4335-A9D1-5BD7120119AD} + // -DhcpIpAddress = 10.0.1.5 + // -DhcpDefaultGateay = 10.0.1.1 + // -[more] + // + // In order to extract this information, we enumerate each network card, and extract the ServiceName value. + // This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value. + // Once one is found, we're done. + // + // It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value. + // However, the technique used is the technique most cited on the web, and we assume it to be more correct. + + if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predifined key + networkCardsPath, // Name of registry subkey to open + 0, // Reserved - must be zero + KEY_READ, // Mask - desired access rights + &networkCardsKey)) // Pointer to output key + { + // Unable to open network cards keys + return -1; + } + + if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predefined key + interfacesPath, // Name of registry subkey to open + 0, // Reserved - must be zero + KEY_READ, // Mask - desired access rights + &interfacesKey)) // Pointer to output key + { + // Unable to open interfaces key + RegCloseKey(networkCardsKey); + return -1; + } + + // Figure out how many subfolders are within the NetworkCards folder + RegQueryInfoKey(networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + //printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys); + + // Enumrate through each subfolder within the NetworkCards folder + for(i = 0; i < numSubKeys && !done; i++) + { + keyNameLength = MAX_KEY_LENGTH; + if(ERROR_SUCCESS == RegEnumKeyEx(networkCardsKey, // Open registry key + i, // Index of subkey to retrieve + keyName, // Buffer that receives the name of the subkey + &keyNameLength, // Variable that receives the size of the above buffer + NULL, // Reserved - must be NULL + NULL, // Buffer that receives the class string + NULL, // Variable that receives the size of the above buffer + NULL)) // Variable that receives the last write time of subkey + { + if(RegOpenKeyEx(networkCardsKey, keyName, 0, KEY_READ, &networkCardKey) == ERROR_SUCCESS) + { + keyValueLength = MAX_VALUE_LENGTH; + if(ERROR_SUCCESS == RegQueryValueEx(networkCardKey, // Open registry key + STR_SERVICENAME, // Name of key to query + NULL, // Reserved - must be NULL + &keyValueType, // Receives value type + (LPBYTE)keyValue, // Receives value + &keyValueLength)) // Receives value length in bytes + { +// printf("keyValue: %s\n", keyValue); + if(RegOpenKeyEx(interfacesKey, keyValue, 0, KEY_READ, &interfaceKey) == ERROR_SUCCESS) + { + gatewayValueLength = MAX_VALUE_LENGTH; + if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key + STR_DHCPDEFAULTGATEWAY, // Name of key to query + NULL, // Reserved - must be NULL + &gatewayValueType, // Receives value type + (LPBYTE)gatewayValue, // Receives value + &gatewayValueLength)) // Receives value length in bytes + { + // Check to make sure it's a string + if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1)) + { + //printf("gatewayValue: %s\n", gatewayValue); + done = 1; + } + } + else if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key + STR_DEFAULTGATEWAY, // Name of key to query + NULL, // Reserved - must be NULL + &gatewayValueType, // Receives value type + (LPBYTE)gatewayValue,// Receives value + &gatewayValueLength)) // Receives value length in bytes + { + // Check to make sure it's a string + if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1)) + { + //printf("gatewayValue: %s\n", gatewayValue); + done = 1; + } + } + RegCloseKey(interfaceKey); + } + } + RegCloseKey(networkCardKey); + } + } + } + + RegCloseKey(interfacesKey); + RegCloseKey(networkCardsKey); + + if(done) + { +#if UNICODE + char tmp[32]; + for(i = 0; i < 32; i++) { + tmp[i] = (char)gatewayValue[i]; + if(!tmp[i]) + break; + } + tmp[31] = '\0'; + *addr = inet_addr(tmp); +#else + *addr = inet_addr(gatewayValue); +#endif + return 0; + } + + return -1; +} +#endif /* #ifdef USE_WIN32_CODE */ + +#ifdef USE_WIN32_CODE_2 +int getdefaultgateway(in_addr_t *addr) +{ + MIB_IPFORWARDROW ip_forward; + memset(&ip_forward, 0, sizeof(ip_forward)); + if(GetBestRoute(inet_addr("0.0.0.0"), 0, &ip_forward) != NO_ERROR) + return -1; + *addr = ip_forward.dwForwardNextHop; + return 0; +} +#endif /* #ifdef USE_WIN32_CODE_2 */ + +#ifdef USE_HAIKU_CODE +int getdefaultgateway(in_addr_t *addr) +{ + int fd, ret = -1; + struct ifconf config; + void *buffer = NULL; + struct ifreq *interface; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return -1; + } + if (ioctl(fd, SIOCGRTSIZE, &config, sizeof(config)) != 0) { + goto fail; + } + if (config.ifc_value < 1) { + goto fail; /* No routes */ + } + if ((buffer = malloc(config.ifc_value)) == NULL) { + goto fail; + } + config.ifc_len = config.ifc_value; + config.ifc_buf = buffer; + if (ioctl(fd, SIOCGRTTABLE, &config, sizeof(config)) != 0) { + goto fail; + } + for (interface = buffer; + (uint8_t *)interface < (uint8_t *)buffer + config.ifc_len; ) { + struct route_entry route = interface->ifr_route; + int intfSize; + if (route.flags & (RTF_GATEWAY | RTF_DEFAULT)) { + *addr = ((struct sockaddr_in *)route.gateway)->sin_addr.s_addr; + ret = 0; + break; + } + intfSize = sizeof(route) + IF_NAMESIZE; + if (route.destination != NULL) { + intfSize += route.destination->sa_len; + } + if (route.mask != NULL) { + intfSize += route.mask->sa_len; + } + if (route.gateway != NULL) { + intfSize += route.gateway->sa_len; + } + interface = (struct ifreq *)((uint8_t *)interface + intfSize); + } +fail: + free(buffer); + close(fd); + return ret; +} +#endif /* #ifdef USE_HAIKU_CODE */ + +#if !defined(USE_PROC_NET_ROUTE) && !defined(USE_SOCKET_ROUTE) && !defined(USE_SYSCTL_NET_ROUTE) && !defined(USE_WIN32_CODE) && !defined(USE_WIN32_CODE_2) && !defined(USE_HAIKU_CODE) +int getdefaultgateway(in_addr_t * addr) +{ + return -1; +} +#endif diff --git a/ext-deps/libnatpmp-20150609/getgateway.h b/ext-deps/libnatpmp-20150609/getgateway.h new file mode 100644 index 000000000..5d3df7312 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/getgateway.h @@ -0,0 +1,49 @@ +/* $Id: getgateway.h,v 1.8 2014/04/22 09:15:40 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2014, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __GETGATEWAY_H__ +#define __GETGATEWAY_H__ + +#ifdef WIN32 +#if !defined(_MSC_VER) || _MSC_VER >= 1600 +#include +#else +typedef unsigned long uint32_t; +typedef unsigned short uint16_t; +#endif +#define in_addr_t uint32_t +#endif +/* #include "declspec.h" */ + +/* getdefaultgateway() : + * return value : + * 0 : success + * -1 : failure */ +/* LIBSPEC */int getdefaultgateway(in_addr_t * addr); + +#endif diff --git a/ext-deps/libnatpmp-20150609/libnatpmpmodule.c b/ext-deps/libnatpmp-20150609/libnatpmpmodule.c new file mode 100644 index 000000000..0fd9914b5 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/libnatpmpmodule.c @@ -0,0 +1,281 @@ +/* $Id: libnatpmpmodule.c,v 1.7 2012/03/05 19:38:37 nanard Exp $ */ +/* libnatpmp + * http://miniupnp.free.fr/libnatpmp.html +Copyright (c) 2007-2011, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#ifdef WIN32 +#include +#else +#include +#include +#endif + +#define STATICLIB +#include "structmember.h" +#include "natpmp.h" + +/* for compatibility with Python < 2.4 */ +#ifndef Py_RETURN_NONE +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +#ifndef Py_RETURN_TRUE +#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True +#endif + +#ifndef Py_RETURN_FALSE +#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False +#endif + +typedef struct { + PyObject_HEAD + + /* Type-specific fields go here. */ + unsigned int discoverdelay; + + natpmp_t natpmp; +} NATPMPObject; + +static PyMemberDef NATPMP_members[] = { + {"discoverdelay", T_UINT, offsetof(NATPMPObject, discoverdelay), + 0/*READWRITE*/, "value in ms used to wait for NATPMP responses" + }, + {NULL} +}; + +static PyObject * +NATPMPObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + NATPMPObject *self; + + self = (NATPMPObject *)type->tp_alloc(type, 0); + if (self) { + initnatpmp(&self->natpmp, 0, 0); + } + + return (PyObject *)self; +} + +static void +NATPMPObject_dealloc(NATPMPObject *self) +{ + closenatpmp(&self->natpmp); + self->ob_type->tp_free((PyObject*)self); +} + +static PyObject * +NATPMP_externalipaddress(NATPMPObject *self) +{ + int r; + struct timeval timeout; + fd_set fds; + natpmpresp_t response; + + r = sendpublicaddressrequest(&self->natpmp); + + if (r < 0) { +#ifdef ENABLE_STRNATPMPERR + PyErr_SetString(PyExc_Exception, strnatpmperr(r)); +#endif + return NULL; + } + + do { + FD_ZERO(&fds); + FD_SET(self->natpmp.s, &fds); + getnatpmprequesttimeout(&self->natpmp, &timeout); + select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + r = readnatpmpresponseorretry(&self->natpmp, &response); + if (r < 0 && r != NATPMP_TRYAGAIN) { +#ifdef ENABLE_STRNATPMPERR + PyErr_SetString(PyExc_Exception, strnatpmperr(r)); +#endif + return NULL; + } + } while (r == NATPMP_TRYAGAIN); + + return Py_BuildValue("s", inet_ntoa(response.pnu.publicaddress.addr)); +} + +static PyObject * +NATPMP_domapping(natpmp_t *n, unsigned short eport, unsigned short iport, + const char *protocol, unsigned int lifetime) +{ + int proto; + struct timeval timeout; + fd_set fds; + natpmpresp_t response; + int r; + + if (!strncasecmp("tcp", protocol, 3)) { + proto = NATPMP_PROTOCOL_TCP; + } else if (!strncasecmp("udp", protocol, 3)) { + proto = NATPMP_PROTOCOL_UDP; + } else { + PyErr_SetString(PyExc_Exception, "Unknown protocol"); + return NULL; + } + + r = sendnewportmappingrequest(n, proto, iport, eport, + lifetime); + + if (r < 0) { +#ifdef ENABLE_STRNATPMPERR + PyErr_SetString(PyExc_Exception, strnatpmperr(r)); +#endif + return NULL; + } + + do { + FD_ZERO(&fds); + FD_SET(n->s, &fds); + getnatpmprequesttimeout(n, &timeout); + select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + r = readnatpmpresponseorretry(n, &response); + if (r < 0 && r != NATPMP_TRYAGAIN) { +#ifdef ENABLE_STRNATPMPERR + PyErr_SetString(PyExc_Exception, strnatpmperr(r)); +#endif + return NULL; + } + } while (r == NATPMP_TRYAGAIN); + + return Py_BuildValue("H", response.pnu.newportmapping.mappedpublicport); +} + + +/* AddPortMapping(externalPort, protocol, internalPort, lifetime) + * protocol is 'UDP' or 'TCP' */ +static PyObject * +NATPMP_addportmapping(NATPMPObject *self, PyObject *args) +{ + unsigned short eport; + unsigned short iport; + unsigned int lifetime; + const char *protocol; + + if (!PyArg_ParseTuple(args, "HsHI", &eport, &protocol, &iport, &lifetime)) + return NULL; + + return NATPMP_domapping(&self->natpmp, eport, iport, protocol, lifetime); +} + +/* DeletePortMapping(externalPort, protocol, internalPort) + * protocol is 'UDP' or 'TCP' */ +static PyObject * +NATPMP_deleteportmapping(NATPMPObject *self, PyObject *args) +{ + unsigned short eport; + unsigned short iport; + const char *protocol; + + if (!PyArg_ParseTuple(args, "HsH", &eport, &protocol, &iport)) + return NULL; + + return NATPMP_domapping(&self->natpmp, eport, iport, protocol, 0); +} + +/* natpmp.NATPMP object Method Table */ +static PyMethodDef NATPMP_methods[] = { + {"externalipaddress", (PyCFunction)NATPMP_externalipaddress, METH_NOARGS, + "return external IP address" + }, + {"addportmapping", (PyCFunction)NATPMP_addportmapping, METH_VARARGS, + "add a port mapping" + }, + {"deleteportmapping", (PyCFunction)NATPMP_deleteportmapping, METH_VARARGS, + "delete a port mapping" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject NATPMPType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "libnatpmp.NATPMP", /*tp_name*/ + sizeof(NATPMPObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)NATPMPObject_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + "NATPMP objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + NATPMP_methods, /* tp_methods */ + NATPMP_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + NATPMPObject_new, /* tp_new */ +}; + +/* module methods */ +static PyMethodDef libnatpmp_methods[] = { + {NULL} /* Sentinel */ +}; + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif +PyMODINIT_FUNC +initlibnatpmp(void) +{ + PyObject* m; + + if (PyType_Ready(&NATPMPType) < 0) + return; + + m = Py_InitModule3("libnatpmp", libnatpmp_methods, + "libnatpmp module."); + + Py_INCREF(&NATPMPType); + PyModule_AddObject(m, "NATPMP", (PyObject *)&NATPMPType); +} + diff --git a/ext-deps/libnatpmp-20150609/msvc/libnatpmp.sln b/ext-deps/libnatpmp-20150609/msvc/libnatpmp.sln new file mode 100644 index 000000000..ac746d415 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/msvc/libnatpmp.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnatpmp", "libnatpmp.vcproj", "{D59B6527-F3DE-4D26-A08D-52F1EE989301}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "natpmpc-static", "natpmpc-static.vcproj", "{A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}" + ProjectSection(ProjectDependencies) = postProject + {D59B6527-F3DE-4D26-A08D-52F1EE989301} = {D59B6527-F3DE-4D26-A08D-52F1EE989301} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D59B6527-F3DE-4D26-A08D-52F1EE989301}.Debug|Win32.ActiveCfg = Debug|Win32 + {D59B6527-F3DE-4D26-A08D-52F1EE989301}.Debug|Win32.Build.0 = Debug|Win32 + {D59B6527-F3DE-4D26-A08D-52F1EE989301}.Release|Win32.ActiveCfg = Release|Win32 + {D59B6527-F3DE-4D26-A08D-52F1EE989301}.Release|Win32.Build.0 = Release|Win32 + {A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Debug|Win32.Build.0 = Debug|Win32 + {A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Release|Win32.ActiveCfg = Release|Win32 + {A0B49FA9-98AB-4A74-8B4C-8AB7FA36089B}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/ext-deps/libnatpmp-20150609/msvc/libnatpmp.vcproj b/ext-deps/libnatpmp-20150609/msvc/libnatpmp.vcproj new file mode 100644 index 000000000..9bae5c185 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/msvc/libnatpmp.vcproj @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext-deps/libnatpmp-20150609/msvc/natpmpc-static.vcproj b/ext-deps/libnatpmp-20150609/msvc/natpmpc-static.vcproj new file mode 100644 index 000000000..c2052d982 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/msvc/natpmpc-static.vcproj @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext-deps/libnatpmp-20150609/natpmp-jni.c b/ext-deps/libnatpmp-20150609/natpmp-jni.c new file mode 100644 index 000000000..feec1cea6 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/natpmp-jni.c @@ -0,0 +1,157 @@ +#ifdef __CYGWIN__ +#include +#define __int64 uint64_t +#endif + +#ifdef WIN32 +#include +#include +#include +#endif + +#include +#include "natpmp.h" + +#include "fr_free_miniupnp_libnatpmp_NatPmp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT void JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_init (JNIEnv *env, jobject obj, jint forcegw, jint forcedgw) { + natpmp_t *p = malloc (sizeof(natpmp_t)); + if (p == NULL) return; + + initnatpmp(p, forcegw, (in_addr_t) forcedgw); + + jobject wrapped = (*env)->NewDirectByteBuffer(env, p, sizeof(natpmp_t)); + if (wrapped == NULL) return; + + jclass thisClass = (*env)->GetObjectClass(env,obj); + if (thisClass == NULL) return; + + jfieldID fid = (*env)->GetFieldID(env, thisClass, "natpmp", "Ljava/nio/ByteBuffer;"); + if (fid == NULL) return; + (*env)->SetObjectField(env, obj, fid, wrapped); +} + +JNIEXPORT void JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_free (JNIEnv *env, jobject obj) { + + jclass thisClass = (*env)->GetObjectClass(env,obj); + if (thisClass == NULL) return; + + jfieldID fid = (*env)->GetFieldID(env, thisClass, "natpmp", "Ljava/nio/ByteBuffer;"); + + if (fid == NULL) return; + jobject wrapped = (*env)->GetObjectField(env, obj, fid); + if (wrapped == NULL) return; + + natpmp_t* natpmp = (natpmp_t*) (*env)->GetDirectBufferAddress(env, wrapped); + + closenatpmp(natpmp); + + if (natpmp == NULL) return; + free(natpmp); + + (*env)->SetObjectField(env, obj, fid, NULL); +} + +static natpmp_t* getNatPmp(JNIEnv* env, jobject obj) { + jclass thisClass = (*env)->GetObjectClass(env,obj); + if (thisClass == NULL) return NULL; + + jfieldID fid = (*env)->GetFieldID(env, thisClass, "natpmp", "Ljava/nio/ByteBuffer;"); + + if (fid == NULL) return NULL; + jobject wrapped = (*env)->GetObjectField(env, obj, fid); + if (wrapped == NULL) return NULL; + + natpmp_t* natpmp = (natpmp_t*) (*env)->GetDirectBufferAddress(env, wrapped); + + return natpmp; +} + +JNIEXPORT jint JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_sendPublicAddressRequest(JNIEnv* env, jobject obj) { + natpmp_t* natpmp = getNatPmp(env, obj); + if (natpmp == NULL) return -1; + + return sendpublicaddressrequest(natpmp); +} + + +JNIEXPORT void JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_startup(JNIEnv* env, jclass cls) { + (void)env; + (void)cls; +#ifdef WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + WSAStartup(wVersionRequested, &wsaData); +#endif +} + + +JNIEXPORT jint JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_sendNewPortMappingRequest(JNIEnv* env, jobject obj, jint protocol, jint privateport, jint publicport, jint lifetime) { + natpmp_t* natpmp = getNatPmp(env, obj); + if (natpmp == NULL) return -1; + + return sendnewportmappingrequest(natpmp, protocol, privateport, publicport, lifetime); +} + +JNIEXPORT jlong JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_getNatPmpRequestTimeout(JNIEnv* env, jobject obj) { + natpmp_t* natpmp = getNatPmp(env, obj); + + struct timeval timeout; + + getnatpmprequesttimeout(natpmp, &timeout); + + return ((jlong) timeout.tv_sec) * 1000 + (timeout.tv_usec / 1000); + +} + +#define SET_FIELD(prefix, name, type, longtype) { \ + jfieldID fid = (*env)->GetFieldID(env, thisClass, #name, type); \ + if (fid == NULL) return -1; \ + (*env)->Set ## longtype ## Field(env, response, fid, resp. prefix name); \ +} + +JNIEXPORT jint JNICALL Java_fr_free_miniupnp_libnatpmp_NatPmp_readNatPmpResponseOrRetry(JNIEnv* env, jobject obj, jobject response) { + + natpmp_t* natpmp = getNatPmp(env, obj); + natpmpresp_t resp; + int result = readnatpmpresponseorretry(natpmp, &resp); + + if (result != 0) { + return result; + } + + jclass thisClass = (*env)->GetObjectClass(env, response); + if (thisClass == NULL) return -1; + + SET_FIELD(,type, "S", Short); + SET_FIELD(,resultcode, "S", Short); + + jfieldID fid = (*env)->GetFieldID(env, thisClass, "epoch", "J"); + if (fid == NULL) return -1; + (*env)->SetLongField(env, response, fid, ((jlong)resp.epoch) * 1000); + + if (resp.type == 0) { + jfieldID fid = (*env)->GetFieldID(env, thisClass, "addr", "I"); + if (fid == NULL) return -1; + (*env)->SetIntField(env, response, fid, resp.pnu.publicaddress.addr.s_addr); + + + } else { + SET_FIELD(pnu.newportmapping., privateport, "I", Int); + SET_FIELD(pnu.newportmapping., mappedpublicport, "I", Int); + + jfieldID fid = (*env)->GetFieldID(env, thisClass, "lifetime", "J"); + if (fid == NULL) return -1; + (*env)->SetLongField(env, response, fid, ((jlong) resp.pnu.newportmapping.lifetime) * 1000 * 1000); + } + return result; +} + + +#ifdef __cplusplus +} +#endif diff --git a/ext-deps/libnatpmp-20150609/natpmp.c b/ext-deps/libnatpmp-20150609/natpmp.c new file mode 100644 index 000000000..3a4981292 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/natpmp.c @@ -0,0 +1,379 @@ +/* $Id: natpmp.c,v 1.20 2015/05/27 12:43:15 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2015, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifdef __linux__ +#define _BSD_SOURCE 1 +#endif +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#ifdef WIN32 +#include +#include +#include +#include +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNREFUSED WSAECONNREFUSED +#include "wingettimeofday.h" +#define gettimeofday natpmp_gettimeofday +#else +#include +#include +#include +#include +#include +#define closesocket close +#endif +#include "natpmp.h" +#include "getgateway.h" +#include + +LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw) +{ +#ifdef WIN32 + u_long ioctlArg = 1; +#else + int flags; +#endif + struct sockaddr_in addr; + if(!p) + return NATPMP_ERR_INVALIDARGS; + memset(p, 0, sizeof(natpmp_t)); + p->s = socket(PF_INET, SOCK_DGRAM, 0); + if(p->s < 0) + return NATPMP_ERR_SOCKETERROR; +#ifdef WIN32 + if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR) + return NATPMP_ERR_FCNTLERROR; +#else + if((flags = fcntl(p->s, F_GETFL, 0)) < 0) + return NATPMP_ERR_FCNTLERROR; + if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0) + return NATPMP_ERR_FCNTLERROR; +#endif + + if(forcegw) { + p->gateway = forcedgw; + } else { + if(getdefaultgateway(&(p->gateway)) < 0) + return NATPMP_ERR_CANNOTGETGATEWAY; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(NATPMP_PORT); + addr.sin_addr.s_addr = p->gateway; + if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) + return NATPMP_ERR_CONNECTERR; + return 0; +} + +LIBSPEC int closenatpmp(natpmp_t * p) +{ + if(!p) + return NATPMP_ERR_INVALIDARGS; + if(closesocket(p->s) < 0) + return NATPMP_ERR_CLOSEERR; + return 0; +} + +int sendpendingrequest(natpmp_t * p) +{ + int r; +/* struct sockaddr_in addr;*/ + if(!p) + return NATPMP_ERR_INVALIDARGS; +/* memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(NATPMP_PORT); + addr.sin_addr.s_addr = p->gateway; + r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0, + (struct sockaddr *)&addr, sizeof(addr));*/ + r = (int)send(p->s, (const char *)p->pending_request, p->pending_request_len, 0); + return (r<0) ? NATPMP_ERR_SENDERR : r; +} + +int sendnatpmprequest(natpmp_t * p) +{ + int n; + if(!p) + return NATPMP_ERR_INVALIDARGS; + /* TODO : check if no request is already pending */ + p->has_pending_request = 1; + p->try_number = 1; + n = sendpendingrequest(p); + gettimeofday(&p->retry_time, NULL); // check errors ! + p->retry_time.tv_usec += 250000; /* add 250ms */ + if(p->retry_time.tv_usec >= 1000000) { + p->retry_time.tv_usec -= 1000000; + p->retry_time.tv_sec++; + } + return n; +} + +LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout) +{ + struct timeval now; + if(!p || !timeout) + return NATPMP_ERR_INVALIDARGS; + if(!p->has_pending_request) + return NATPMP_ERR_NOPENDINGREQ; + if(gettimeofday(&now, NULL) < 0) + return NATPMP_ERR_GETTIMEOFDAYERR; + timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec; + timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec; + if(timeout->tv_usec < 0) { + timeout->tv_usec += 1000000; + timeout->tv_sec--; + } + return 0; +} + +LIBSPEC int sendpublicaddressrequest(natpmp_t * p) +{ + if(!p) + return NATPMP_ERR_INVALIDARGS; + //static const unsigned char request[] = { 0, 0 }; + p->pending_request[0] = 0; + p->pending_request[1] = 0; + p->pending_request_len = 2; + // TODO: return 0 instead of sizeof(request) ?? + return sendnatpmprequest(p); +} + +LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol, + uint16_t privateport, uint16_t publicport, + uint32_t lifetime) +{ + if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP)) + return NATPMP_ERR_INVALIDARGS; + p->pending_request[0] = 0; + p->pending_request[1] = protocol; + p->pending_request[2] = 0; + p->pending_request[3] = 0; + /* break strict-aliasing rules : + *((uint16_t *)(p->pending_request + 4)) = htons(privateport); */ + p->pending_request[4] = (privateport >> 8) & 0xff; + p->pending_request[5] = privateport & 0xff; + /* break stric-aliasing rules : + *((uint16_t *)(p->pending_request + 6)) = htons(publicport); */ + p->pending_request[6] = (publicport >> 8) & 0xff; + p->pending_request[7] = publicport & 0xff; + /* break stric-aliasing rules : + *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime); */ + p->pending_request[8] = (lifetime >> 24) & 0xff; + p->pending_request[9] = (lifetime >> 16) & 0xff; + p->pending_request[10] = (lifetime >> 8) & 0xff; + p->pending_request[11] = lifetime & 0xff; + p->pending_request_len = 12; + return sendnatpmprequest(p); +} + +LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response) +{ + unsigned char buf[16]; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + int n; + if(!p) + return NATPMP_ERR_INVALIDARGS; + n = recvfrom(p->s, (char *)buf, sizeof(buf), 0, + (struct sockaddr *)&addr, &addrlen); + if(n<0) +#ifdef WIN32 + switch(WSAGetLastError()) { +#else + switch(errno) { +#endif + /*case EAGAIN:*/ + case EWOULDBLOCK: + n = NATPMP_TRYAGAIN; + break; + case ECONNREFUSED: + n = NATPMP_ERR_NOGATEWAYSUPPORT; + break; + default: + n = NATPMP_ERR_RECVFROM; + } + /* check that addr is correct (= gateway) */ + else if(addr.sin_addr.s_addr != p->gateway) + n = NATPMP_ERR_WRONGPACKETSOURCE; + else { + response->resultcode = ntohs(*((uint16_t *)(buf + 2))); + response->epoch = ntohl(*((uint32_t *)(buf + 4))); + if(buf[0] != 0) + n = NATPMP_ERR_UNSUPPORTEDVERSION; + else if(buf[1] < 128 || buf[1] > 130) + n = NATPMP_ERR_UNSUPPORTEDOPCODE; + else if(response->resultcode != 0) { + switch(response->resultcode) { + case 1: + n = NATPMP_ERR_UNSUPPORTEDVERSION; + break; + case 2: + n = NATPMP_ERR_NOTAUTHORIZED; + break; + case 3: + n = NATPMP_ERR_NETWORKFAILURE; + break; + case 4: + n = NATPMP_ERR_OUTOFRESOURCES; + break; + case 5: + n = NATPMP_ERR_UNSUPPORTEDOPCODE; + break; + default: + n = NATPMP_ERR_UNDEFINEDERROR; + } + } else { + response->type = buf[1] & 0x7f; + if(buf[1] == 128) + //response->publicaddress.addr = *((uint32_t *)(buf + 8)); + response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8)); + else { + response->pnu.newportmapping.privateport = ntohs(*((uint16_t *)(buf + 8))); + response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t *)(buf + 10))); + response->pnu.newportmapping.lifetime = ntohl(*((uint32_t *)(buf + 12))); + } + n = 0; + } + } + return n; +} + +int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response) +{ + int n; + if(!p || !response) + return NATPMP_ERR_INVALIDARGS; + if(!p->has_pending_request) + return NATPMP_ERR_NOPENDINGREQ; + n = readnatpmpresponse(p, response); + if(n<0) { + if(n==NATPMP_TRYAGAIN) { + struct timeval now; + gettimeofday(&now, NULL); // check errors ! + if(timercmp(&now, &p->retry_time, >=)) { + int delay, r; + if(p->try_number >= 9) { + return NATPMP_ERR_NOGATEWAYSUPPORT; + } + /*printf("retry! %d\n", p->try_number);*/ + delay = 250 * (1<try_number); // ms + /*for(i=0; itry_number; i++) + delay += delay;*/ + p->retry_time.tv_sec += (delay / 1000); + p->retry_time.tv_usec += (delay % 1000) * 1000; + if(p->retry_time.tv_usec >= 1000000) { + p->retry_time.tv_usec -= 1000000; + p->retry_time.tv_sec++; + } + p->try_number++; + r = sendpendingrequest(p); + if(r<0) + return r; + } + } + } else { + p->has_pending_request = 0; + } + return n; +} + +#ifdef ENABLE_STRNATPMPERR +LIBSPEC const char * strnatpmperr(int r) +{ + const char * s; + switch(r) { + case NATPMP_ERR_INVALIDARGS: + s = "invalid arguments"; + break; + case NATPMP_ERR_SOCKETERROR: + s = "socket() failed"; + break; + case NATPMP_ERR_CANNOTGETGATEWAY: + s = "cannot get default gateway ip address"; + break; + case NATPMP_ERR_CLOSEERR: +#ifdef WIN32 + s = "closesocket() failed"; +#else + s = "close() failed"; +#endif + break; + case NATPMP_ERR_RECVFROM: + s = "recvfrom() failed"; + break; + case NATPMP_ERR_NOPENDINGREQ: + s = "no pending request"; + break; + case NATPMP_ERR_NOGATEWAYSUPPORT: + s = "the gateway does not support nat-pmp"; + break; + case NATPMP_ERR_CONNECTERR: + s = "connect() failed"; + break; + case NATPMP_ERR_WRONGPACKETSOURCE: + s = "packet not received from the default gateway"; + break; + case NATPMP_ERR_SENDERR: + s = "send() failed"; + break; + case NATPMP_ERR_FCNTLERROR: + s = "fcntl() failed"; + break; + case NATPMP_ERR_GETTIMEOFDAYERR: + s = "gettimeofday() failed"; + break; + case NATPMP_ERR_UNSUPPORTEDVERSION: + s = "unsupported nat-pmp version error from server"; + break; + case NATPMP_ERR_UNSUPPORTEDOPCODE: + s = "unsupported nat-pmp opcode error from server"; + break; + case NATPMP_ERR_UNDEFINEDERROR: + s = "undefined nat-pmp server error"; + break; + case NATPMP_ERR_NOTAUTHORIZED: + s = "not authorized"; + break; + case NATPMP_ERR_NETWORKFAILURE: + s = "network failure"; + break; + case NATPMP_ERR_OUTOFRESOURCES: + s = "nat-pmp server out of resources"; + break; + default: + s = "Unknown libnatpmp error"; + } + return s; +} +#endif + diff --git a/ext-deps/libnatpmp-20150609/natpmp.def b/ext-deps/libnatpmp-20150609/natpmp.def new file mode 100644 index 000000000..cd110033f --- /dev/null +++ b/ext-deps/libnatpmp-20150609/natpmp.def @@ -0,0 +1,11 @@ +LIBRARY +; libnatpmp library + +EXPORTS + initnatpmp + closenatpmp + sendpublicaddressrequest + sendnewportmappingrequest + getnatpmprequesttimeout + readnatpmpresponseorretry + strnatpmperr diff --git a/ext-deps/libnatpmp-20150609/natpmp.h b/ext-deps/libnatpmp-20150609/natpmp.h new file mode 100644 index 000000000..7889d206f --- /dev/null +++ b/ext-deps/libnatpmp-20150609/natpmp.h @@ -0,0 +1,219 @@ +/* $Id: natpmp.h,v 1.20 2014/04/22 09:15:40 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2014, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __NATPMP_H__ +#define __NATPMP_H__ + +/* NAT-PMP Port as defined by the NAT-PMP draft */ +#define NATPMP_PORT (5351) + +#include +#if !defined(_MSC_VER) +#include +#endif /* !defined(_MSC_VER) */ + +#ifdef WIN32 +#include +#if !defined(_MSC_VER) || _MSC_VER >= 1600 +#include +#else /* !defined(_MSC_VER) || _MSC_VER >= 1600 */ +typedef unsigned long uint32_t; +typedef unsigned short uint16_t; +#endif /* !defined(_MSC_VER) || _MSC_VER >= 1600 */ +#define in_addr_t uint32_t +#include "declspec.h" +#else /* WIN32 */ +#define LIBSPEC +#include +#endif /* WIN32 */ + +/* causes problem when installing. Maybe should it be inlined ? */ +/* #include "declspec.h" */ + +typedef struct { + int s; /* socket */ + in_addr_t gateway; /* default gateway (IPv4) */ + int has_pending_request; + unsigned char pending_request[12]; + int pending_request_len; + int try_number; + struct timeval retry_time; +} natpmp_t; + +typedef struct { + uint16_t type; /* NATPMP_RESPTYPE_* */ + uint16_t resultcode; /* NAT-PMP response code */ + uint32_t epoch; /* Seconds since start of epoch */ + union { + struct { + //in_addr_t addr; + struct in_addr addr; + } publicaddress; + struct { + uint16_t privateport; + uint16_t mappedpublicport; + uint32_t lifetime; + } newportmapping; + } pnu; +} natpmpresp_t; + +/* possible values for type field of natpmpresp_t */ +#define NATPMP_RESPTYPE_PUBLICADDRESS (0) +#define NATPMP_RESPTYPE_UDPPORTMAPPING (1) +#define NATPMP_RESPTYPE_TCPPORTMAPPING (2) + +/* Values to pass to sendnewportmappingrequest() */ +#define NATPMP_PROTOCOL_UDP (1) +#define NATPMP_PROTOCOL_TCP (2) + +/* return values */ +/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */ +#define NATPMP_ERR_INVALIDARGS (-1) +/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */ +#define NATPMP_ERR_SOCKETERROR (-2) +/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */ +#define NATPMP_ERR_CANNOTGETGATEWAY (-3) +/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */ +#define NATPMP_ERR_CLOSEERR (-4) +/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */ +#define NATPMP_ERR_RECVFROM (-5) +/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while + * no NAT-PMP request was pending */ +#define NATPMP_ERR_NOPENDINGREQ (-6) +/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */ +#define NATPMP_ERR_NOGATEWAYSUPPORT (-7) +/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */ +#define NATPMP_ERR_CONNECTERR (-8) +/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */ +#define NATPMP_ERR_WRONGPACKETSOURCE (-9) +/* NATPMP_ERR_SENDERR : send() failed. check errno for details */ +#define NATPMP_ERR_SENDERR (-10) +/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */ +#define NATPMP_ERR_FCNTLERROR (-11) +/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */ +#define NATPMP_ERR_GETTIMEOFDAYERR (-12) + +/* */ +#define NATPMP_ERR_UNSUPPORTEDVERSION (-14) +#define NATPMP_ERR_UNSUPPORTEDOPCODE (-15) + +/* Errors from the server : */ +#define NATPMP_ERR_UNDEFINEDERROR (-49) +#define NATPMP_ERR_NOTAUTHORIZED (-51) +#define NATPMP_ERR_NETWORKFAILURE (-52) +#define NATPMP_ERR_OUTOFRESOURCES (-53) + +/* NATPMP_TRYAGAIN : no data available for the moment. try again later */ +#define NATPMP_TRYAGAIN (-100) + +#ifdef __cplusplus +extern "C" { +#endif + +/* initnatpmp() + * initialize a natpmp_t object + * With forcegw=1 the gateway is not detected automaticaly. + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SOCKETERROR + * NATPMP_ERR_FCNTLERROR + * NATPMP_ERR_CANNOTGETGATEWAY + * NATPMP_ERR_CONNECTERR */ +LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw); + +/* closenatpmp() + * close resources associated with a natpmp_t object + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_CLOSEERR */ +LIBSPEC int closenatpmp(natpmp_t * p); + +/* sendpublicaddressrequest() + * send a public address NAT-PMP request to the network gateway + * Return values : + * 2 = OK (size of the request) + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SENDERR */ +LIBSPEC int sendpublicaddressrequest(natpmp_t * p); + +/* sendnewportmappingrequest() + * send a new port mapping NAT-PMP request to the network gateway + * Arguments : + * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP, + * lifetime is in seconds. + * To remove a port mapping, set lifetime to zero. + * To remove all port mappings to the host, set lifetime and both ports + * to zero. + * Return values : + * 12 = OK (size of the request) + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SENDERR */ +LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol, + uint16_t privateport, uint16_t publicport, + uint32_t lifetime); + +/* getnatpmprequesttimeout() + * fills the timeval structure with the timeout duration of the + * currently pending NAT-PMP request. + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_GETTIMEOFDAYERR + * NATPMP_ERR_NOPENDINGREQ */ +LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout); + +/* readnatpmpresponseorretry() + * fills the natpmpresp_t structure if possible + * Return values : + * 0 = OK + * NATPMP_TRYAGAIN + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_NOPENDINGREQ + * NATPMP_ERR_NOGATEWAYSUPPORT + * NATPMP_ERR_RECVFROM + * NATPMP_ERR_WRONGPACKETSOURCE + * NATPMP_ERR_UNSUPPORTEDVERSION + * NATPMP_ERR_UNSUPPORTEDOPCODE + * NATPMP_ERR_NOTAUTHORIZED + * NATPMP_ERR_NETWORKFAILURE + * NATPMP_ERR_OUTOFRESOURCES + * NATPMP_ERR_UNSUPPORTEDOPCODE + * NATPMP_ERR_UNDEFINEDERROR */ +LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response); + +#ifdef ENABLE_STRNATPMPERR +LIBSPEC const char * strnatpmperr(int t); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ext-deps/libnatpmp-20150609/natpmpc.1 b/ext-deps/libnatpmp-20150609/natpmpc.1 new file mode 100644 index 000000000..5f0003da6 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/natpmpc.1 @@ -0,0 +1,19 @@ +.TH natpmpc 1 + +.SH NAME +natpmpc \- NAT\-PMP library test client and mapping setter. + +.SH "SYNOPSIS" +Display the public IP address: +.br +\fBnatpmpc\fP + +Add a port mapping: +.br +\fBnatpmpc\fP \-a [lifetime] + +.SH DESCRIPTION + +In order to remove a mapping, set it with a lifetime of 0 seconds. +To remove all mappings for your machine, use 0 as private port and +lifetime. diff --git a/ext-deps/libnatpmp-20150609/natpmpc.c b/ext-deps/libnatpmp-20150609/natpmpc.c new file mode 100644 index 000000000..611bd2d1a --- /dev/null +++ b/ext-deps/libnatpmp-20150609/natpmpc.c @@ -0,0 +1,244 @@ +/* $Id: natpmpc.c,v 1.13 2012/08/21 17:23:38 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2011, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#if defined(_MSC_VER) +#if _MSC_VER >= 1400 +#define strcasecmp _stricmp +#else +#define strcasecmp stricmp +#endif +#else +#include +#endif +#ifdef WIN32 +#include +#else +#include +#include +#endif +#include "natpmp.h" + +void usage(FILE * out, const char * argv0) +{ + fprintf(out, "Usage :\n"); + fprintf(out, " %s [options]\n", argv0); + fprintf(out, "\tdisplay the public IP address.\n"); + fprintf(out, " %s -h\n", argv0); + fprintf(out, "\tdisplay this help screen.\n"); + fprintf(out, " %s [options] -a [lifetime]\n", argv0); + fprintf(out, "\tadd a port mapping.\n"); + fprintf(out, "\nOption available :\n"); + fprintf(out, " -g ipv4address\n"); + fprintf(out, "\tforce the gateway to be used as destination for NAT-PMP commands.\n"); + fprintf(out, "\n In order to remove a mapping, set it with a lifetime of 0 seconds.\n"); + fprintf(out, " To remove all mappings for your machine, use 0 as private port and lifetime.\n"); +} + +/* sample code for using libnatpmp */ +int main(int argc, char * * argv) +{ + natpmp_t natpmp; + natpmpresp_t response; + int r; + int sav_errno; + struct timeval timeout; + fd_set fds; + int i; + int protocol = 0; + uint16_t privateport = 0; + uint16_t publicport = 0; + uint32_t lifetime = 3600; + int command = 0; + int forcegw = 0; + in_addr_t gateway = 0; + struct in_addr gateway_in_use; + +#ifdef WIN32 + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if(nResult != NO_ERROR) + { + fprintf(stderr, "WSAStartup() failed.\n"); + return -1; + } +#endif + + /* argument parsing */ + for(i=1; i i + 1) { + if(1 != sscanf(argv[i+1], "%u", &lifetime)) { + fprintf(stderr, "%s is not a correct 32bits unsigned integer\n", argv[i]); + } else { + i++; + } + } + break; + default: + fprintf(stderr, "Unknown option %s\n", argv[i]); + usage(stderr, argv[0]); + return 1; + } + } else { + fprintf(stderr, "Unknown option %s\n", argv[i]); + usage(stderr, argv[0]); + return 1; + } + } + + /* initnatpmp() */ + r = initnatpmp(&natpmp, forcegw, gateway); + printf("initnatpmp() returned %d (%s)\n", r, r?"FAILED":"SUCCESS"); + if(r<0) + return 1; + + gateway_in_use.s_addr = natpmp.gateway; + printf("using gateway : %s\n", inet_ntoa(gateway_in_use)); + + /* sendpublicaddressrequest() */ + r = sendpublicaddressrequest(&natpmp); + printf("sendpublicaddressrequest returned %d (%s)\n", + r, r==2?"SUCCESS":"FAILED"); + if(r<0) + return 1; + + do { + FD_ZERO(&fds); + FD_SET(natpmp.s, &fds); + getnatpmprequesttimeout(&natpmp, &timeout); + r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + if(r<0) { + fprintf(stderr, "select()"); + return 1; + } + r = readnatpmpresponseorretry(&natpmp, &response); + sav_errno = errno; + printf("readnatpmpresponseorretry returned %d (%s)\n", + r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED")); + if(r<0 && r!=NATPMP_TRYAGAIN) { +#ifdef ENABLE_STRNATPMPERR + fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n", + strnatpmperr(r)); +#endif + fprintf(stderr, " errno=%d '%s'\n", + sav_errno, strerror(sav_errno)); + } + } while(r==NATPMP_TRYAGAIN); + if(r<0) + return 1; + + /* TODO : check that response.type == 0 */ + printf("Public IP address : %s\n", inet_ntoa(response.pnu.publicaddress.addr)); + printf("epoch = %u\n", response.epoch); + + if(command == 'a') { + /* sendnewportmappingrequest() */ + r = sendnewportmappingrequest(&natpmp, protocol, + privateport, publicport, + lifetime); + printf("sendnewportmappingrequest returned %d (%s)\n", + r, r==12?"SUCCESS":"FAILED"); + if(r < 0) + return 1; + + do { + FD_ZERO(&fds); + FD_SET(natpmp.s, &fds); + getnatpmprequesttimeout(&natpmp, &timeout); + select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + r = readnatpmpresponseorretry(&natpmp, &response); + printf("readnatpmpresponseorretry returned %d (%s)\n", + r, r==0?"OK":(r==NATPMP_TRYAGAIN?"TRY AGAIN":"FAILED")); + } while(r==NATPMP_TRYAGAIN); + if(r<0) { +#ifdef ENABLE_STRNATPMPERR + fprintf(stderr, "readnatpmpresponseorretry() failed : %s\n", + strnatpmperr(r)); +#endif + return 1; + } + + printf("Mapped public port %hu protocol %s to local port %hu " + "liftime %u\n", + response.pnu.newportmapping.mappedpublicport, + response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ? "UDP" : + (response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ? "TCP" : + "UNKNOWN"), + response.pnu.newportmapping.privateport, + response.pnu.newportmapping.lifetime); + printf("epoch = %u\n", response.epoch); + } + + r = closenatpmp(&natpmp); + printf("closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED"); + if(r<0) + return 1; + + return 0; +} + diff --git a/ext-deps/libnatpmp-20150609/setup.py b/ext-deps/libnatpmp-20150609/setup.py new file mode 100644 index 000000000..aa774ee73 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/setup.py @@ -0,0 +1,18 @@ +#! /usr/bin/python +# $Id: setup.py,v 1.3 2012/03/05 04:54:01 nanard Exp $ +# +# python script to build the libnatpmp module under unix +# +# replace libnatpmp.a by libnatpmp.so for shared library usage +from distutils.core import setup, Extension +from distutils import sysconfig +sysconfig.get_config_vars()["OPT"] = '' +sysconfig.get_config_vars()["CFLAGS"] = '' +setup(name="libnatpmp", version="1.0", + ext_modules=[ + Extension(name="libnatpmp", sources=["libnatpmpmodule.c"], + extra_objects=["libnatpmp.a"], + define_macros=[('ENABLE_STRNATPMPERR', None)] + )] + ) + diff --git a/ext-deps/libnatpmp-20150609/setupmingw32.py b/ext-deps/libnatpmp-20150609/setupmingw32.py new file mode 100644 index 000000000..d02fdfcaa --- /dev/null +++ b/ext-deps/libnatpmp-20150609/setupmingw32.py @@ -0,0 +1,17 @@ +#! /usr/bin/python +# $Id: setupmingw32.py,v 1.3 2012/03/05 04:54:01 nanard Exp $ +# python script to build the miniupnpc module under windows +# +from distutils.core import setup, Extension +from distutils import sysconfig +sysconfig.get_config_vars()["OPT"] = '' +sysconfig.get_config_vars()["CFLAGS"] = '' +setup(name="libnatpmp", version="1.0", + ext_modules=[ + Extension(name="libnatpmp", sources=["libnatpmpmodule.c"], + libraries=["ws2_32"], + extra_objects=["libnatpmp.a"], + define_macros=[('ENABLE_STRNATPMPERR', None)] + )] + ) + diff --git a/ext-deps/libnatpmp-20150609/testgetgateway.c b/ext-deps/libnatpmp-20150609/testgetgateway.c new file mode 100644 index 000000000..24cbe7d05 --- /dev/null +++ b/ext-deps/libnatpmp-20150609/testgetgateway.c @@ -0,0 +1,57 @@ +/* $Id: testgetgateway.c,v 1.7 2012/08/21 17:13:31 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2011, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#ifdef WIN32 +#include +#else +#include +#include +#endif +#include "getgateway.h" + +int main(int argc, char * * argv) +{ + (void)argc; + (void)argv; + struct in_addr gatewayaddr; + int r; +#ifdef WIN32 + uint32_t temp = 0; + r = getdefaultgateway(&temp); + gatewayaddr.S_un.S_addr = temp; +#else + r = getdefaultgateway(&(gatewayaddr.s_addr)); +#endif + if(r>=0) + printf("default gateway : %s\n", inet_ntoa(gatewayaddr)); + else + fprintf(stderr, "getdefaultgateway() failed\n"); + return 0; +} + diff --git a/ext-deps/libnatpmp-20150609/wingettimeofday.c b/ext-deps/libnatpmp-20150609/wingettimeofday.c new file mode 100644 index 000000000..cb730e17d --- /dev/null +++ b/ext-deps/libnatpmp-20150609/wingettimeofday.c @@ -0,0 +1,60 @@ +/* $Id: wingettimeofday.c,v 1.6 2013/09/10 20:13:26 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2013, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifdef WIN32 +#if defined(_MSC_VER) +struct timeval { + long tv_sec; + long tv_usec; +}; +#else +#include +#endif + +typedef struct _FILETIME { + unsigned long dwLowDateTime; + unsigned long dwHighDateTime; +} FILETIME; + +void __stdcall GetSystemTimeAsFileTime(FILETIME*); + +int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */) { + union { + long long ns100; /*time since 1 Jan 1601 in 100ns units */ + FILETIME ft; + } _now; + + if(!p) + return -1; + GetSystemTimeAsFileTime( &(_now.ft) ); + p->tv_usec =(long)((_now.ns100 / 10LL) % 1000000LL ); + p->tv_sec = (long)((_now.ns100-(116444736000000000LL))/10000000LL); + return 0; +} +#endif + diff --git a/ext-deps/libnatpmp-20150609/wingettimeofday.h b/ext-deps/libnatpmp-20150609/wingettimeofday.h new file mode 100644 index 000000000..1d18d9fac --- /dev/null +++ b/ext-deps/libnatpmp-20150609/wingettimeofday.h @@ -0,0 +1,39 @@ +/* $Id: wingettimeofday.h,v 1.5 2013/09/11 07:22:25 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2013, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __WINGETTIMEOFDAY_H__ +#define __WINGETTIMEOFDAY_H__ +#ifdef WIN32 +#if defined(_MSC_VER) +#include +#else +#include +#endif +int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */); +#endif +#endif diff --git a/gpujpeg b/gpujpeg index d277a02f5..3d3731f30 160000 --- a/gpujpeg +++ b/gpujpeg @@ -1 +1 @@ -Subproject commit d277a02f517814c12279c55186af842a5564a18c +Subproject commit 3d3731f30c6e03683a4b08d718a24b7cd8028cec diff --git a/gui/QT/option/audio_opts.cpp b/gui/QT/option/audio_opts.cpp index f1cb976d5..df55c97c2 100644 --- a/gui/QT/option/audio_opts.cpp +++ b/gui/QT/option/audio_opts.cpp @@ -28,14 +28,14 @@ const char * const sdiAudioCards[] = { "decklink", "aja", "dvs", - "deltacast" + "deltacast", + "ndi", }; const char * const sdiAudio[] = { "analog", "AESEBU", "embedded", - "decklink" }; static std::vector> getSdiCond(const std::string &opt){ diff --git a/gui/QT/option/combobox_ui.cpp b/gui/QT/option/combobox_ui.cpp index 1aad76c22..82b2fd1a3 100644 --- a/gui/QT/option/combobox_ui.cpp +++ b/gui/QT/option/combobox_ui.cpp @@ -40,7 +40,8 @@ static bool conditionsSatisfied( for(const auto &condClause : conds){ bool orRes = false; for(const auto &condItem : condClause){ - bool val = condItem.value.val == settings->getOption(condItem.value.opt).getValue(); + //Check if condItem.value.val is the prefix of the currently set value + bool val = settings->getOption(condItem.value.opt).getValue().rfind(condItem.value.val, 0) == 0; //negate result if negation is true val = val != condItem.negation; diff --git a/gui/QT/option/settings.cpp b/gui/QT/option/settings.cpp index 1d98f6b77..f73e8f90f 100644 --- a/gui/QT/option/settings.cpp +++ b/gui/QT/option/settings.cpp @@ -214,6 +214,15 @@ const static struct{ {"preview", Option::BoolOpt, "", "t", true, "", ""}, {"vuMeter", Option::BoolOpt, "", "t", true, "", ""}, {"errors_fatal", Option::BoolOpt, " --param errors-fatal", "t", true, "", ""}, + {"aja.device", Option::StringOpt, ":", "", false, "video.source", "aja"}, + {"bluefish444.device", Option::StringOpt, ":", "", false, "video.source", "bluefish444"}, + {"deltacast.device", Option::StringOpt, ":", "", false, "video.source", "deltacast"}, + {"deltacast-dv.device", Option::StringOpt, ":", "", false, "video.source", "deltacast-dv"}, + {"dvs.device", Option::StringOpt, ":", "", false, "video.source", "dvs"}, + {"ndi.device", Option::StringOpt, ":", "", false, "video.source", "ndi"}, + {"spout.device", Option::StringOpt, ":", "", false, "video.source", "spout"}, + {"syphon.device", Option::StringOpt, ":", "", false, "video.source", "syphon"}, + {"ximea.device", Option::StringOpt, ":", "", false, "video.source", "ximea"}, }; const struct { @@ -275,7 +284,11 @@ std::string Settings::getLaunchParams() const{ out += getOption("audio.source").getLaunchOption(); out += getOption("audio.source.channels").getLaunchOption(); out += getOption("audio.compress").getLaunchOption(); - out += getOption("audio.playback").getLaunchOption(); + std::string audioPlay = getOption("audio.playback").getLaunchOption(); + if(audioPlay.empty() && getOption("preview").isEnabled()){ + audioPlay = " -r dummy"; + } + out += audioPlay; out += getOption("network.fec").getLaunchOption(); out += getOption("network.port").getLaunchOption(); out += getOption("network.control_port").getLaunchOption(); @@ -371,4 +384,4 @@ void Settings::changedAll(){ } } - +/* vim: set noexpandtab: */ diff --git a/gui/QT/option/video_opts.cpp b/gui/QT/option/video_opts.cpp index 77f0ddc3a..830c9bde1 100644 --- a/gui/QT/option/video_opts.cpp +++ b/gui/QT/option/video_opts.cpp @@ -6,11 +6,7 @@ std::vector getVideoSrc(AvailableSettings *availSettings){ const char * const whiteList[] = { - "aja", - "dvs", "bitflow", - "spout", - "syphon" }; const std::string optStr = "video.source"; diff --git a/gui/QT/window/ultragrid_window.cpp b/gui/QT/window/ultragrid_window.cpp index 519d36c6c..7aff4b6b3 100644 --- a/gui/QT/window/ultragrid_window.cpp +++ b/gui/QT/window/ultragrid_window.cpp @@ -282,16 +282,13 @@ void UltragridWindow::showSettings(){ void UltragridWindow::saveSettings(){ const auto &optMap = settings.getOptionMap(); - QFileDialog fileDialog(this); - fileDialog.setFileMode(QFileDialog::AnyFile); - fileDialog.setNameFilter(tr("Json (*.json)")); - fileDialog.setDefaultSuffix("json"); - QStringList fileNames; - if (fileDialog.exec()) - fileNames = fileDialog.selectedFiles(); + QString filename = QFileDialog::getSaveFileName(this, "Save Settings", "", "Json (*.json)"); + if(!filename.endsWith(QString(".json"))){ + filename.append(QString(".json")); + } - QFile outFile(fileNames.first()); + QFile outFile(filename); if (!outFile.open(QIODevice::WriteOnly)) { qWarning("Couldn't open save file."); @@ -344,6 +341,9 @@ void UltragridWindow::loadSettings(){ if (fileDialog.exec()) fileNames = fileDialog.selectedFiles(); + if(fileNames.empty()) + return; + loadSettingsFile(fileNames.first()); } diff --git a/src/audio/audio_playback.c b/src/audio/audio_playback.c index 540bb3de4..d85e2c06e 100644 --- a/src/audio/audio_playback.c +++ b/src/audio/audio_playback.c @@ -45,21 +45,22 @@ #include #include -#include "debug.h" -#include "host.h" - #include "audio/audio.h" #include "audio/audio_playback.h" #include "audio/playback/sdi.h" - +#include "debug.h" +#include "host.h" #include "lib_common.h" - +#include "tv.h" #include "video_display.h" /* flags */ struct state_audio_playback { char name[128]; const struct audio_playback_info *funcs; void *state; + + struct timeval t0; + long long int samples_played; }; void audio_playback_help(bool full) @@ -73,6 +74,7 @@ int audio_playback_init(const char *device, const char *cfg, struct state_audio_ struct state_audio_playback *s; s = calloc(1, sizeof(struct state_audio_playback)); + gettimeofday(&s->t0, NULL); s->funcs = load_library(device, LIBRARY_CLASS_AUDIO_PLAYBACK, AUDIO_PLAYBACK_ABI_VERSION); if (s->funcs == NULL) { @@ -111,10 +113,21 @@ struct state_audio_playback *audio_playback_init_null_device(void) void audio_playback_done(struct state_audio_playback *s) { - if(s) { - s->funcs->done(s->state); - free(s); + if (!s) { + return; } + + if (s->samples_played > 0) { + struct timeval t1; + gettimeofday(&t1, NULL); + + log_msg(LOG_LEVEL_INFO, "Played %lld audio samples in %f seconds (%f samples per second).\n", + s->samples_played, tv_diff(t1, s->t0), + s->samples_played / tv_diff(t1, s->t0)); + } + + s->funcs->done(s->state); + free(s); } unsigned int audio_playback_get_display_flags(struct state_audio_playback *s) @@ -135,6 +148,7 @@ unsigned int audio_playback_get_display_flags(struct state_audio_playback *s) void audio_playback_put_frame(struct state_audio_playback *s, struct audio_frame *frame) { + s->samples_played += frame->data_len / frame->ch_count / frame->bps; s->funcs->write(s->state, frame); } diff --git a/src/audio/codec/libavcodec.cpp b/src/audio/codec/libavcodec.cpp index acae2bf9d..ad98dd125 100644 --- a/src/audio/codec/libavcodec.cpp +++ b/src/audio/codec/libavcodec.cpp @@ -164,9 +164,7 @@ ADD_TO_PARAM("audioenc-frame-duration", "* audioenc-frame-duration=\n" static void *libavcodec_init(audio_codec_t audio_codec, audio_codec_direction_t direction, bool silent, int bitrate) { - if (log_level >= LOG_LEVEL_VERBOSE) { - av_log_set_level(AV_LOG_VERBOSE); - } + av_log_set_level((log_level - 1) * 8); enum AVCodecID codec_id = AV_CODEC_ID_NONE; diff --git a/src/audio/playback/alsa.c b/src/audio/playback/alsa.c index 6f6ec8f94..77feca0bb 100644 --- a/src/audio/playback/alsa.c +++ b/src/audio/playback/alsa.c @@ -103,9 +103,6 @@ struct state_alsa_playback { snd_pcm_t *handle; struct audio_desc desc; - struct timeval start_time; - long long int played_samples; - /* Local configuration with handle_underrun workaround set for PulseAudio ALSA plugin. Will be NULL if the PA ALSA plugin is not in use or the workaround is not required. */ @@ -834,8 +831,6 @@ static void * audio_play_alsa_init(const char *cfg) log_msg(LOG_LEVEL_WARNING, MOD_NAME "Async API is experimental, in case of problems use either \"thread\" or \"sync\" API\n"); } - gettimeofday(&s->start_time, NULL); - if(cfg && strlen(cfg) > 0) { if(strcmp(cfg, "help") == 0) { printf("Usage\n"); @@ -964,8 +959,6 @@ static void audio_play_alsa_write_frame(void *state, struct audio_frame *frame) signed2unsigned(frame->data, frame->data, frame->data_len); } - s->played_samples += frame->data_len / frame->bps / frame->ch_count; - int frames = frame->data_len / (frame->bps * frame->ch_count); rc = write_samples(s->handle, frame, frames, s->non_interleaved, s->playback_mode); if (rc == -EPIPE) { @@ -1038,8 +1031,6 @@ static void audio_play_alsa_done(void *state) { struct state_alsa_playback *s = (struct state_alsa_playback *) state; - struct timeval t; - if (s->playback_mode == THREAD && s->thread_started) { pthread_mutex_lock(&s->lock); s->should_exit_thread = true; @@ -1051,11 +1042,6 @@ static void audio_play_alsa_done(void *state) snd_async_del_handler(s->pcm_callback); } - gettimeofday(&t, NULL); - log_msg(LOG_LEVEL_INFO, MOD_NAME "Played %lld samples in %f seconds (%f samples per second).\n", - s->played_samples, tv_diff(t, s->start_time), - s->played_samples / tv_diff(t, s->start_time)); - snd_pcm_drain(s->handle); snd_pcm_close(s->handle); if (s->local_config) { diff --git a/src/audio/wav_reader.c b/src/audio/wav_reader.c index ab1973900..30e6b4000 100644 --- a/src/audio/wav_reader.c +++ b/src/audio/wav_reader.c @@ -61,7 +61,7 @@ static int read_fmt_chunk(FILE *wav_file, struct wav_metadata *metadata) uint16_t ch_count; READ_N(&ch_count, 2); - if (ch_count > 100) { + if (ch_count > 128) { return WAV_HDR_PARSE_INVALID_PARAM; } metadata->ch_count = ch_count; diff --git a/src/compat/platform_pipe.cpp b/src/compat/platform_pipe.cpp index 92beec118..afb14ae20 100644 --- a/src/compat/platform_pipe.cpp +++ b/src/compat/platform_pipe.cpp @@ -42,11 +42,24 @@ #include "config_win32.h" #include "compat/platform_pipe.h" +#include "rtp/net_udp.h" // socket_error #include using std::thread; +#ifdef _WIN32 +#define DECLARE_TIMEOUT(name, init_val_s) DWORD name = init_val_s * 1000 +typedef char *sockopt_t; +#else +#define DECLARE_TIMEOUT(name, init_val_s) struct timeval name = { init_val_s, 0 } +typedef void *sockopt_t; +#endif + +#ifndef HAVE_CONFIG_H // compiled outside of UltraGrid +#define socket_error(...) fprintf(stderr, __VA_ARGS__) +#endif + static fd_t open_socket(int *port) { fd_t sock; @@ -90,12 +103,26 @@ static fd_t connect_to_socket(int local_port) return INVALID_SOCKET; } int ret; + + DECLARE_TIMEOUT(timeout, 1); + DECLARE_TIMEOUT(old_timeout, 0); + socklen_t old_timeout_len = sizeof old_timeout; + if (getsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&old_timeout), &old_timeout_len) != 0) { + socket_error("pipe getsockopt"); + } + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&timeout), sizeof timeout) != 0) { + socket_error("pipe setsockopt"); + } ret = connect(fd, (struct sockaddr *) &s_in, sizeof(s_in)); if (ret != 0) { + socket_error("pipe connect"); CLOSESOCKET(fd); return INVALID_SOCKET; } + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&old_timeout), old_timeout_len) != 0) { + socket_error("pipe setsockopt"); + } return fd; } @@ -113,6 +140,17 @@ static void * worker(void *args) return NULL; } +/// @brief return regular pipe if available if our implementation fails +static int system_pipe(fd_t p[2]) +{ +#ifdef _WIN32 + (void) p; + return -1; +#else + fprintf(stderr, "Using native pipe instead of custom implementaton.\n"); + return pipe(p); +#endif +} int platform_pipe_init(fd_t p[2]) { @@ -149,23 +187,37 @@ int platform_pipe_init(fd_t p[2]) fd_t sock = open_socket(&par.port); if (sock == INVALID_SOCKET) { perror("open_socket"); - return -1; + return system_pipe(p); + } + + DECLARE_TIMEOUT(timeout, 1); + DECLARE_TIMEOUT(old_timeout, 0); + socklen_t old_timeout_len = sizeof old_timeout; + if (getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&old_timeout), &old_timeout_len) != 0) { + socket_error("pipe getsockopt"); + } + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&timeout), sizeof timeout) != 0) { + socket_error("pipe setsockopt"); } thread thr(worker, &par); p[0] = accept(sock, NULL, NULL); if (p[0] == INVALID_SOCKET) { - perror("accept"); + perror("pipe accept"); + thr.join(); CLOSESOCKET(sock); - return -1; + return system_pipe(p); } thr.join(); + if (setsockopt(p[0], SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&old_timeout), old_timeout_len) != 0) { + socket_error("pipe setsockopt"); + } p[1] = par.sock; if (p[1] == INVALID_SOCKET) { CLOSESOCKET(sock); perror("accept"); - return -1; + return system_pipe(p); } CLOSESOCKET(sock); diff --git a/src/compat/platform_pipe.h b/src/compat/platform_pipe.h index da3daed7a..34b0eaaa8 100644 --- a/src/compat/platform_pipe.h +++ b/src/compat/platform_pipe.h @@ -38,6 +38,9 @@ #ifndef platform_pipe_h #define platform_pipe_h +#include "config_unix.h" +#include "config_win32.h" + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -45,8 +48,23 @@ extern "C" { int platform_pipe_init(fd_t pipe[2]); void platform_pipe_close(fd_t pipe); +#ifdef _WIN32 +#define PLATFORM_PIPE_READ(fd, buf, len) recv(fd, buf, len, 0) +#define PLATFORM_PIPE_WRITE(fd, buf, len) send(fd, buf, len, 0) +#else +// the fallback implementation of platform_pipe is a plain pipe for which +// doesn't work send()/recv(). read() and write(), however, works for both +// socket and pipe (but doesn't so in Windows, therefore there is used +// send()/recv()). +// +// PLATFORM_PIPE_READ()/PLATFORM_PIPE_WRITE() can be also safely be used if +// unsure if underlying fd is a socket or a pipe. +#define PLATFORM_PIPE_READ read +#define PLATFORM_PIPE_WRITE write +#endif + #ifdef __cplusplus } #endif // __cplusplus -#endif /* _PLATFORM_SEMAPHORE_H */ +#endif /* defined platform_pipe_h */ diff --git a/src/control_socket.cpp b/src/control_socket.cpp index 501be6918..786c66b65 100644 --- a/src/control_socket.cpp +++ b/src/control_socket.cpp @@ -806,8 +806,8 @@ static void * control_thread(void *args) while(cur) { if(FD_ISSET(cur->fd, &fds)) { - ssize_t ret = recv(cur->fd, cur->buff + cur->buff_len, - sizeof(cur->buff) - cur->buff_len, 0); + ssize_t ret = PLATFORM_PIPE_READ(cur->fd, cur->buff + cur->buff_len, + sizeof(cur->buff) - cur->buff_len); if(ret == -1) { fprintf(stderr, "Error reading socket, closing!!!\n"); } diff --git a/src/debug.cpp b/src/debug.cpp index 67899a681..38bb52c16 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -179,3 +179,84 @@ void debug_dump(void *lp, int len) start = i; /* next line starting byte */ } } + +bool set_log_level(const char *optarg, bool *logger_repeat_msgs) { + assert(optarg != nullptr); + assert(logger_repeat_msgs != nullptr); + + using namespace std::string_literals; + using std::clog; + using std::cout; + + static const struct { const char *name; int level; } mapping[] = { + { "quiet", LOG_LEVEL_QUIET }, + { "fatal", LOG_LEVEL_FATAL }, + { "error", LOG_LEVEL_ERROR }, + { "warning", LOG_LEVEL_WARNING}, + { "notice", LOG_LEVEL_NOTICE}, + { "info", LOG_LEVEL_INFO }, + { "verbose", LOG_LEVEL_VERBOSE}, + { "debug", LOG_LEVEL_DEBUG }, + { "debug2", LOG_LEVEL_DEBUG2 }, + }; + + if ("help"s == optarg) { + cout << "log level: [0-" << LOG_LEVEL_MAX; + for (auto m : mapping) { + cout << "|" << m.name; + } + cout << "][+repeat]\n"; + cout << "\trepeat - print repeating log messages\n"; + return false; + } + + if (strstr(optarg, "+repeat") != nullptr) { + *logger_repeat_msgs = true; + } + + if (optarg[0] == '+') { + return true; + } + + if (isdigit(optarg[0])) { + long val = strtol(optarg, nullptr, 0); + if (val < 0 || val > LOG_LEVEL_MAX) { + clog << "Log: wrong value: " << log_level << "\n"; + return false; + } + log_level = val; + return true; + } + + for (auto m : mapping) { + if (strstr(optarg, m.name) == optarg) { + log_level = m.level; + return true; + } + } + + LOG(LOG_LEVEL_ERROR) << "Wrong log level specification: " << optarg << "\n"; + return false; +} + +void Logger::preinit(bool skip_repeated) +{ + Logger::skip_repeated = skip_repeated; + if (rang::rang_implementation::supportsColor() + && rang::rang_implementation::isTerminal(std::cout.rdbuf()) + && rang::rang_implementation::isTerminal(std::cerr.rdbuf())) { + // force ANSI sequences even when written to ostringstream + rang::setControlMode(rang::control::Force); +#ifdef _WIN32 + // ANSI control sequences need to be explicitly set in Windows + if (rang::rang_implementation::setWinTermAnsiColors(std::cout.rdbuf()) && + rang::rang_implementation::setWinTermAnsiColors(std::cerr.rdbuf())) { + rang::setWinTermMode(rang::winTerm::Ansi); + } +#endif + } +} + +std::atomic Logger::last_msg{}; +std::atomic Logger::skip_repeated{true}; + diff --git a/src/debug.h b/src/debug.h index d42526875..e342c09a4 100644 --- a/src/debug.h +++ b/src/debug.h @@ -40,6 +40,10 @@ #ifndef _RAT_DEBUG_H #define _RAT_DEBUG_H +#ifndef __cplusplus +#include +#endif // ! defined __cplusplus + #define UNUSED(x) (x=x) #define LOG_LEVEL_QUIET 0 ///< suppress all logging @@ -74,48 +78,83 @@ void debug_dump(void*lp, int len); #define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, __VA_ARGS__) void log_msg(int log_level, const char *format, ...) ATTRIBUTE(format (printf, 2, 3)); +bool set_log_level(const char *optarg, bool *logger_repeat_msgs); + #ifdef __cplusplus } #endif #ifdef __cplusplus +#include #include #include +#include #include "compat/platform_time.h" #include "rang.hpp" +class keyboard_control; // friend + // Log, version 0.1: a simple logging class class Logger { public: + static void preinit(bool skip_repeated); inline Logger(int l) : level(l) {} inline ~Logger() { - std::cerr << rang::style::reset << rang::fg::reset; - } - inline std::ostream& Get() { rang::fg color = rang::fg::reset; rang::style style = rang::style::reset; + std::string msg = oss.str(); + + if (skip_repeated && rang::rang_implementation::isTerminal(std::clog.rdbuf())) { + auto last = last_msg.exchange(nullptr); + if (last != nullptr && last->msg == msg) { + int count = last->count += 1; + auto current = last_msg.exchange(last); + delete current; + std::clog << " Last message repeated " << count << " times\r"; + return; + } + if (last != nullptr && last->count > 0) { + std::clog << "\n"; + delete last; + } + } + switch (level) { case LOG_LEVEL_FATAL: color = rang::fg::red; style = rang::style::bold; break; case LOG_LEVEL_ERROR: color = rang::fg::red; break; case LOG_LEVEL_WARNING: color = rang::fg::yellow; break; case LOG_LEVEL_NOTICE: color = rang::fg::green; break; } - std::cerr << style << color; + + std::ostringstream timestamp; if (log_level >= LOG_LEVEL_VERBOSE) { - unsigned long long time_ms = time_since_epoch_in_ms(); - auto flags = std::cerr.flags(); - auto precision = std::cerr.precision(); - std::cerr << "[" << std::fixed << std::setprecision(3) << time_ms / 1000.0 << "] "; - std::cerr.precision(precision); - std::cerr.flags(flags); + auto time_ms = time_since_epoch_in_ms(); + timestamp << "[" << std::fixed << std::setprecision(3) << time_ms / 1000.0 << "] "; } - return std::cerr; + std::clog << style << color << timestamp.str() << msg << rang::style::reset << rang::fg::reset; + + auto *lmsg = new last_message{std::move(msg)}; + auto current = last_msg.exchange(lmsg); + delete current; + } + inline std::ostream& Get() { + return oss; } private: int level; + std::ostringstream oss; + + static std::atomic skip_repeated; + struct last_message { + std::string msg; + int count{0}; + }; + static std::atomic last_msg; // leaks last message upon exit + + friend class keyboard_control; }; #define LOG(level) \ diff --git a/src/hd-rum-translator/hd-rum-translator.cpp b/src/hd-rum-translator/hd-rum-translator.cpp index e67ae1d3b..9b614a986 100644 --- a/src/hd-rum-translator/hd-rum-translator.cpp +++ b/src/hd-rum-translator/hd-rum-translator.cpp @@ -696,7 +696,7 @@ int main(int argc, char **argv) int i; struct cmdline_parameters params; - if ((init = common_preinit(argc, argv)) == nullptr) { + if ((init = common_preinit(argc, argv, nullptr)) == nullptr) { EXIT(EXIT_FAILURE); } diff --git a/src/host.cpp b/src/host.cpp index 00a3d3e72..a7363d7e1 100644 --- a/src/host.cpp +++ b/src/host.cpp @@ -44,6 +44,8 @@ #include "config_win32.h" #endif +#include + #include "host.h" #include "audio/audio_capture.h" @@ -184,13 +186,20 @@ static int x11_error_handler(Display *d, XErrorEvent *e) { } #endif -struct init_data *common_preinit(int argc, char *argv[]) +struct init_data *common_preinit(int argc, char *argv[], const char *log_opt) { struct init_data *init; + bool logger_repeat_msgs = false; uv_argc = argc; uv_argv = argv; + if (log_opt != nullptr && !set_log_level(log_opt, &logger_repeat_msgs)) { + return nullptr; + } + + Logger::preinit(!logger_repeat_msgs); + #ifdef HAVE_X void *handle = dlopen(X11_LIB_NAME, RTLD_NOW); diff --git a/src/host.h b/src/host.h index 187826f80..e927b58f0 100644 --- a/src/host.h +++ b/src/host.h @@ -122,7 +122,7 @@ void set_audio_delay(int val); #define RATE_FLAG_FIXED_RATE (1ll<<62ll) ///< use the bitrate as fixed, not capped struct init_data; -struct init_data *common_preinit(int argc, char *argv[]); +struct init_data *common_preinit(int argc, char *argv[], const char *log_opt); void common_cleanup(struct init_data *init_data); /** diff --git a/src/keyboard_control.cpp b/src/keyboard_control.cpp index 69691fd45..35bd550bc 100644 --- a/src/keyboard_control.cpp +++ b/src/keyboard_control.cpp @@ -121,7 +121,7 @@ keyboard_control::keyboard_control(struct module *parent) : m_should_exit(false), m_started(false), m_locked_against_changes(true), - guarded_keys { '*', '/', '9', '0', 'c', 'C', 'm', 'M', '>', '<', 'e', 's', 'S', 'v', 'V' } + guarded_keys { '*', '/', '9', '0', 'c', 'C', 'm', 'M', '>', '<', 'e', 's', 'S', 'v', 'V', 'r' } { m_start_time = time(NULL); @@ -540,6 +540,10 @@ void keyboard_control::run() cout << "Log level: " << log_level << "\n"; break; } + case 'r': + Logger::skip_repeated = !Logger::skip_repeated; + cout << "Skip repeated messages: " << std::boolalpha << Logger::skip_repeated << std::noboolalpha << "\n"; + break; case 's': if (saved_log_level == -1) { saved_log_level = log_level; @@ -672,6 +676,7 @@ void keyboard_control::usage() BOLD("\t M ") << "- mute/unmute sender" << G('M') << "\n" << BOLD("\t v ") << "- increase verbosity level" << G('v') << "\n" << BOLD("\t V ") << "- decrease verbosity level" << G('V') << "\n" << + BOLD("\t r ") << "- skip repeated messages (toggle) " << G('r') << "\n" << BOLD("\t e ") << "- record captured content (toggle)" << G('e') << "\n" << BOLD("\t h ") << "- show help" << G('h') << "\n" << BOLD("\t i ") << "- show various information" << G('i') << "\n" << diff --git a/src/main.cpp b/src/main.cpp index 2f20829ef..159d80817 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,6 +89,7 @@ #include "ug_runtime_error.hpp" #include "utils/color_out.h" #include "utils/misc.h" +#include "utils/nat.h" #include "utils/net.h" #include "utils/thread.h" #include "utils/wait_obj.h" @@ -133,7 +134,6 @@ static constexpr const char *DEFAULT_AUDIO_CODEC = "PCM"; #define OPT_PIX_FMTS (('P' << 8) | 'F') #define OPT_PROTOCOL (('P' << 8) | 'R') #define OPT_START_PAUSED (('S' << 8) | 'P') -#define OPT_VERBOSE (('V' << 8) | 'E') #define OPT_VIDEO_CODECS (('V' << 8) | 'C') #define OPT_VIDEO_PROTOCOL (('V' << 8) | 'P') #define OPT_WINDOW_TITLE (('W' << 8) | 'T') @@ -173,7 +173,9 @@ struct state_uv { static void should_exit_watcher(state_uv *s) { set_thread_name(__func__); char c; - while (recv(s->should_exit_pipe[0], &c, 1, 0) != 1) perror("recv"); + while (PLATFORM_PIPE_READ(s->should_exit_pipe[0], &c, 1) != 1) { + perror("PLATFORM_PIPE_READ"); + } unique_lock lk(s->lock); for (auto c : s->should_exit_callbacks) { get<0>(c)(get<1>(c)); @@ -186,7 +188,9 @@ struct state_uv { return; } should_exit_thread_notified = true; - while (send(should_exit_pipe[1], &c, 1, 0) != 1) perror("send"); + while (PLATFORM_PIPE_WRITE(should_exit_pipe[1], &c, 1) != 1) { + perror("PLATFORM_PIPE_WRITE"); + } } static void new_message(struct module *mod) { @@ -374,6 +378,7 @@ static void usage(const char *exec_path, bool full = false) print_help_item("-M ", {"received video mode (eg tiled-4K, 3D,", "dual-link)"}); print_help_item("-p | help", {"postprocess module"}); + print_help_item("-N|--nat-traverse"s, {"try to deploy NAT traversal techniques"s}); } print_help_item("-f [A:|V:]", {"FEC settings (audio or video) - use", "\"none\", \"mult:\",", "\"ldgm:%%\" or", "\"ldgm:::\"", @@ -716,7 +721,7 @@ int main(int argc, char *argv[]) {"capture-filter", required_argument, 0, OPT_CAPTURE_FILTER}, {"control-port", required_argument, 0, OPT_CONTROL_PORT}, {"encryption", required_argument, 0, OPT_ENCRYPTION}, - {"verbose", optional_argument, 0, OPT_VERBOSE}, + {"verbose", optional_argument, nullptr, 'V'}, {"window-title", required_argument, 0, OPT_WINDOW_TITLE}, {"capabilities", no_argument, 0, OPT_CAPABILITIES}, {"audio-delay", required_argument, 0, OPT_AUDIO_DELAY}, @@ -729,9 +734,10 @@ int main(int argc, char *argv[]) {"param", required_argument, 0, OPT_PARAM}, {"pix-fmts", no_argument, 0, OPT_PIX_FMTS}, {"video-codecs", no_argument, 0, OPT_VIDEO_CODECS}, + {"nat-traverse", no_argument, nullptr, 'N'}, {0, 0, 0, 0} }; - const char optstring[] = "d:t:m:r:s:v46c:hM:p:f:P:l:A:"; + const char *optstring = "d:t:m:r:s:v46c:hM:Np:f:P:l:A:V"; const char *audio_protocol = "ultragrid_rtp"; const char *audio_protocol_opts = ""; @@ -739,17 +745,21 @@ int main(int argc, char *argv[]) const char *video_protocol = "ultragrid_rtp"; const char *video_protocol_opts = ""; + const char *log_opt = nullptr; + bool setup_nat_traverse = false; + struct ug_nat_traverse *nat_traverse = nullptr; + // First we need to set verbosity level prior to everything else. // common_preinit() uses the verbosity level. while ((ch = getopt_long(argc, argv, optstring, getopt_options, NULL)) != -1) { switch (ch) { - case OPT_VERBOSE: + case 'V': if (optarg) { - log_level = atoi(optarg); + log_opt = optarg; } else { - log_level = LOG_LEVEL_VERBOSE; + log_level += 1; } break; default: @@ -758,7 +768,7 @@ int main(int argc, char *argv[]) } optind = 1; - if ((init = common_preinit(argc, argv)) == nullptr) { + if ((init = common_preinit(argc, argv, log_opt)) == nullptr) { log_msg(LOG_LEVEL_FATAL, "common_preinit() failed!\n"); EXIT(EXIT_FAILURE); } @@ -1057,7 +1067,7 @@ int main(int argc, char *argv[]) connection_type = 0; } break; - case OPT_VERBOSE: + case 'V': break; // already handled earlier case OPT_WINDOW_TITLE: log_msg(LOG_LEVEL_WARNING, "Deprecated option used, please use " @@ -1087,6 +1097,9 @@ int main(int argc, char *argv[]) case OPT_VIDEO_CODECS: print_video_codecs(); EXIT(EXIT_SUCCESS); + case 'N': + setup_nat_traverse = true; + break; case '?': default: usage(uv_argv[0]); @@ -1126,7 +1139,7 @@ int main(int argc, char *argv[]) audio_codec = "OPUS:sample_rate=48000"; } if (requested_compression == nullptr) { - requested_compression = "none"; // will be set later + requested_compression = "none"; // will be set later by h264_sdp_video_rxtx::send_frame() } if (force_ip_version == 0 && strcasecmp(video_protocol, "rtsp") == 0) { force_ip_version = 4; @@ -1236,6 +1249,10 @@ int main(int argc, char *argv[]) EXIT(EXIT_FAIL_CONTROL_SOCK); } + if (setup_nat_traverse) { + nat_traverse = start_nat_traverse(video_rx_port, audio_rx_port); + } + uv.audio = audio_cfg_init (&uv.root_module, audio_host, audio_rx_port, audio_tx_port, audio_send, audio_recv, audio_protocol, audio_protocol_opts, @@ -1502,6 +1519,8 @@ cleanup: if (uv.display_device) display_done(uv.display_device); + stop_nat_traverse(nat_traverse); + kc.stop(); control_done(control); diff --git a/src/rang.hpp b/src/rang.hpp index f3eaa56fc..de5646fad 100644 --- a/src/rang.hpp +++ b/src/rang.hpp @@ -174,9 +174,9 @@ namespace rang_implementation { { // Dynamic load for binary compability with old Windows const auto ptrGetFileInformationByHandleEx - = reinterpret_cast( + = reinterpret_cast(reinterpret_cast( GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), - "GetFileInformationByHandleEx")); + "GetFileInformationByHandleEx"))); if (!ptrGetFileInformationByHandleEx) { return false; } diff --git a/src/rtp/net_udp.cpp b/src/rtp/net_udp.cpp index 1d98bec2a..34ca19c30 100644 --- a/src/rtp/net_udp.cpp +++ b/src/rtp/net_udp.cpp @@ -967,7 +967,7 @@ void udp_exit(socket_udp * s) if (!s->local_is_slave) { if (s->local->multithreaded) { char c = 0; - int ret = send(s->local->should_exit_fd[1], &c, 1, 0); + int ret = PLATFORM_PIPE_WRITE(s->local->should_exit_fd[1], &c, 1); assert (ret == 1); s->local->should_exit = true; s->local->reader_cv.notify_one(); diff --git a/src/spout_receiver.cpp b/src/spout_receiver.cpp deleted file mode 100644 index c0417aaa1..000000000 --- a/src/spout_receiver.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file spout_receiver.cpp - * @author Martin Pulec - */ -/* - * Copyright (c) 2018 CESNET, z. s. p. o. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, is permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of CESNET nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, - * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "SpoutSDK/Spout.h" - -#include "spout_receiver.h" - -void *spout_create_receiver(char *name, unsigned int *width, unsigned int *height) { - SpoutReceiver *receiver = new SpoutReceiver; - receiver->CreateReceiver(name, *width, *height); - bool connected; - receiver->CheckReceiver(name, *width, *height, connected); - if (!connected) { - fprintf(stderr, "[SPOUT] Not connected to server '%s'. Is it running?\n", name); - receiver->ReleaseReceiver(); - delete receiver; - return NULL; - } - - return receiver; -} - -bool spout_receiver_recvframe(void *s, char *sender_name, unsigned int width, unsigned int height, char *data, GLenum glFormat) { - return ((SpoutReceiver *)s)->ReceiveImage(sender_name, width, height, (unsigned char *) data, glFormat); -} - -void spout_receiver_delete(void *s) { - if (!s) { - return; - } - ((SpoutReceiver *)s)->ReleaseReceiver(); - delete s; -} - diff --git a/src/spout_sender.cpp b/src/spout_sender.cpp index 932e014a8..7cebdd7bb 100644 --- a/src/spout_sender.cpp +++ b/src/spout_sender.cpp @@ -34,7 +34,7 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "SpoutSDK/Spout.h" +#include #include "spout_sender.h" diff --git a/src/spout_sender.h b/src/spout_sender.h index a09da5860..d9fd5dbde 100644 --- a/src/spout_sender.h +++ b/src/spout_sender.h @@ -44,19 +44,9 @@ extern "C" { #endif // __cplusplus -#if defined _MSC_VER || defined __MINGW32__ -#ifdef EXPORT_DLL_SYMBOLS -#define SPOUT_SENDER_DLL_API __declspec(dllexport) -#else -#define SPOUT_SENDER_DLL_API __declspec(dllimport) -#endif -#else // other platforms -#define SPOUT_SENDER_DLL_API -#endif - -SPOUT_SENDER_DLL_API void *spout_sender_register(const char *name, int width, int height); -SPOUT_SENDER_DLL_API void spout_sender_sendframe(void *s, int width, int height, unsigned int id); -SPOUT_SENDER_DLL_API void spout_sender_unregister(void *s); +void *spout_sender_register(const char *name, int width, int height); +void spout_sender_sendframe(void *s, int width, int height, unsigned int id); +void spout_sender_unregister(void *s); #ifdef __cplusplus } diff --git a/src/transmit.cpp b/src/transmit.cpp index 8db1a90b6..a730298ea 100644 --- a/src/transmit.cpp +++ b/src/transmit.cpp @@ -284,7 +284,7 @@ static bool set_fec(struct tx *tx, const char *fec_const) } } else if(strcasecmp(fec, "RS") == 0) { if(tx->media_type == TX_MEDIA_AUDIO) { - fprintf(stderr, "LDGM is not currently supported for audio!\n"); + fprintf(stderr, "Reed–Solomon is not currently supported for audio!\n"); ret = false; } else { snprintf(msg->fec_cfg, sizeof(msg->fec_cfg), "RS cfg %s", @@ -651,7 +651,7 @@ tx_send_base(struct tx *tx, struct video_frame *frame, struct rtp *rtp_session, if (tx->bitrate == RATE_AUTO) { // adaptive (spread packets to 75% frame time) packet_rate = packet_rate_auto; } else { // bitrate given manually - long long int bitrate = tx->bitrate | RATE_FLAG_FIXED_RATE; + long long int bitrate = tx->bitrate & ~RATE_FLAG_FIXED_RATE; int avg_packet_size = tile->data_len / packet_count; packet_rate = 1000ll * 1000 * 1000 * avg_packet_size * 8 / bitrate; // fixed rate if ((tx->bitrate & RATE_FLAG_FIXED_RATE) == 0) { // adaptive capped rate @@ -772,26 +772,22 @@ void audio_tx_send(struct tx* tx, struct rtp *rtp_session, const audio_frame2 * int mult_pos[FEC_MAX_MULT]; int mult_index = 0; int mult_first_sent = 0; - int rtp_hdr_len; fec_check_messages(tx); timestamp = get_local_mediatime(); perf_record(UVP_SEND, timestamp); - if(tx->encryption) { - rtp_hdr_len = sizeof(crypto_payload_hdr_t) + sizeof(audio_payload_hdr_t); + int rtp_hdr_len = sizeof(audio_payload_hdr_t); + int hdrs_len = (rtp_is_ipv6(rtp_session) ? 40 : 20) + 8 + 12 + sizeof(audio_payload_hdr_t); // MTU - IP hdr - UDP hdr - RTP hdr - payload_hdr + if (tx->encryption) { + hdrs_len += sizeof(crypto_payload_hdr_t) + tx->enc_funcs->get_overhead(tx->encryption); + rtp_hdr_len += sizeof(crypto_payload_hdr_t); pt = PT_ENCRYPT_AUDIO; } else { - rtp_hdr_len = sizeof(audio_payload_hdr_t); pt = PT_AUDIO; /* PT set for audio in our packet format */ } - int hdrs_len = (rtp_is_ipv6(rtp_session) ? 40 : 20) + 8 + 12 + sizeof(audio_payload_hdr_t); // MTU - IP hdr - UDP hdr - RTP hdr - payload_hdr - if(tx->encryption) { - hdrs_len += sizeof(crypto_payload_hdr_t); - } - for(channel = 0; channel < buffer->get_channel_count(); ++channel) { chan_data = buffer->get_data(channel); diff --git a/src/utils/misc.c b/src/utils/misc.c index 88d769000..ad625a40f 100644 --- a/src/utils/misc.c +++ b/src/utils/misc.c @@ -183,3 +183,109 @@ void replace_all(char *in, const char *from, const char *to) { } } +int urlencode_html5_eval(int c) +{ + return isalnum(c) || c == '*' || c == '-' || c == '.' || c == '_'; +} + +int urlencode_rfc3986_eval(int c) +{ + return isalnum(c) || c == '~' || c == '-' || c == '.' || c == '_'; +} + +/** + * Replaces all occurences where eval() evaluates to true with %-encoding + * @param in input + * @param out output array + * @param max_len maximal lenght to be written (including terminating NUL) + * @param eval_pass predictor if an input character should be kept (functions + * from ctype.h may be used) + * @param space_plus_replace replace spaces (' ') with ASCII plus sign - + * should be true for HTML5 URL encoding, false for RFC 3986 + * @returns bytes written to out + * + * @note + * Symbol ' ' is not treated specially (unlike in classic URL encoding which + * translates it to '+'. + * @todo + * There may be a LUT as in https://rosettacode.org/wiki/URL_encoding#C + */ +size_t urlencode(char *out, size_t max_len, const char *in, int (*eval_pass)(int c), + bool space_plus_replace) +{ + if (max_len == 0 || max_len >= INT_MAX) { // prevent overflow + return 0; + } + size_t len = 0; + while (*in && len < max_len - 1) { + if (*in == ' ' && space_plus_replace) { + *out++ = '+'; + in++; + } else if (eval_pass(*in) != 0) { + *out++ = *in++; + len++; + } else { + if ((int) len < (int) max_len - 3 - 1) { + int ret = sprintf(out, "%%%02X", *in++); + out += ret; + len += ret; + } else { + break; + } + } + } + *out = '\0'; + len++; + + return len; +} + +static inline int ishex(int x) +{ + return (x >= '0' && x <= '9') || + (x >= 'a' && x <= 'f') || + (x >= 'A' && x <= 'F'); +} + +/** + * URL decodes input string (replaces all "%XX" sequences with ASCII representation of 0xXX) + * @param in input + * @param out output array + * @param max_len maximal lenght to be written (including terminating NUL) + * @returns bytes written, 0 on error + * + * @note + * Symbol '+' is not treated specially (unlike in classic URL decoding which + * translates it to ' '. + */ +size_t urldecode(char *out, size_t max_len, const char *in) +{ + if (max_len == 0) { // avoid (uint) -1 cast + return 0; + } + size_t len = 0; + while (*in && len < max_len - 1) { + if (*in == '+') { + *out++ = ' '; + in++; + } else if (*in != '%') { + *out++ = *in++; + } else { + in++; // skip '%' + if (!ishex(in[0]) || !ishex(in[1])) { + return 0; + } + unsigned int c = 0; + if (sscanf(in, "%2x", &c) != 1) { + return 0; + } + *out++ = c; + in += 2; + } + len++; + } + *out = '\0'; + len++; + + return len; +} diff --git a/src/utils/misc.h b/src/utils/misc.h index ce85aefa2..76aa2eda4 100644 --- a/src/utils/misc.h +++ b/src/utils/misc.h @@ -38,7 +38,12 @@ #ifndef UTILS_MISC_H_ #define UTILS_MISC_H_ +#ifdef __cplusplus +#include +#else #include +#include +#endif #ifdef __cplusplus extern "C" { @@ -53,6 +58,11 @@ int get_framerate_d(double framerate); #define ESCAPED_COLON "\\:" void replace_all(char *in, const char *from, const char *to); +int urlencode_html5_eval(int c); +int urlencode_rfc3986_eval(int c); +size_t urlencode(char *out, size_t max_len, const char *in, int (*eval_pass)(int c), bool space_plus_replace); +size_t urldecode(char *out, size_t max_len, const char *in); + /** * @brief Creates FourCC word * diff --git a/src/utils/nat.c b/src/utils/nat.c new file mode 100644 index 000000000..988a7c1e7 --- /dev/null +++ b/src/utils/nat.c @@ -0,0 +1,204 @@ +/** + * @file utils/nat.c + * @author Martin Pulec + */ +/* + * Copyright (c) 2020 CESNET z.s.p.o. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "config_unix.h" +#include "config_win32.h" + +#include "debug.h" +#include "utils/nat.h" + +#define ENABLE_STRNATPMPERR 1 +#define STATICLIB 1 +#include "ext-deps/libnatpmp-20150609/natpmp.h" + +#define ALLOCATION_TIMEOUT (4 * 3600) + +struct ug_nat_traverse { + enum { + UG_NAT_TRAVERSE_NONE, + UG_NAT_TRAVERSE_NAT_PMP, + } traverse; + int audio_rx_port; + int video_rx_port; +}; + +static bool nat_pmp_add_mapping(natpmp_t *natpmp, int privateport, int publicport, int lifetime) +{ + if (privateport == 0 && publicport == 0) { + return true; + } + + int r = 0; + /* sendnewportmappingrequest() */ + r = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_UDP, + privateport, publicport, + lifetime); + log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, + "[NAT PMP] sendnewportmappingrequest returned %d (%s)\n", + r, r == 12 ? "SUCCESS" : "FAILED"); + if (r < 0) { + return false; + } + + natpmpresp_t response; + do { + fd_set fds; + struct timeval timeout; + FD_ZERO(&fds); + FD_SET(natpmp->s, &fds); + getnatpmprequesttimeout(natpmp, &timeout); + select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + r = readnatpmpresponseorretry(natpmp, &response); + log_msg(LOG_LEVEL_VERBOSE, "[NAT PMP] readnatpmpresponseorretry returned %d (%s)\n", + r, r == 0 ? "OK" : (r == NATPMP_TRYAGAIN ? "TRY AGAIN" : "FAILED")); + } while(r==NATPMP_TRYAGAIN); + if(r<0) { + log_msg(LOG_LEVEL_ERROR, "[NAT PMP] readnatpmpresponseorretry() failed : %s\n", + strnatpmperr(r)); + return false; + } + + log_msg(LOG_LEVEL_INFO, "[NAT PMP] Mapped public port %hu protocol %s to local port %hu " + "liftime %u\n", + response.pnu.newportmapping.mappedpublicport, + response.type == NATPMP_RESPTYPE_UDPPORTMAPPING ? "UDP" : + (response.type == NATPMP_RESPTYPE_TCPPORTMAPPING ? "TCP" : + "UNKNOWN"), + response.pnu.newportmapping.privateport, + response.pnu.newportmapping.lifetime); + log_msg(LOG_LEVEL_DEBUG, "[NAT PMP] epoch = %u\n", response.epoch); + + return true; +} + +static bool setup_nat_pmp(int video_rx_port, int audio_rx_port, int lifetime) +{ + struct in_addr gateway_in_use = { 0 }; + natpmp_t natpmp; + int r = 0; + r = initnatpmp(&natpmp, 0, 0); + log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, "[NAT PMP] initnatpmp returned %d (%s)\n", r, + r ? "FAILED" : "SUCCESS"); + if (r < 0) { + return false; + } + gateway_in_use.s_addr = natpmp.gateway; + log_msg(LOG_LEVEL_NOTICE, "[NAT PMP] using gateway: %s\n", inet_ntoa(gateway_in_use)); + + /* sendpublicaddressrequest() */ + r = sendpublicaddressrequest(&natpmp); + log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, "[NAT PMP] sendpublicaddressrequest returned %d (%s)\n", + r, r == 2 ? "SUCCESS" : "FAILED"); + if (r < 0) { + return false; + } + + natpmpresp_t response; + do { + fd_set fds; + struct timeval timeout; + FD_ZERO(&fds); + FD_SET(natpmp.s, &fds); + getnatpmprequesttimeout(&natpmp, &timeout); + r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + if(r<0) { + log_msg(LOG_LEVEL_ERROR, "[NAT PMP] select()\n"); + return false; + } + r = readnatpmpresponseorretry(&natpmp, &response); + int sav_errno = errno; + log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, "[NAT PMP] readnatpmpresponseorretry returned %d (%s)\n", + r, r == 0 ? "OK" : ( r== NATPMP_TRYAGAIN ? "TRY AGAIN" : "FAILED")); + if (r < 0 && r != NATPMP_TRYAGAIN) { + log_msg(LOG_LEVEL_ERROR, "[NAT_PMP] readnatpmpresponseorretry() failed : %s\n", + strnatpmperr(r)); + log_msg(LOG_LEVEL_ERROR, "[NAT PMP] errno=%d '%s'\n", + sav_errno, strerror(sav_errno)); + } + } while(r==NATPMP_TRYAGAIN); + + if (r < 0) { + return false; + } + + log_msg(LOG_LEVEL_NOTICE, "[NAT PMP] Public IP address: %s\n", inet_ntoa(response.pnu.publicaddress.addr)); + log_msg(LOG_LEVEL_DEBUG, "[NAT PMP] epoch = %u\n", response.epoch); + + if (!nat_pmp_add_mapping(&natpmp, video_rx_port, video_rx_port, lifetime) || + !nat_pmp_add_mapping(&natpmp, audio_rx_port, audio_rx_port, lifetime)) { + return false; + } + + r = closenatpmp(&natpmp); + log_msg(LOG_LEVEL_VERBOSE, "[NAT PMP] closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED"); + return r >= 0; +} + +struct ug_nat_traverse *start_nat_traverse(int video_rx_port, int audio_rx_port) +{ + assert(video_rx_port >= 0 && video_rx_port <= 65535 && audio_rx_port >= 0 && audio_rx_port <= 65535); + struct ug_nat_traverse s = { .audio_rx_port = audio_rx_port, .video_rx_port = video_rx_port }; + if (setup_nat_pmp(video_rx_port, audio_rx_port, ALLOCATION_TIMEOUT)) { + log_msg(LOG_LEVEL_NOTICE, "Successfully set NAT traversal with NAT PMP. Sender can send to external IP address.\n"); + s.traverse = UG_NAT_TRAVERSE_NAT_PMP; + return memcpy(malloc(sizeof s), &s, sizeof s); + } + // other techniques may follow + return NULL; +} + +void stop_nat_traverse(struct ug_nat_traverse *s) +{ + if (s == NULL) { + return; + } + + switch (s->traverse) { + case UG_NAT_TRAVERSE_NAT_PMP: + setup_nat_pmp(s->video_rx_port, s->audio_rx_port, 0); + break; + default: + break; + } + + free(s); +} + +/* vim: set expandtab sw=8: */ diff --git a/src/spout_receiver.h b/src/utils/nat.h similarity index 65% rename from src/spout_receiver.h rename to src/utils/nat.h index 0591ad141..83f056068 100644 --- a/src/spout_receiver.h +++ b/src/utils/nat.h @@ -1,9 +1,9 @@ /** - * @file spout_receiver.h - * @author Martin Pulec + * @file utils/nat.h + * @author Martin Pulec */ /* - * Copyright (c) 2018 CESNET, z. s. p. o. + * Copyright (c) 2020 CESNET z.s.p.o. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,34 +35,26 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SPOUT_RECEIVER_H_ -#define SPOUT_RECEIVER_H_ +#ifndef UTILS_NAT_H_AC13CDA9_2B22_4441_A81A_9858C87CE0AA +#define UTILS_NAT_H_AC13CDA9_2B22_4441_A81A_9858C87CE0AA -#include -#include +#ifndef __cplusplus #include +#endif // ! defined __cplusplus #ifdef __cplusplus extern "C" { -#endif // __cplusplus - -#if defined _MSC_VER || defined __MINGW32__ -#ifdef EXPORT_DLL_SYMBOLS -#define SPOUT_RECEIVER_DLL_API __declspec(dllexport) -#else -#define SPOUT_RECEIVER_DLL_API __declspec(dllimport) -#endif -#else // other platforms -#define SPOUT_RECEIVER_DLL_API #endif -SPOUT_RECEIVER_DLL_API void *spout_create_receiver(char *name, unsigned int *width, unsigned int *height); -SPOUT_RECEIVER_DLL_API bool spout_receiver_recvframe(void *s, char *sender_name, unsigned int width, unsigned int height, char *data, GLenum glFormat); -SPOUT_RECEIVER_DLL_API void spout_receiver_delete(void *s); +struct ug_nat_traverse; + +struct ug_nat_traverse *start_nat_traverse(int video_rx_port, int audio_rx_port); +void stop_nat_traverse(struct ug_nat_traverse *); + #ifdef __cplusplus } -#endif // __cplusplus +#endif -#endif // SPOUT_RECEIVER_H_ +#endif // defined UTILS_NAT_H_AC13CDA9_2B22_4441_A81A_9858C87CE0AA diff --git a/src/utils/sdp.c b/src/utils/sdp.c index 7416f1841..6e95e2d8b 100644 --- a/src/utils/sdp.c +++ b/src/utils/sdp.c @@ -106,7 +106,7 @@ struct sdp { }; struct sdp *new_sdp(int ip_version, const char *receiver) { - assert(ip_version == 0 || ip_version == 4 || ip_version == 6); + assert(ip_version == 4 || ip_version == 6); struct sdp *sdp; sdp = calloc(1, sizeof(struct sdp)); assert(sdp != NULL); @@ -137,9 +137,9 @@ struct sdp *new_sdp(int ip_version, const char *receiver) { connection_address = receiver; } strncpy(sdp->version, "v=0\n", STR_LENGTH - 1); - snprintf(sdp->origin, STR_LENGTH, "o=- 0 0 IN IP%d %s\n", ip_version == 0 ? 4 : 6, origin_address); + snprintf(sdp->origin, STR_LENGTH, "o=- 0 0 IN IP%d %s\n", ip_version, origin_address); strncpy(sdp->session_name, "s=Ultragrid streams\n", STR_LENGTH - 1); - snprintf(sdp->connection, STR_LENGTH, "c=IN IP%d %s\n", ip_version == 0 ? 4 : 6, connection_address); + snprintf(sdp->connection, STR_LENGTH, "c=IN IP%d %s\n", ip_version, connection_address); strncpy(sdp->times, "t=0 0\n", STR_LENGTH - 1); return sdp; diff --git a/src/video_capture.cpp b/src/video_capture.cpp index 4bd2e95fe..2613b7abf 100644 --- a/src/video_capture.cpp +++ b/src/video_capture.cpp @@ -112,6 +112,9 @@ void print_available_capturers() void (*deleter)(void *) = nullptr; struct vidcap_type *vt = vci->probe(true, &deleter); + if (vt == nullptr) { + continue; + } printf("[cap][capture] %s\n", item.first.c_str()); for (int i = 0; i < vt->card_count; ++i) { printf("[capability][device][v2] {" diff --git a/src/video_capture/DirectShowGrabber.cpp b/src/video_capture/DirectShowGrabber.cpp index 4739dac17..c72a6d1d6 100644 --- a/src/video_capture/DirectShowGrabber.cpp +++ b/src/video_capture/DirectShowGrabber.cpp @@ -167,6 +167,12 @@ public: (char *) buffer + (s->desc.height - i - 1) * linesize, linesize); } + } else if (s->desc.color_spec == RGBA) { // convert from ABGR and bottom-to-top + for(unsigned int i = 0; i < s->desc.height; ++i) { + vc_copylineRGBA(s->grabBuffer + i * linesize, + buffer + (s->desc.height - i - 1) * linesize, + linesize, 16, 8, 0); + } } else { memcpy((char *) s->grabBuffer, (char *) buffer, len); } @@ -1317,7 +1323,8 @@ static struct video_frame * vidcap_dshow_grab(void *state, struct audio_frame ** s->frame->tiles[0].data = (char *) s->returnBuffer; //fprintf(stderr, "[dshow] s: %p\n", s); //s->tile->data_len = s->width * s->height * 3; - s->frame->tiles[0].data_len = s->returnBufferLen; + s->frame->tiles[0].data_len = is_codec_opaque(s->frame->color_spec) ? s->returnBufferLen : + vc_get_datalen(s->frame->tiles[0].width, s->frame->tiles[0].height, s->frame->color_spec); /* fprintf(stderr, "[dshow] s5: %p\n", s); diff --git a/src/video_capture/bluefish444.cpp b/src/video_capture/bluefish444.cpp index 8247a83c7..36f9e0462 100644 --- a/src/video_capture/bluefish444.cpp +++ b/src/video_capture/bluefish444.cpp @@ -351,24 +351,29 @@ vidcap_bluefish444_probe(bool verbose, void (**deleter)(void *)) *deleter = free; vt = (struct vidcap_type *) calloc(1, sizeof(struct vidcap_type)); - if (vt != NULL) { - vt->name = "bluefish444"; - vt->description = "Bluefish444 video capture"; - - if (verbose) { - BLUE_S32 iDevices; - BLUEVELVETC_HANDLE pSDK = bfcFactory(); - bfcEnumerate(pSDK, &iDevices); - bfcDestroy(pSDK); - - vt->card_count = iDevices; - vt->cards = (struct device_info *) calloc(iDevices, sizeof(struct device_info)); - for (int i = 0; i < iDevices; ++i) { - snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "%d", i + 1); - snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "Bluefish444 card #%d", i); - } - } + if (vt == NULL) { + return NULL; } + + vt->name = "bluefish444"; + vt->description = "Bluefish444 video capture"; + + if (!verbose) { + return vt; + } + + BLUE_S32 iDevices; + BLUEVELVETC_HANDLE pSDK = bfcFactory(); + bfcEnumerate(pSDK, &iDevices); + bfcDestroy(pSDK); + + vt->card_count = iDevices; + vt->cards = (struct device_info *) calloc(iDevices, sizeof(struct device_info)); + for (int i = 0; i < iDevices; ++i) { + snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "%d", i + 1); + snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "Bluefish444 card #%d", i); + } + return vt; } diff --git a/src/video_capture/decklink.cpp b/src/video_capture/decklink.cpp index 2c60b72e8..c1889b62f 100644 --- a/src/video_capture/decklink.cpp +++ b/src/video_capture/decklink.cpp @@ -825,10 +825,11 @@ static struct vidcap_type *vidcap_decklink_probe(bool verbose, void (**deleter)( } list> modes = get_input_modes (deckLink); int i = 0; + const int mode_count = sizeof vt->cards[vt->card_count - 1].modes / + sizeof vt->cards[vt->card_count - 1].modes[0]; for (auto &m : modes) { for (auto &c : connections) { - if (i >= (int) (sizeof vt->cards[vt->card_count - 1].modes / - sizeof vt->cards[vt->card_count - 1].modes[0])) { // no space + if (i >= mode_count) { // no space break; } @@ -843,14 +844,27 @@ static struct vidcap_type *vidcap_decklink_probe(bool verbose, void (**deleter)( } } - if (i < (int) (sizeof vt->cards[vt->card_count - 1].modes / - sizeof vt->cards[vt->card_count - 1].modes[0])) { + for (auto &c : connections) { + if (i >= mode_count) { + break; + } snprintf(vt->cards[vt->card_count - 1].modes[i].id, sizeof vt->cards[vt->card_count - 1].modes[i].id, - "{\"modeOpt\":\"detect-format\"}"); + "{\"modeOpt\":\"connection=%s\"}", + c.c_str()); snprintf(vt->cards[vt->card_count - 1].modes[i].name, sizeof vt->cards[vt->card_count - 1].modes[i].name, - "UltraGrid auto-detect"); + "Decklink Auto (%s)", c.c_str()); + if (++i >= mode_count) { + break; + } + snprintf(vt->cards[vt->card_count - 1].modes[i].id, + sizeof vt->cards[vt->card_count - 1].modes[i].id, + "{\"modeOpt\":\"detect-format:connection=%s\"}", + c.c_str()); + snprintf(vt->cards[vt->card_count - 1].modes[i].name, + sizeof vt->cards[vt->card_count - 1].modes[i].name, + "UltraGrid auto-detect (%s)", c.c_str()); i++; } diff --git a/src/video_capture/deltacast.cpp b/src/video_capture/deltacast.cpp index fb38f3f09..eff683470 100644 --- a/src/video_capture/deltacast.cpp +++ b/src/video_capture/deltacast.cpp @@ -122,21 +122,25 @@ vidcap_deltacast_probe(bool verbose, void (**deleter)(void *)) *deleter = free; vt = (struct vidcap_type *) calloc(1, sizeof(struct vidcap_type)); - if (vt != NULL) { - vt->name = "deltacast"; - vt->description = "DELTACAST card"; + if (vt == NULL) { + return NULL; + } - if (verbose) { - ULONG Result,DllVersion,NbBoards; - Result = VHD_GetApiInfo(&DllVersion,&NbBoards); - if (Result == VHDERR_NOERROR) { - vt->cards = (struct device_info *) calloc(NbBoards, sizeof(struct device_info)); - vt->card_count = NbBoards; - for (ULONG i = 0; i < NbBoards; ++i) { - snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "device=%" PRIu32, i); - snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "DELTACAST SDI board %" PRIu32, i); - } - } + vt->name = "deltacast"; + vt->description = "DELTACAST card"; + + if (!verbose) { + return vt; + } + + ULONG Result,DllVersion,NbBoards; + Result = VHD_GetApiInfo(&DllVersion,&NbBoards); + if (Result == VHDERR_NOERROR) { + vt->cards = (struct device_info *) calloc(NbBoards, sizeof(struct device_info)); + vt->card_count = NbBoards; + for (ULONG i = 0; i < NbBoards; ++i) { + snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "device=%" PRIu32, i); + snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "DELTACAST SDI board %" PRIu32, i); } } return vt; diff --git a/src/video_capture/deltacast_dvi.cpp b/src/video_capture/deltacast_dvi.cpp index 63c600c75..47bf77f7e 100644 --- a/src/video_capture/deltacast_dvi.cpp +++ b/src/video_capture/deltacast_dvi.cpp @@ -165,38 +165,44 @@ vidcap_deltacast_dvi_probe(bool verbose, void (**deleter)(void *)) *deleter = free; vt = (struct vidcap_type *) calloc(1, sizeof(struct vidcap_type)); - if (vt != NULL) { - vt->name = "deltacast-dv"; - vt->description = "DELTACAST DVI/HDMI card"; + if (vt == NULL) { + return NULL; + } - if (verbose) { - ULONG Result,DllVersion,NbBoards; - Result = VHD_GetApiInfo(&DllVersion,&NbBoards); - if (Result == VHDERR_NOERROR) { - vt->cards = (struct device_info *) calloc(NbBoards, sizeof(struct device_info)); - vt->card_count = NbBoards; - for (ULONG i = 0; i < NbBoards; ++i) { - string board{"Unknown board type"}; - ULONG BoardType; - HANDLE BoardHandle = NULL; - Result = VHD_OpenBoardHandle(i, &BoardHandle, NULL, 0); - VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_BOARD_TYPE, &BoardType); - if (Result == VHDERR_NOERROR) - { - auto it = board_type_map.find(BoardType); - if (it != board_type_map.end()) { - board = it->second; - } - } - VHD_CloseBoardHandle(BoardHandle); + vt->name = "deltacast-dv"; + vt->description = "DELTACAST DVI/HDMI card"; - snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "device=%" PRIu32, i); - snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "DELTACAST %s #%" PRIu32, - board.c_str(), i); - } + if (!verbose) { + return vt; + } + + ULONG Result,DllVersion,NbBoards; + Result = VHD_GetApiInfo(&DllVersion,&NbBoards); + if (Result != VHDERR_NOERROR) { + return vt; + } + + vt->cards = (struct device_info *) calloc(NbBoards, sizeof(struct device_info)); + vt->card_count = NbBoards; + for (ULONG i = 0; i < NbBoards; ++i) { + string board{"Unknown board type"}; + ULONG BoardType; + HANDLE BoardHandle = NULL; + Result = VHD_OpenBoardHandle(i, &BoardHandle, NULL, 0); + VHD_GetBoardProperty(BoardHandle, VHD_CORE_BP_BOARD_TYPE, &BoardType); + if (Result == VHDERR_NOERROR) + { + auto it = board_type_map.find(BoardType); + if (it != board_type_map.end()) { + board = it->second; } } - } + VHD_CloseBoardHandle(BoardHandle); + + snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "device=%" PRIu32, i); + snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "DELTACAST %s #%" PRIu32, + board.c_str(), i); + } return vt; } diff --git a/src/video_capture/dvs.c b/src/video_capture/dvs.c index 475cebb3e..2dd621098 100644 --- a/src/video_capture/dvs.c +++ b/src/video_capture/dvs.c @@ -650,31 +650,33 @@ static struct vidcap_type *vidcap_dvs_probe(bool verbose, void (**deleter)(void *deleter = free; vt = (struct vidcap_type *) calloc(1, sizeof(struct vidcap_type)); - if (vt != NULL) { - vt->name = "dvs"; - vt->description = "DVS (SMPTE 274M/25i)"; + if (vt == NULL) { + return NULL; + } + vt->name = "dvs"; + vt->description = "DVS (SMPTE 274M/25i)"; - if (verbose) { - int card_idx = 0; - sv_handle *sv; - char name[128]; - int res; - snprintf(name, 128, "PCI,card:%d", card_idx); - res = sv_openex(&sv, name, SV_OPENPROGRAM_DEFAULT, SV_OPENTYPE_DEFAULT, 0, 0); - while (res == SV_OK) { - vt->card_count = card_idx + 1; - vt->cards = realloc(vt->cards, vt->card_count * sizeof(struct device_info)); - memset(&vt->cards[card_idx], 0, sizeof(struct device_info)); - strncpy(vt->cards[card_idx].id, name, sizeof vt->cards[card_idx].id - 1); - snprintf(vt->cards[card_idx].name, sizeof vt->cards[card_idx].name, - "DVS card #%d", card_idx); + if (!verbose) { + return vt; + } + int card_idx = 0; + sv_handle *sv; + char name[128]; + int res; + snprintf(name, 128, "PCI,card:%d", card_idx); + res = sv_openex(&sv, name, SV_OPENPROGRAM_DEFAULT, SV_OPENTYPE_DEFAULT, 0, 0); + while (res == SV_OK) { + vt->card_count = card_idx + 1; + vt->cards = realloc(vt->cards, vt->card_count * sizeof(struct device_info)); + memset(&vt->cards[card_idx], 0, sizeof(struct device_info)); + strncpy(vt->cards[card_idx].id, name, sizeof vt->cards[card_idx].id - 1); + snprintf(vt->cards[card_idx].name, sizeof vt->cards[card_idx].name, + "DVS card #%d", card_idx); - sv_close(sv); - card_idx++; - snprintf(name, 128, "PCI,card:%d", card_idx); - res = sv_openex(&sv, name, SV_OPENPROGRAM_DEFAULT, SV_OPENTYPE_DEFAULT, 0, 0); - } - } + sv_close(sv); + card_idx++; + snprintf(name, 128, "PCI,card:%d", card_idx); + res = sv_openex(&sv, name, SV_OPENPROGRAM_DEFAULT, SV_OPENTYPE_DEFAULT, 0, 0); } return vt; } diff --git a/src/video_capture/ndi.cpp b/src/video_capture/ndi.cpp index 2d13518b7..29eafc7b7 100644 --- a/src/video_capture/ndi.cpp +++ b/src/video_capture/ndi.cpp @@ -137,6 +137,7 @@ static void show_help() { cout << "\t\t" << p_sources[i].p_ndi_name << " - " << p_sources[i].p_url_address << "\n"; } cout << "\n"; + NDIlib_find_destroy(pNDI_find); } static int vidcap_ndi_init(struct vidcap_params *params, void **state) @@ -416,34 +417,39 @@ static struct vidcap_type *vidcap_ndi_probe(bool verbose, void (**deleter)(void vt->name = "ndi"; vt->description = "NDI source"; - if (verbose) { - auto pNDI_find = NDIlib_find_create_v2(); - if (pNDI_find == nullptr) { - LOG(LOG_LEVEL_ERROR) << "[NDI] Cannot create finder object!\n"; - return vt; - } - - uint32_t nr_sources = 0; - const NDIlib_source_t* p_sources = nullptr; - // Give sources some time to occur - usleep(100 * 1000); - // we do not usea NDIlib_find_wait_for_sources() here because: 1) if there is - // no source, it will still wait requested amount of time and 2) if there are - // more sources, it will continue after first source found while there can be more - p_sources = NDIlib_find_get_current_sources(pNDI_find, &nr_sources); - - vt->cards = (struct device_info *) malloc(nr_sources * sizeof(struct device_info)); - if (vt->cards == nullptr) { - return vt; - } - vt->card_count = nr_sources; - for (int i = 0; i < static_cast(nr_sources); ++i) { - snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "ndi:url=%s", p_sources[i].p_url_address); - snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "%s", p_sources[i].p_ndi_name); - vt->cards[i].repeatable = true; - } + if (!verbose) { + return vt; } + auto pNDI_find = NDIlib_find_create_v2(); + if (pNDI_find == nullptr) { + LOG(LOG_LEVEL_ERROR) << "[NDI] Cannot create finder object!\n"; + return vt; + } + + uint32_t nr_sources = 0; + const NDIlib_source_t* p_sources = nullptr; + // Give sources some time to occur + usleep(100 * 1000); + // we do not usea NDIlib_find_wait_for_sources() here because: 1) if there is + // no source, it will still wait requested amount of time and 2) if there are + // more sources, it will continue after first source found while there can be more + p_sources = NDIlib_find_get_current_sources(pNDI_find, &nr_sources); + + vt->cards = (struct device_info *) calloc(nr_sources, sizeof(struct device_info)); + if (vt->cards == nullptr) { + NDIlib_find_destroy(pNDI_find); + return vt; + } + vt->card_count = nr_sources; + for (int i = 0; i < static_cast(nr_sources); ++i) { + snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "url=%s", p_sources[i].p_url_address); + snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "%s", p_sources[i].p_ndi_name); + vt->cards[i].repeatable = true; + } + + NDIlib_find_destroy(pNDI_find); + return vt; } diff --git a/src/video_capture/screen_win.c b/src/video_capture/screen_win.c index f0e1c3e02..af9c7e0ee 100644 --- a/src/video_capture/screen_win.c +++ b/src/video_capture/screen_win.c @@ -99,7 +99,6 @@ static struct vidcap_type * vidcap_screen_win_probe(bool verbose, void (**delete vt->card_count = 1; vt->cards = calloc(vt->card_count, sizeof(struct device_info)); // vt->cards[0].id can be "" since screen cap. doesn't require parameters - snprintf(vt->cards[0].id, sizeof vt->cards[0].id, "screen"); snprintf(vt->cards[0].name, sizeof vt->cards[0].name, "Screen capture"); return vt; diff --git a/src/video_capture/spout.cpp b/src/video_capture/spout.cpp index 6ca96086e..ffe9bcb2a 100644 --- a/src/video_capture/spout.cpp +++ b/src/video_capture/spout.cpp @@ -41,25 +41,40 @@ #include "config_win32.h" #endif +#include "gl_context.h" // it looks like it needs to be included prior to Spout.h + +#include +#include #include +#include +#include +#include #include "debug.h" -#include "gl_context.h" #include "host.h" #include "lib_common.h" -#include "spout_receiver.h" +#include "utils/color_out.h" +#include "utils/misc.h" // urlencode, urldecode #include "video.h" #include "video_capture.h" #define DEFAULT_FPS 60.0 #define DEFAULT_CODEC RGB +static constexpr const char *MOD_NAME = "[SPOUT] "; + +using std::array; +using std::cout; +using std::shared_ptr; +using std::stoi; +using std::string; + /** * Class state_vidcap_spout must be value-initialized */ struct state_vidcap_spout { struct video_desc desc; - void *spout_state; + shared_ptr spout_state; struct gl_context glc; GLenum gl_format; @@ -72,17 +87,59 @@ struct state_vidcap_spout { static void usage() { - printf("\t-t spout[:name=][:fps=][:codec=]\n"); - printf("\n"); - printf("\tname\n\t\tSPOUT server name\n"); - printf("\tfps\n\t\tFPS count (default: %.2lf)\n", DEFAULT_FPS); - printf("\tcodec\n\t\tvideo codec (default: %s)\n", get_codec_name(DEFAULT_CODEC)); + cout << "Usage:\n"; + cout << "\t" << BOLD(RED("-t spout") << "[:name=|device=][:fps=][:codec=]") << "\n"; + cout << "where\n"; + cout << "\t" << BOLD("name") << "\n\t\tSPOUT server name\n"; + cout << "\t" << BOLD("fps") << "\n\t\tFPS count (default: " << DEFAULT_FPS << ")\n"; + cout << "\t" << BOLD("codec") << "\n\t\tvideo codec (default: " << get_codec_name(DEFAULT_CODEC) << ")\n"; + cout << "\nServers:\n"; + auto receiver = shared_ptr(new SpoutReceiver); + int count = receiver->GetSenderCount(); + + for (int i = 0; i < count; ++i) { + array name{}; + if (!receiver->GetSenderName(i, name.data(), name.size())) { + LOG(LOG_LEVEL_ERROR) << "Cannot get name for server #" << i << "\n"; + continue; + } + unsigned int width = 0; + unsigned int height = 0; + HANDLE dxShareHandle = 0; + DWORD dwFormat = 0; + if (!receiver->GetSenderInfo(name.data(), width, height, dxShareHandle, dwFormat)) { + LOG(LOG_LEVEL_ERROR) << "Cannot get server " << name.data() << "details\n"; + } + cout << "\t" << i << ") " << BOLD(name.data()) << " - width: " << width << ", height: " << height << "\n"; + } +} + +static string vidcap_spout_get_device_name(int idx) +{ + if (idx < 0) { + LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Negative indices not allowed, given: " << idx << "\n"; + return {}; + } + auto receiver = shared_ptr(new SpoutReceiver); + int count = receiver->GetSenderCount(); + if (idx >= count) { + LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Cannot find server #" << idx << " (total count " << count << ")!\n"; + return {}; + } + + array name{}; + if (!receiver->GetSenderName(idx, name.data(), name.size())) { + LOG(LOG_LEVEL_ERROR) << "Cannot get name for server #" << idx << "\n"; + return {}; + } + return name.data(); } static int vidcap_spout_init(struct vidcap_params *params, void **state) { state_vidcap_spout *s = new state_vidcap_spout(); + int device_idx = 0; double fps = DEFAULT_FPS; codec_t codec = DEFAULT_CODEC; @@ -96,8 +153,19 @@ static int vidcap_spout_init(struct vidcap_params *params, void **state) usage(); ret = VIDCAP_INIT_NOERR; break; + } else if (strstr(item, "device=") == item) { + device_idx = stoi(item + strlen("device=")); } else if (strstr(item, "name=") == item) { - strncpy(s->server_name, item + strlen("name="), sizeof(s->server_name) - 1); + char *name = item + strlen("name="); + if (strstr(name, "urlencoded=") == name) { + name += strlen("urlencoded="); + if (urldecode(s->server_name, sizeof s->server_name, name) == 0) { + LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Improperly formatted name: " << item + strlen("name=") << "\n"; + ret = VIDCAP_INIT_FAIL; + } + } else { + strncpy(s->server_name, item + strlen("name="), sizeof(s->server_name) - 1); + } } else if (strstr(item, "fps=") == item) { fps = atof(item + strlen("fps=")); } else if (strstr(item, "codec=") == item) { @@ -130,6 +198,15 @@ static int vidcap_spout_init(struct vidcap_params *params, void **state) return VIDCAP_INIT_FAIL; } + if (strlen(s->server_name) == 0) { + const string &name = vidcap_spout_get_device_name(device_idx); + if (name.empty()) { + delete s; + return VIDCAP_INIT_FAIL; + } + strncpy(s->server_name, name.c_str(), sizeof s->server_name - 1); + } + if (!init_gl_context(&s->glc, GL_CONTEXT_ANY)) { LOG(LOG_LEVEL_ERROR) << "[SPOUT] Unable to initialize GL context!\n"; delete s; @@ -139,14 +216,17 @@ static int vidcap_spout_init(struct vidcap_params *params, void **state) unsigned int width = 0, height = 0; - s->spout_state = spout_create_receiver(s->server_name, &width, &height); - if (!s->spout_state) { - LOG(LOG_LEVEL_ERROR) << "[SPOUT] Unable to initialize SPOUT state!\n"; + s->spout_state = shared_ptr(new SpoutReceiver); + s->spout_state->CreateReceiver(s->server_name, width, height); + bool connected; + s->spout_state->CheckReceiver(s->server_name, width, height, connected); + if (!connected) { + LOG(LOG_LEVEL_ERROR) << "[SPOUT] Not connected to server '" << s->server_name << "'. Is it running?\n"; + s->spout_state->ReleaseReceiver(); delete s; return VIDCAP_INIT_FAIL; - } else { - LOG(LOG_LEVEL_NOTICE) << "[SPOUT] Initialized successfully - server name: " << s->server_name << ", width: " << width << ", height: " << height << ", fps: " << fps << ", codec: " << get_codec_name_long(codec) << "\n"; } + LOG(LOG_LEVEL_NOTICE) << "[SPOUT] Initialized successfully - server name: " << s->server_name << ", width: " << width << ", height: " << height << ", fps: " << fps << ", codec: " << get_codec_name_long(codec) << "\n"; s->desc = video_desc{width, height, codec, fps, PROGRESSIVE, 1}; @@ -162,7 +242,7 @@ static void vidcap_spout_done(void *state) state_vidcap_spout *s = (state_vidcap_spout *) state; gl_context_make_current(&s->glc); - spout_receiver_delete(s->spout_state); + s->spout_state->ReleaseReceiver(); destroy_gl_context(&s->glc); delete s; @@ -175,12 +255,11 @@ static struct video_frame *vidcap_spout_grab(void *state, struct audio_frame **a struct video_frame *out = vf_alloc_desc_data(s->desc); out->callbacks.dispose = vf_free; - bool ret; unsigned int width, height; width = s->desc.width; height = s->desc.height; gl_context_make_current(&s->glc); - ret = spout_receiver_recvframe(s->spout_state, s->server_name, width, height, out->tiles[0].data, s->gl_format); + bool ret = s->spout_state->ReceiveImage(s->server_name, width, height, (unsigned char *) out->tiles[0].data, s->gl_format); gl_context_make_current(NULL); if (ret) { // statistics @@ -220,6 +299,35 @@ static struct vidcap_type *vidcap_spout_probe(bool verbose, void (**deleter)(voi vt->name = "spout"; vt->description = "SPOUT capture client"; } + + if (!verbose) { + return vt; + } + + auto receiver = shared_ptr(new SpoutReceiver); + int count = receiver->GetSenderCount(); + + vt->cards = (struct device_info *) calloc(count, sizeof(struct device_info)); + if (vt->cards == nullptr) { + return vt; + } + vt->card_count = count; + + for (int i = 0; i < count; ++i) { + array name{}; + if (!receiver->GetSenderName(i, name.data(), name.size())) { + LOG(LOG_LEVEL_VERBOSE) << MOD_NAME << "Cannot get name for server #" << i << "\n"; + snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "device=%d", i); + snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "SPOUT #%d", i); + } else { + snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "name=urlencoded="); + urlencode(vt->cards[i].id + strlen(vt->cards[i].id), + sizeof vt->cards[i].id - strlen(vt->cards[i].id), + name.data(), urlencode_rfc3986_eval, false); + snprintf(vt->cards[i].name, sizeof vt->cards[i].name, "SPOUT %s", name.data()); + } + vt->cards[i].repeatable = true; + } return vt; } diff --git a/src/video_capture/testcard.cpp b/src/video_capture/testcard.cpp index 334254751..0433671c9 100644 --- a/src/video_capture/testcard.cpp +++ b/src/video_capture/testcard.cpp @@ -816,59 +816,60 @@ static struct vidcap_type *vidcap_testcard_probe(bool verbose, void (**deleter)( *deleter = free; vt = (struct vidcap_type *) calloc(1, sizeof(struct vidcap_type)); - if (vt != NULL) { - vt->name = "testcard"; - vt->description = "Video testcard"; + if (vt == NULL) { + return NULL; + } - if (verbose) { - vt->card_count = 1; - vt->cards = (struct device_info *) calloc(vt->card_count, sizeof(struct device_info)); - vt->cards[0].id[0] = '\0'; - snprintf(vt->cards[0].name, sizeof vt->cards[0].name, "Testing signal"); + vt->name = "testcard"; + vt->description = "Video testcard"; - struct { - int width; - int height; - } sizes[] = { - {1280, 720}, - {1920, 1080}, - {3840, 2160}, - }; - int framerates[] = {24, 30, 60}; - const char * const pix_fmts[] = {"UYVY", "RGB"}; + if (!verbose) { + return vt; + } - snprintf(vt->cards[0].modes[0].name, - sizeof vt->cards[0].name, "Default"); - snprintf(vt->cards[0].modes[0].id, - sizeof vt->cards[0].id, - "{\"width\":\"\", " - "\"height\":\"\", " - "\"format\":\"\", " - "\"fps\":\"\"}"); + vt->card_count = 1; + vt->cards = (struct device_info *) calloc(vt->card_count, sizeof(struct device_info)); + snprintf(vt->cards[0].name, sizeof vt->cards[0].name, "Testing signal"); - int i = 1; - for(const auto &pix_fmt : pix_fmts){ - for(const auto &size : sizes){ - for(const auto &fps : framerates){ - snprintf(vt->cards[0].modes[i].name, - sizeof vt->cards[0].name, - "%dx%d@%d %s", - size.width, size.height, - fps, pix_fmt); - snprintf(vt->cards[0].modes[i].id, - sizeof vt->cards[0].id, - "{\"width\":\"%d\", " - "\"height\":\"%d\", " - "\"format\":\"%s\", " - "\"fps\":\"%d\"}", - size.width, size.height, - pix_fmt, fps); - i++; - } - } + struct { + int width; + int height; + } sizes[] = { + {1280, 720}, + {1920, 1080}, + {3840, 2160}, + }; + int framerates[] = {24, 30, 60}; + const char * const pix_fmts[] = {"UYVY", "RGB"}; + snprintf(vt->cards[0].modes[0].name, + sizeof vt->cards[0].name, "Default"); + snprintf(vt->cards[0].modes[0].id, + sizeof vt->cards[0].id, + "{\"width\":\"\", " + "\"height\":\"\", " + "\"format\":\"\", " + "\"fps\":\"\"}"); + + int i = 1; + for(const auto &pix_fmt : pix_fmts){ + for(const auto &size : sizes){ + for(const auto &fps : framerates){ + snprintf(vt->cards[0].modes[i].name, + sizeof vt->cards[0].name, + "%dx%d@%d %s", + size.width, size.height, + fps, pix_fmt); + snprintf(vt->cards[0].modes[i].id, + sizeof vt->cards[0].id, + "{\"width\":\"%d\", " + "\"height\":\"%d\", " + "\"format\":\"%s\", " + "\"fps\":\"%d\"}", + size.width, size.height, + pix_fmt, fps); + i++; } - } } return vt; diff --git a/src/video_capture/ximea.c b/src/video_capture/ximea.c index 061e9ee25..e187f46cf 100644 --- a/src/video_capture/ximea.c +++ b/src/video_capture/ximea.c @@ -351,9 +351,8 @@ static struct vidcap_type *vidcap_ximea_probe(bool verbose, void (**deleter)(voi vt->cards = calloc(count, sizeof(struct device_info)); for (DWORD i = 0; i < count; ++i) { - snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "ximea:device=%d", (int) i); + snprintf(vt->cards[i].id, sizeof vt->cards[i].id, "device=%d", (int) i); char name[256]; - color_out(COLOR_OUT_BOLD, "%d) ", (int) i); if (funcs.xiGetDeviceInfoString(i, XI_PRM_DEVICE_NAME, name, sizeof name) == XI_OK) { strncpy(vt->cards[i].name, name, sizeof vt->cards[i].name); } diff --git a/src/video_compress/libavcodec.cpp b/src/video_compress/libavcodec.cpp index 1ecce00c9..241056096 100644 --- a/src/video_compress/libavcodec.cpp +++ b/src/video_compress/libavcodec.cpp @@ -501,9 +501,7 @@ struct module * libavcodec_compress_init(struct module *parent, const char *opts s = new state_video_compress_libav(); s->lavcd_global_lock = rm_acquire_shared_lock(LAVCD_LOCK_NAME); - if (log_level >= LOG_LEVEL_VERBOSE) { - av_log_set_level(AV_LOG_VERBOSE); - } + av_log_set_level((log_level - 1) * 8); #if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(58, 9, 100) /* register all the codecs (you can also register only the codec * you wish to have smaller code */ @@ -675,29 +673,26 @@ bool set_codec_ctx_params(struct state_video_compress_libav *s, AVPixelFormat pi // set bitrate if ((s->requested_bitrate > 0 || s->requested_bpp > 0.0) - || !is_x264_x265) { + || (!is_x264_x265 && s->requested_crf == -1 && s->requested_cqp == -1)) { s->codec_ctx->bit_rate = bitrate; s->codec_ctx->bit_rate_tolerance = bitrate / desc.fps * 6; LOG(LOG_LEVEL_INFO) << MOD_NAME << "Setting bitrate to " << bitrate << " bps.\n"; - } - - if (is_x264_x265) { + } else { // set CRF unless explicitly specified CQP or ABR (bitrate) if (s->requested_crf >= 0.0 || (s->requested_bitrate == 0 && s->requested_bpp == 0.0 && s->requested_cqp == -1)) { double crf = s->requested_crf >= 0.0 ? s->requested_crf : DEFAULT_X264_X265_CRF; - av_opt_set_double(s->codec_ctx->priv_data, "crf", crf, 0); - log_msg(LOG_LEVEL_INFO, "[lavc] Setting CRF to %.2f.\n", crf); + if (int rc = av_opt_set_double(s->codec_ctx->priv_data, "crf", crf, 0)) { + print_libav_error(LOG_LEVEL_WARNING, MOD_NAME "Warning: Unable to set CRF", rc); + } else { + log_msg(LOG_LEVEL_INFO, "[lavc] Setting CRF to %.2f.\n", crf); + } } if (s->requested_cqp >= 0) { - av_opt_set_int(s->codec_ctx->priv_data, "qp", s->requested_cqp, 0); - log_msg(LOG_LEVEL_INFO, "[lavc] Setting CQP to %d.\n", s->requested_cqp); - } - } else { - if (s->requested_crf >= 0.0) { - log_msg(LOG_LEVEL_WARNING, "[lavc] Unable to set CRF! Not supported for this encoder, ignored.\n"); - } - if (s->requested_cqp >= 0.0) { - log_msg(LOG_LEVEL_WARNING, "[lavc] Unable to set CQP! Not supported for this encoder, ignored.\n"); + if (int rc = av_opt_set_int(s->codec_ctx->priv_data, "qp", s->requested_cqp, 0)) { + print_libav_error(LOG_LEVEL_WARNING, MOD_NAME "Warning: Unable to set CQP", rc); + } else { + log_msg(LOG_LEVEL_INFO, "[lavc] Setting CQP to %d.\n", s->requested_cqp); + } } } diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index 3d210b305..8ac186b3c 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -375,9 +375,7 @@ static void * libavcodec_decompress_init(void) calloc(1, sizeof(struct state_libavcodec_decompress)); s->global_lavcd_lock = rm_acquire_shared_lock(LAVCD_LOCK_NAME); - if (log_level >= LOG_LEVEL_VERBOSE) { - av_log_set_level(AV_LOG_VERBOSE); - } + av_log_set_level((log_level - 1) * 8); #if LIBAVCODEC_VERSION_INT <= AV_VERSION_INT(58, 9, 100) /* register all the codecs (you can also register only the codec diff --git a/src/video_display/ndi.c b/src/video_display/ndi.c index 2485589a4..d4a073164 100644 --- a/src/video_display/ndi.c +++ b/src/video_display/ndi.c @@ -259,7 +259,7 @@ static int display_ndi_get_property(void *state, int property, void *val, size_t { assert(*len == sizeof(struct audio_desc)); struct audio_desc *desc = (struct audio_desc *) val; - desc->bps = 4; + desc->bps = desc->bps <= 2 ? 2 : 4; desc->codec = AC_PCM; } break; @@ -269,22 +269,28 @@ static int display_ndi_get_property(void *state, int property, void *val, size_t return TRUE; } +#define NDI_SEND_AUDIO(frame, bit_depth) do { \ + assert((frame)->bps * 8 == (bit_depth)); \ + NDIlib_audio_frame_interleaved_ ## bit_depth ## s_t NDI_audio_frame = { 0 }; \ + NDI_audio_frame.sample_rate = (frame)->sample_rate; \ + NDI_audio_frame.no_channels = (frame)->ch_count; \ + NDI_audio_frame.timecode = NDIlib_send_timecode_synthesize; \ + NDI_audio_frame.p_data = (int ## bit_depth ## _t *) (frame)->data; \ + NDI_audio_frame.no_samples = (frame)->data_len / (frame)->ch_count / ((bit_depth) / 8); \ + \ + NDIlib_util_send_send_audio_interleaved_ ## bit_depth ## s(s->pNDI_send, &NDI_audio_frame); \ + } while(0) + static void display_ndi_put_audio_frame(void *state, struct audio_frame *frame) { struct display_ndi *s = (struct display_ndi *) state; - assert(frame->bps == 4); - float *tmp = malloc(frame->data_len); - NDIlib_audio_frame_v2_t NDI_audio_frame = { 0 }; - NDI_audio_frame.sample_rate = frame->sample_rate; - NDI_audio_frame.no_channels = frame->ch_count; - NDI_audio_frame.timecode = NDIlib_send_timecode_synthesize; - NDI_audio_frame.p_data = tmp; - NDI_audio_frame.channel_stride_in_bytes = frame->data_len / frame->ch_count; - NDI_audio_frame.no_samples = frame->data_len / frame->ch_count / sizeof(float); - int2float((char *) tmp, frame->data, frame->data_len); - - NDIlib_send_send_audio_v2(s->pNDI_send, &NDI_audio_frame); - free(tmp); + switch (frame->bps * 8) { +#define HANDLE_CASE(b) case b: NDI_SEND_AUDIO(frame, b); break; + HANDLE_CASE(16) + HANDLE_CASE(32) + default: + abort(); + } } static int display_ndi_reconfigure_audio(void *state, int quant_samples, int channels, diff --git a/src/video_display/pipe.cpp b/src/video_display/pipe.cpp index 7bba268b1..15b57635d 100644 --- a/src/video_display/pipe.cpp +++ b/src/video_display/pipe.cpp @@ -39,6 +39,7 @@ #include "config_unix.h" #include "config_win32.h" +#include #include #include @@ -50,6 +51,7 @@ #include "video_display.h" #include "video_display/pipe.hpp" +using std::cout; using std::list; using std::mutex; using std::lock_guard; @@ -57,9 +59,10 @@ using std::lock_guard; struct state_pipe { struct module *parent; frame_recv_delegate *delegate; - struct video_desc desc; - list audio_frames; - mutex audio_lock; + codec_t decode_to; + struct video_desc desc{}; + list audio_frames{}; + mutex audio_lock{}; }; static struct display *display_pipe_fork(void *state) @@ -74,6 +77,11 @@ static struct display *display_pipe_fork(void *state) if (rc == 0) return out; else return NULL; } +static void display_pipe_usage() { + cout << "Usage:\n" + "\t-d pipe:[:codec=]\n"; +} + /** * @note * Audio is always received regardless if enabled in flags. @@ -81,16 +89,34 @@ static struct display *display_pipe_fork(void *state) static void *display_pipe_init(struct module *parent, const char *fmt, unsigned int flags) { UNUSED(flags); + codec_t decode_to = UYVY; frame_recv_delegate *delegate; if (!fmt || strlen(fmt) == 0 || strcmp(fmt, "help") == 0) { fprintf(stderr, "Pipe dummy video driver. For internal usage - please do not use.\n"); + if (fmt != nullptr && strcmp(fmt, "help") == 0) { + display_pipe_usage(); + } return nullptr; } sscanf(fmt, "%p", &delegate); + if (strchr(fmt, ':') != nullptr) { + fmt = strchr(fmt, ':') + 1; + if (strstr(fmt, "codec=") == fmt) { + const char *codec_name = fmt + strlen("codec="); + decode_to = get_codec_from_name(codec_name); + if (decode_to == VIDEO_CODEC_NONE) { + LOG(LOG_LEVEL_ERROR) << "Wrong codec name: " << codec_name << "\n"; + return nullptr; + } + } else { + display_pipe_usage(); + return nullptr; + } + } - struct state_pipe *s = new state_pipe{parent, delegate, video_desc(), {}, {}}; + auto *s = new state_pipe{parent, delegate, decode_to}; return s; } @@ -180,20 +206,17 @@ static void display_pipe_run(void *state) static int display_pipe_get_property(void *state, int property, void *val, size_t *len) { - UNUSED(state); - codec_t codecs[] = {UYVY}; + auto *s = static_cast(state); enum interlacing_t supported_il_modes[] = {PROGRESSIVE, INTERLACED_MERGED, SEGMENTED_FRAME}; int rgb_shift[] = {0, 8, 16}; switch (property) { case DISPLAY_PROPERTY_CODECS: - if(sizeof(codecs) <= *len) { - memcpy(val, codecs, sizeof(codecs)); - } else { + if(sizeof(codec_t) > *len) { return FALSE; } - - *len = sizeof(codecs); + memcpy(val, &s->decode_to, sizeof(s->decode_to)); + *len = sizeof s->decode_to; break; case DISPLAY_PROPERTY_RGB_SHIFT: if(sizeof(rgb_shift) > *len) { diff --git a/src/video_display/sdl2.cpp b/src/video_display/sdl2.cpp index 53afd522d..96be70270 100644 --- a/src/video_display/sdl2.cpp +++ b/src/video_display/sdl2.cpp @@ -744,6 +744,16 @@ static void display_sdl2_new_message(struct module *mod) SDL_PushEvent(&event); } +static void display_sdl2_put_audio_frame([[maybe_unused]] void *state, [[maybe_unused]] struct audio_frame *frame) +{ +} + +static int display_sdl2_reconfigure_audio([[maybe_unused]] void *state, [[maybe_unused]] int quant_samples, + [[maybe_unused]] int channels, [[maybe_unused]] int sample_rate) +{ + return FALSE; +} + static const struct video_display_info display_sdl2_info = { [](struct device_info **available_cards, int *count, void (**deleter)(void *)) { UNUSED(deleter); @@ -760,8 +770,8 @@ static const struct video_display_info display_sdl2_info = { display_sdl2_putf, display_sdl2_reconfigure, display_sdl2_get_property, - NULL, - NULL, + display_sdl2_put_audio_frame, + display_sdl2_reconfigure_audio, DISPLAY_NEEDS_MAINLOOP, }; diff --git a/src/video_rxtx/h264_sdp.cpp b/src/video_rxtx/h264_sdp.cpp index 4714d64c6..c0c93a58b 100644 --- a/src/video_rxtx/h264_sdp.cpp +++ b/src/video_rxtx/h264_sdp.cpp @@ -82,7 +82,7 @@ h264_sdp_video_rxtx::h264_sdp_video_rxtx(std::map const &p } LOG(LOG_LEVEL_WARNING) << "Warning: SDP support is experimental only. Things may be broken - feel free to report them but the support may be limited.\n"; - m_sdp = new_sdp(params.at("force_ip_version").i, m_requested_receiver.c_str()); + m_sdp = new_sdp(rtp_is_ipv6(m_network_devices[0]) ? 6 : 4 , m_requested_receiver.c_str()); m_saved_addr = m_requested_receiver; if (m_sdp == nullptr) { throw string("[SDP] SDP creation failed\n"); @@ -162,6 +162,12 @@ void h264_sdp_video_rxtx::sdp_add_video(codec_t codec) #endif } +/** + * @note + * This function sets compression just after first frame is received. The delayed initialization is to allow devices + * producing H.264/JPEG natively (eg. v4l2) to be passed untouched to transport. Fallback H.264 is applied when uncompressed + * stream is detected. + */ void h264_sdp_video_rxtx::send_frame(shared_ptr tx_frame) { if (!is_codec_opaque(tx_frame->color_spec)) { diff --git a/src/video_rxtx/rtp.cpp b/src/video_rxtx/rtp.cpp index 4e1484b5f..ff45c8886 100644 --- a/src/video_rxtx/rtp.cpp +++ b/src/video_rxtx/rtp.cpp @@ -272,7 +272,7 @@ void rtp_video_rxtx::display_buf_increase_warning(int size) log_msg(LOG_LEVEL_VERBOSE, "\n***\n" "Unable to set buffer size to %d B.\n" #if defined WIN32 - "See https://github.com/CESNET/UltraGrid/wiki/Extending-Network-Buffers-%28Windows%29 for details.\n", + "See https://github.com/CESNET/UltraGrid/wiki/Extending-Network-Buffers-%%28Windows%%29 for details.\n", #else "Please set net.core.rmem_max value to %d or greater. (see also\n" "https://github.com/CESNET/UltraGrid/wiki/OS-Setup-UltraGrid)\n" diff --git a/test/run_tests.cpp b/test/run_tests.cpp index c81a3971c..25917046a 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -144,7 +144,7 @@ static bool run_unit_tests() int main(int argc, char **argv) { struct init_data *init = nullptr; - if ((init = common_preinit(argc, argv)) == nullptr) { + if ((init = common_preinit(argc, argv, nullptr)) == nullptr) { return 2; }