mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-22 07:40:24 +00:00
Merge branch 'master' into vr-devel
This commit is contained in:
11
.github/scripts/Linux/prepare.sh
vendored
11
.github/scripts/Linux/prepare.sh
vendored
@@ -15,7 +15,9 @@ echo "CUDA_HOST_COMPILER=$CUDA_HOST_COMPILER" >> $GITHUB_ENV
|
||||
|
||||
sudo sed -n 'p; /^deb /s/^deb /deb-src /p' -i /etc/apt/sources.list # for build-dep ffmpeg
|
||||
sudo apt update
|
||||
sudo apt install libcppunit-dev nvidia-cuda-toolkit
|
||||
sudo apt -y update
|
||||
sudo apt install libcppunit-dev
|
||||
sudo apt --no-install-recommends install nvidia-cuda-toolkit
|
||||
sudo apt install libglew-dev freeglut3-dev libgl1-mesa-dev
|
||||
sudo apt install libx11-dev
|
||||
sudo apt install libsdl2-dev
|
||||
@@ -29,7 +31,8 @@ sudo apt --no-install-recommends install asciidoc xmlto
|
||||
|
||||
sudo apt install libopencv-dev
|
||||
sudo apt install libglib2.0-dev libcurl4-nss-dev
|
||||
( mkdir gpujpeg/build && cd gpujpeg/build && CC=$CUDA_HOST_COMPILER cmake .. && make && sudo make install && sudo ldconfig || exit 1 )
|
||||
sudo apt install libtool # gpujpeg
|
||||
( mkdir gpujpeg/build && cd gpujpeg/build && CUDA_FLAGS=-D_FORCE_INLINES CXXFLAGS=-std=c++11 CC=$CUDA_HOST_COMPILER ../autogen.sh && make && sudo make install && sudo ldconfig || exit 1 )
|
||||
( sudo apt install uuid-dev && cd cineform-sdk/ && cmake -DBUILD_TOOLS=OFF . && make CFHDCodecStatic || exit 1 )
|
||||
sudo apt install qtbase5-dev
|
||||
sudo chmod 777 /usr/local
|
||||
@@ -66,6 +69,8 @@ cd live555
|
||||
git checkout 35c375
|
||||
./genMakefiles linux-64bit
|
||||
sudo make install CPLUSPLUS_COMPILER="c++ -DXLOCALE_NOT_USED"
|
||||
|
||||
cd ..
|
||||
|
||||
# Install cross-platform deps
|
||||
$GITHUB_WORKSPACE/.github/scripts/install-common-deps.sh
|
||||
|
||||
|
||||
10
.github/scripts/Windows/prepare_msys.sh
vendored
10
.github/scripts/Windows/prepare_msys.sh
vendored
@@ -5,8 +5,8 @@ set -ex
|
||||
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:/usr/local/share/pkgconfig:/mingw64/lib/pkgconfig
|
||||
export CPATH=/usr/local/include:/usr/include
|
||||
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/share/pkgconfig:/usr/lib/pkgconfig:/mingw64/lib/pkgconfig
|
||||
export LIBRARY_PATH=/usr/local/lib
|
||||
|
||||
CUDA_D=$(ls -d /c/Program\ Files/NVIDIA\ GPU\ Computing\ Toolkit/CUDA/*)
|
||||
@@ -37,7 +37,7 @@ EOF
|
||||
|
||||
PACMAN_INSTALL='pacman -Sy --needed --noconfirm --disable-download-timeout'
|
||||
# Install MSYS2 packages
|
||||
$PACMAN_INSTALL automake autoconf git make pkg-config mingw-w64-x86_64-toolchain mingw-w64-x86_64-cppunit unzip zip
|
||||
$PACMAN_INSTALL automake autoconf git make pkgconf mingw-w64-x86_64-binutils mingw-w64-x86_64-gcc mingw-w64-x86_64-cppunit unzip zip
|
||||
$PACMAN_INSTALL mingw-w64-x86_64-glew mingw-w64-x86_64-SDL2 mingw-w64-x86_64-freeglut
|
||||
$PACMAN_INSTALL mingw-w64-x86_64-portaudio # in case of problems build PA with --with-winapi=wmme,directx,wasapi
|
||||
$PACMAN_INSTALL mingw-w64-x86_64-glib2 mingw-w64-x86_64-curl # RTSP capture
|
||||
@@ -45,6 +45,7 @@ pacman -Scc --noconfirm # make some free space
|
||||
$PACMAN_INSTALL mingw-w64-x86_64-qt5
|
||||
$PACMAN_INSTALL mingw-w64-x86_64-imagemagick mingw-w64-x86_64-opencv
|
||||
$PACMAN_INSTALL p7zip
|
||||
$PACMAN_INSTALL libtool # PCP
|
||||
pacman -Scc --noconfirm
|
||||
|
||||
# Build AJA wrapper if we have SDK
|
||||
@@ -75,3 +76,6 @@ wget --no-verbose https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full-shared.
|
||||
# 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 )
|
||||
|
||||
# Install cross-platform deps
|
||||
$GITHUB_WORKSPACE/.github/scripts/install-common-deps.sh
|
||||
|
||||
|
||||
26
.github/scripts/install-common-deps.sh
vendored
Executable file
26
.github/scripts/install-common-deps.sh
vendored
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh -eux
|
||||
|
||||
case "$(uname -s)" in
|
||||
CYGWIN*|MINGW32*|MSYS*|MINGW*)
|
||||
SUDO=
|
||||
;;
|
||||
|
||||
*)
|
||||
SUDO="sudo "
|
||||
;;
|
||||
esac
|
||||
|
||||
install_pcp() {
|
||||
git clone https://github.com/MartinPulec/pcp.git
|
||||
(
|
||||
cd pcp
|
||||
./autogen.sh || true # autogen exits with 1
|
||||
./configure --disable-shared
|
||||
make -j 5
|
||||
${SUDO}make install
|
||||
)
|
||||
rm -rf pcp
|
||||
}
|
||||
|
||||
install_pcp
|
||||
|
||||
3
.github/scripts/macOS/prepare.sh
vendored
3
.github/scripts/macOS/prepare.sh
vendored
@@ -77,6 +77,9 @@ wget --no-verbose https://github.com/Syphon/Syphon-Framework/releases/download/5
|
||||
unzip Syphon.SDK.5.zip
|
||||
sudo cp -R 'Syphon SDK 5/Syphon.framework' /Library/Frameworks
|
||||
|
||||
# Install cross-platform deps
|
||||
$GITHUB_WORKSPACE/.github/scripts/install-common-deps.sh
|
||||
|
||||
# Remove installation files
|
||||
cd
|
||||
rm -rf $TEMP_INST
|
||||
|
||||
44
configure.ac
44
configure.ac
@@ -2162,28 +2162,6 @@ AC_SUBST(CUDA_INC)
|
||||
AC_SUBST(CUDA_COMPILER)
|
||||
AC_SUBST(CUDA_COMPUTE_ARGS)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# SHM capture
|
||||
# ---------------------------------------------------------------------
|
||||
shm_cap=no
|
||||
|
||||
AC_ARG_ENABLE(shm-cap,
|
||||
[ --disable-shm-cap disable SHM capture (auto)]
|
||||
[ Requires: Linux],
|
||||
[shm_cap_req=$enableval],
|
||||
[shm_cap_req=$build_default])
|
||||
|
||||
if test $shm_cap_req != no -a \( $system = Linux -o $system = Windows \); then
|
||||
AC_DEFINE([HAVE_SHM], [1], [Build with SHM video capture])
|
||||
ADD_MODULE("vidcap_shm", "src/video_capture/shm.o", "$CUDA_LIB")
|
||||
shm_cap=yes
|
||||
fi
|
||||
|
||||
if test $shm_cap_req = yes -a $shm_cap = no; then
|
||||
AC_MSG_ERROR([SHM capture not found]);
|
||||
fi
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
# GPUJPEG
|
||||
GPUJPEG_OBJ=
|
||||
@@ -3156,6 +3134,27 @@ if test $vrg_req = yes -a $vrg = no; then
|
||||
AC_MSG_ERROR([VRG headers or library not found]);
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# PCP
|
||||
# -----------------------------------
|
||||
pcp=no
|
||||
AC_ARG_ENABLE(pcp,
|
||||
[ --disable-pcp disable PCP suppport (default is auto)]
|
||||
[ Requires: libpcp],
|
||||
[pcp_req=$enableval],
|
||||
[pcp_req=$build_default]
|
||||
)
|
||||
PKG_CHECK_MODULES([PCP], [libpcp-client], [FOUND_PCP=yes], [FOUND_PCP=no])
|
||||
if test "$FOUND_PCP" = yes -a $pcp_req != no; then
|
||||
LIBS="$PCP_LIBS $LIBS"
|
||||
AC_DEFINE([HAVE_PCP], [1], [Build with PCP support])
|
||||
pcp=yes
|
||||
fi
|
||||
|
||||
if test $pcp_req = yes -a $pcp = no; then
|
||||
AC_MSG_ERROR([PCP not found]);
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Tests
|
||||
# ---------------------------------------------------------------------
|
||||
@@ -3298,6 +3297,7 @@ RESULT=`add_column "$RESULT" "Blank capture filter" $blank $?`
|
||||
RESULT=`add_column "$RESULT" "GPU accelerated LDGM" $ldgm_gpu $?`
|
||||
RESULT=`add_column "$RESULT" "iHDTV support" $ihdtv $?`
|
||||
RESULT=`add_column "$RESULT" "MCU-like video mixer" $video_mix $?`
|
||||
RESULT=`add_column "$RESULT" "PCP NAT traversal" $pcp $?`
|
||||
RESULT=`add_column "$RESULT" "Resize capture filter" $resize $?`
|
||||
RESULT=`add_column "$RESULT" "RTSP server" $rtsp_server $?`
|
||||
RESULT=`add_column "$RESULT" "Scale postprocessor" $scale $?`
|
||||
|
||||
2
gpujpeg
2
gpujpeg
Submodule gpujpeg updated: 3d3731f30c...b7bc71cbb0
@@ -241,7 +241,8 @@ static const comp_type_t b_cb = 2.112402 * (1<<COMP_BASE);
|
||||
#define YCBCR_TO_B_709_SCALED(y, cb, cr) ((y) /* * b_y */ + (cb) * b_cb /* + (cr) * b_cr */)
|
||||
/// @}
|
||||
|
||||
#define FORMAT_RGBA(r, g, b, depth) (CLAMP_FULL((r), (depth)) << rgb_shift[R] | CLAMP_FULL((g), (depth)) << rgb_shift[G] | CLAMP_FULL((b), (depth)) << rgb_shift[B])
|
||||
#define FORMAT_RGBA(r, g, b, depth) (~(0xFFU << (rgb_shift[R]) | 0xFFU << (rgb_shift[G]) | 0xFFU << (rgb_shift[B])) | \
|
||||
(CLAMP_FULL((r), (depth)) << rgb_shift[R] | CLAMP_FULL((g), (depth)) << rgb_shift[G] | CLAMP_FULL((b), (depth)) << rgb_shift[B]))
|
||||
|
||||
static void uyvy_to_yuv420p(AVFrame * __restrict out_frame, unsigned char * __restrict in_data, int width, int height)
|
||||
{
|
||||
@@ -1746,13 +1747,11 @@ static void nv12_to_rgb32(char * __restrict dst_buffer, AVFrame * __restrict in_
|
||||
|
||||
/**
|
||||
* Changes pixel format from planar 8-bit YUV to packed RGB/A.
|
||||
* Color space is assumed ITU-T Rec. 609. YUV is expected to be full scale (aka in JPEG).
|
||||
* Color space is assumed ITU-T Rec. 709 limited range.
|
||||
*/
|
||||
static inline void yuv8p_to_rgb(int subsampling, char * __restrict dst_buffer, AVFrame * __restrict in_frame,
|
||||
int width, int height, int pitch, int * __restrict rgb_shift, bool rgba)
|
||||
{
|
||||
UNUSED(rgb_shift);
|
||||
UNUSED(subsampling);
|
||||
for(int y = 0; y < height / 2; ++y) {
|
||||
unsigned char *src_y1 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * y * 2;
|
||||
unsigned char *src_y2 = (unsigned char *) in_frame->data[0] + in_frame->linesize[0] * (y * 2 + 1);
|
||||
|
||||
13
src/main.cpp
13
src/main.cpp
@@ -734,10 +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'},
|
||||
{"nat-traverse", optional_argument, nullptr, 'N'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
const char *optstring = "d:t:m:r:s:v46c:hM:Np:f:P:l:A:V";
|
||||
const char *optstring = "d:t:m:r:s:v46c:hM:N::p:f:P:l:A:V";
|
||||
|
||||
const char *audio_protocol = "ultragrid_rtp";
|
||||
const char *audio_protocol_opts = "";
|
||||
@@ -746,7 +746,7 @@ int main(int argc, char *argv[])
|
||||
const char *video_protocol_opts = "";
|
||||
|
||||
const char *log_opt = nullptr;
|
||||
bool setup_nat_traverse = false;
|
||||
const char *nat_traverse_config = nullptr;
|
||||
struct ug_nat_traverse *nat_traverse = nullptr;
|
||||
|
||||
// First we need to set verbosity level prior to everything else.
|
||||
@@ -1098,7 +1098,7 @@ int main(int argc, char *argv[])
|
||||
print_video_codecs();
|
||||
EXIT(EXIT_SUCCESS);
|
||||
case 'N':
|
||||
setup_nat_traverse = true;
|
||||
nat_traverse_config = optarg == nullptr ? "" : optarg;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
@@ -1249,8 +1249,9 @@ 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);
|
||||
if ((nat_traverse = start_nat_traverse(nat_traverse_config, video_rx_port, audio_rx_port)) == nullptr) {
|
||||
exit_uv(1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uv.audio = audio_cfg_init (&uv.root_module, audio_host, audio_rx_port,
|
||||
|
||||
433
src/utils/nat.c
433
src/utils/nat.c
@@ -41,24 +41,272 @@
|
||||
#include "config_unix.h"
|
||||
#include "config_win32.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef HAVE_PCP
|
||||
#include <pcp-client/pcp.h>
|
||||
#endif // defined HAVE_PCP
|
||||
|
||||
#include "debug.h"
|
||||
#include "rtp/net_udp.h" // socket_error
|
||||
#include "utils/color_out.h"
|
||||
#include "utils/nat.h"
|
||||
#include "utils/net.h"
|
||||
|
||||
#define ENABLE_STRNATPMPERR 1
|
||||
#define STATICLIB 1
|
||||
#include "ext-deps/libnatpmp-20150609/natpmp.h"
|
||||
|
||||
#define ALLOCATION_TIMEOUT (4 * 3600)
|
||||
#define DEFAULT_ALLOCATION_TIMEOUT_S 1800
|
||||
#define MOD_NAME "[NAT] "
|
||||
#define PREALLOCATE_S 5 ///< number of seconds that repeated allocation is performed before timeout
|
||||
|
||||
struct ug_nat_traverse {
|
||||
enum {
|
||||
enum traverse_t {
|
||||
UG_NAT_TRAVERSE_NONE,
|
||||
UG_NAT_TRAVERSE_PCP,
|
||||
UG_NAT_TRAVERSE_FIRST = UG_NAT_TRAVERSE_PCP,
|
||||
UG_NAT_TRAVERSE_NAT_PMP,
|
||||
UG_NAT_TRAVERSE_LAST = UG_NAT_TRAVERSE_NAT_PMP,
|
||||
} traverse;
|
||||
union {
|
||||
#ifdef HAVE_PCP
|
||||
struct pcp_state {
|
||||
pcp_ctx_t *ctx;
|
||||
pcp_flow_t *audio_flow;
|
||||
pcp_flow_t *video_flow;
|
||||
} pcp_state;
|
||||
#endif // defined HAVE_PCP
|
||||
} nat_state;
|
||||
int audio_rx_port;
|
||||
int video_rx_port;
|
||||
int allocation_duration;
|
||||
|
||||
pthread_t keepalive_thread;
|
||||
bool keepalive_should_exit;
|
||||
pthread_mutex_t keepalive_mutex;
|
||||
pthread_cond_t keepalive_cv;
|
||||
};
|
||||
|
||||
static bool setup_pcp(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime);
|
||||
static void done_pcp(struct ug_nat_traverse *state);
|
||||
static bool setup_nat_pmp(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime);
|
||||
static void done_nat_pmp(struct ug_nat_traverse *state);
|
||||
|
||||
static const struct nat_traverse_info_t {
|
||||
const char *name_short; ///< for command-line specifiction
|
||||
const char *name_long; ///< for output
|
||||
bool (*init)(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime);
|
||||
void (*done)(struct ug_nat_traverse *state);
|
||||
} nat_traverse_info[] = {
|
||||
[ UG_NAT_TRAVERSE_PCP ] = { "pcp", "PCP", setup_pcp, done_pcp },
|
||||
[ UG_NAT_TRAVERSE_NAT_PMP ] = { "natpmp", "NAT PMP", setup_nat_pmp, done_nat_pmp },
|
||||
};
|
||||
|
||||
#ifdef HAVE_PCP
|
||||
static int check_flow_info(pcp_flow_t* f)
|
||||
{
|
||||
size_t cnt=0;
|
||||
pcp_flow_info_t *info_buf = NULL;
|
||||
pcp_flow_info_t *ret = pcp_flow_get_info(f,&cnt);
|
||||
int ret_val = 2;
|
||||
info_buf=ret;
|
||||
for (; cnt>0; cnt--, ret++) {
|
||||
switch(ret->result)
|
||||
{
|
||||
case pcp_state_succeeded:
|
||||
printf("\nFlow signaling succeeded.\n");
|
||||
ret_val = 0;
|
||||
break;
|
||||
case pcp_state_short_lifetime_error:
|
||||
printf("\nFlow signaling failed. Short lifetime error received.\n");
|
||||
ret_val = 3;
|
||||
break;
|
||||
case pcp_state_failed:
|
||||
printf("\nFlow signaling failed.\n");
|
||||
ret_val = 4;
|
||||
break;
|
||||
case pcp_state_processing:
|
||||
printf("\nFlow signaling processing.\n");
|
||||
continue;
|
||||
case pcp_state_partial_result:
|
||||
printf("\nFlow signaling partial result.\n");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (info_buf) {
|
||||
free(info_buf);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static const char* decode_fresult(pcp_fstate_e s)
|
||||
{
|
||||
switch (s) {
|
||||
case pcp_state_short_lifetime_error:
|
||||
return "slerr";
|
||||
case pcp_state_succeeded:
|
||||
return "succ";
|
||||
case pcp_state_failed:
|
||||
return "fail";
|
||||
default:
|
||||
return "proc";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
static char *ctime_r(const time_t *timep, char *buf)
|
||||
{
|
||||
ctime_s(buf, 26, timep);
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void print_ext_addr(pcp_flow_t* f)
|
||||
{
|
||||
size_t cnt=0;
|
||||
pcp_flow_info_t *info_buf = NULL;
|
||||
pcp_flow_info_t *ret = pcp_flow_get_info(f,&cnt);
|
||||
info_buf=ret;
|
||||
|
||||
printf("%-20s %-4s %-20s %5s %-20s %5s %-20s %5s %3s %5s %s\n",
|
||||
"PCP Server IP",
|
||||
"Prot",
|
||||
"Int. IP", "port",
|
||||
"Dst. IP", "port",
|
||||
"Ext. IP", "port",
|
||||
"Res", "State","Ends");
|
||||
for (; cnt>0; cnt--, ret++) {
|
||||
char ntop_buffs[4][INET6_ADDRSTRLEN];
|
||||
char timebuf[32];
|
||||
|
||||
printf("%-20s %-4s %-20s %5hu %-20s %5hu %-20s %5hu %3d %5s %s",
|
||||
inet_ntop(AF_INET6, &ret->pcp_server_ip, ntop_buffs[0],
|
||||
sizeof(ntop_buffs[0])),
|
||||
ret->protocol == IPPROTO_TCP ? "TCP" : (
|
||||
ret->protocol == IPPROTO_UDP ? "UDP" : "UNK"),
|
||||
inet_ntop(AF_INET6, &ret->int_ip, ntop_buffs[1],
|
||||
sizeof(ntop_buffs[1])),
|
||||
ntohs(ret->int_port),
|
||||
inet_ntop(AF_INET6, &ret->dst_ip, ntop_buffs[2],
|
||||
sizeof(ntop_buffs[2])),
|
||||
ntohs(ret->dst_port),
|
||||
inet_ntop(AF_INET6, &ret->ext_ip, ntop_buffs[3],
|
||||
sizeof(ntop_buffs[3])),
|
||||
ntohs(ret->ext_port),
|
||||
ret->pcp_result_code,
|
||||
decode_fresult(ret->result),
|
||||
ret->recv_lifetime_end == 0 ? " -\n" :
|
||||
ctime_r(&ret->recv_lifetime_end, timebuf));
|
||||
}
|
||||
if (info_buf) {
|
||||
free(info_buf);
|
||||
}
|
||||
}
|
||||
#endif // defined HAVE_PCP
|
||||
|
||||
static void done_pcp(struct ug_nat_traverse *state)
|
||||
{
|
||||
#ifdef HAVE_PCP
|
||||
struct pcp_state *s = &state->nat_state.pcp_state;
|
||||
pcp_terminate(s->ctx, 1);
|
||||
#else
|
||||
UNUSED(state);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define PCP_WAIT_MS 500
|
||||
|
||||
#define NAT_ASSERT_EQ(expr, val) { int rc = expr; if (rc != (val)) { socket_error(#expr); return false; } }
|
||||
#define NAT_ASSERT_NEQ(expr, val) { int rc = expr; if (rc == (val)) { socket_error(#expr); return false; } }
|
||||
static bool get_outbound_ip(struct sockaddr_in *out) {
|
||||
struct sockaddr_in dst = { 0 };
|
||||
socklen_t src_len = sizeof *out;
|
||||
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = htons(80);
|
||||
NAT_ASSERT_EQ(inet_pton(AF_INET, "93.184.216.34", &dst.sin_addr.s_addr), 1);
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
NAT_ASSERT_NEQ(fd, -1);
|
||||
NAT_ASSERT_EQ(connect(fd, (struct sockaddr *) &dst, sizeof dst), 0);
|
||||
NAT_ASSERT_EQ(getsockname(fd, (struct sockaddr *) out, &src_len), 0);
|
||||
CLOSESOCKET(fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_pcp(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime)
|
||||
{
|
||||
#ifdef HAVE_PCP
|
||||
struct pcp_state *s = &state->nat_state.pcp_state;
|
||||
struct sockaddr_in src = { 0 };
|
||||
|
||||
s->ctx = pcp_init(ENABLE_AUTODISCOVERY, NULL);
|
||||
// handle errors
|
||||
|
||||
// get our outbound IP address
|
||||
if (!get_outbound_ip(&src)) {
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "PCP - cannot get outbound address!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
if (video_rx_port) {
|
||||
src.sin_port = htons(video_rx_port);
|
||||
s->video_flow = pcp_new_flow(s->ctx, (struct sockaddr*) &src, NULL, NULL, IPPROTO_UDP, lifetime, NULL);
|
||||
if (s->video_flow == NULL) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
if (audio_rx_port) {
|
||||
src.sin_port = htons(audio_rx_port);
|
||||
s->audio_flow = pcp_new_flow(s->ctx, (struct sockaddr*) &src, NULL, NULL, IPPROTO_UDP, lifetime, NULL);
|
||||
if (s->audio_flow == NULL) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "PCP - cannot create flow!\n");
|
||||
done_pcp(state);
|
||||
return false;
|
||||
}
|
||||
if (video_rx_port != 0) {
|
||||
pcp_wait(s->video_flow, PCP_WAIT_MS, 0);
|
||||
ret = ret && check_flow_info(s->video_flow) == 0;
|
||||
if (ret) {
|
||||
print_ext_addr(s->video_flow);
|
||||
} else {
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "PCP - cannot create video flow!\n");
|
||||
}
|
||||
}
|
||||
if (audio_rx_port != 0) {
|
||||
pcp_wait(s->audio_flow, PCP_WAIT_MS, 0);
|
||||
ret = ret && check_flow_info(s->audio_flow) == 0;
|
||||
if (ret) {
|
||||
print_ext_addr(s->audio_flow);
|
||||
} else {
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "PCP - cannot create video flow!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
done_pcp(state);
|
||||
}
|
||||
|
||||
return ret;
|
||||
#else
|
||||
UNUSED(state);
|
||||
UNUSED(video_rx_port);
|
||||
UNUSED(audio_rx_port);
|
||||
UNUSED(lifetime);
|
||||
log_msg(LOG_LEVEL_WARNING, MOD_NAME "PCP support not compiled in!\n");
|
||||
return false;
|
||||
#endif // defined HAVE_PCP
|
||||
}
|
||||
|
||||
static bool nat_pmp_add_mapping(natpmp_t *natpmp, int privateport, int publicport, int lifetime)
|
||||
{
|
||||
if (privateport == 0 && publicport == 0) {
|
||||
@@ -70,8 +318,8 @@ static bool nat_pmp_add_mapping(natpmp_t *natpmp, int privateport, int publicpor
|
||||
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",
|
||||
log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, MOD_NAME
|
||||
"NAT PMP - sendnewportmappingrequest returned %d (%s)\n",
|
||||
r, r == 12 ? "SUCCESS" : "FAILED");
|
||||
if (r < 0) {
|
||||
return false;
|
||||
@@ -86,16 +334,16 @@ static bool nat_pmp_add_mapping(natpmp_t *natpmp, int privateport, int publicpor
|
||||
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",
|
||||
log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "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",
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "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 "
|
||||
log_msg(LOG_LEVEL_INFO, MOD_NAME "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" :
|
||||
@@ -103,28 +351,29 @@ static bool nat_pmp_add_mapping(natpmp_t *natpmp, int privateport, int publicpor
|
||||
"UNKNOWN"),
|
||||
response.pnu.newportmapping.privateport,
|
||||
response.pnu.newportmapping.lifetime);
|
||||
log_msg(LOG_LEVEL_DEBUG, "[NAT PMP] epoch = %u\n", response.epoch);
|
||||
log_msg(LOG_LEVEL_DEBUG, MOD_NAME "NAT PMP - epoch = %u\n", response.epoch);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_nat_pmp(int video_rx_port, int audio_rx_port, int lifetime)
|
||||
static bool setup_nat_pmp(struct ug_nat_traverse *state, int video_rx_port, int audio_rx_port, int lifetime)
|
||||
{
|
||||
UNUSED(state);
|
||||
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,
|
||||
log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, MOD_NAME "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));
|
||||
log_msg(LOG_LEVEL_NOTICE, MOD_NAME "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",
|
||||
log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, MOD_NAME "NAT PMP - sendpublicaddressrequest returned %d (%s)\n",
|
||||
r, r == 2 ? "SUCCESS" : "FAILED");
|
||||
if (r < 0) {
|
||||
return false;
|
||||
@@ -139,17 +388,17 @@ static bool setup_nat_pmp(int video_rx_port, int audio_rx_port, int lifetime)
|
||||
getnatpmprequesttimeout(&natpmp, &timeout);
|
||||
r = select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
|
||||
if(r<0) {
|
||||
log_msg(LOG_LEVEL_ERROR, "[NAT PMP] select()\n");
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "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",
|
||||
log_msg(r < 0 ? LOG_LEVEL_ERROR : LOG_LEVEL_VERBOSE, MOD_NAME "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",
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "NAT PMP - readnatpmpresponseorretry() failed : %s\n",
|
||||
strnatpmperr(r));
|
||||
log_msg(LOG_LEVEL_ERROR, "[NAT PMP] errno=%d '%s'\n",
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "NAT PMP - errno=%d '%s'\n",
|
||||
sav_errno, strerror(sav_errno));
|
||||
}
|
||||
} while(r==NATPMP_TRYAGAIN);
|
||||
@@ -158,8 +407,8 @@ static bool setup_nat_pmp(int video_rx_port, int audio_rx_port, int lifetime)
|
||||
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);
|
||||
log_msg(LOG_LEVEL_NOTICE, MOD_NAME "NAT PMP - Public IP address: %s\n", inet_ntoa(response.pnu.publicaddress.addr));
|
||||
log_msg(LOG_LEVEL_DEBUG, MOD_NAME "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)) {
|
||||
@@ -167,35 +416,151 @@ static bool setup_nat_pmp(int video_rx_port, int audio_rx_port, int lifetime)
|
||||
}
|
||||
|
||||
r = closenatpmp(&natpmp);
|
||||
log_msg(LOG_LEVEL_VERBOSE, "[NAT PMP] closenatpmp() returned %d (%s)\n", r, r==0?"SUCCESS":"FAILED");
|
||||
log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "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);
|
||||
static void done_nat_pmp(struct ug_nat_traverse *state) {
|
||||
setup_nat_pmp(state, state->video_rx_port, state->audio_rx_port, 0);
|
||||
}
|
||||
|
||||
static void *nat_traverse_keepalive(void *state) {
|
||||
struct ug_nat_traverse *s = state;
|
||||
|
||||
struct timespec timeout = { .tv_sec = time(NULL) + s->allocation_duration - PREALLOCATE_S, .tv_nsec = 0 };
|
||||
|
||||
while (1) {
|
||||
pthread_mutex_lock(&s->keepalive_mutex);
|
||||
int rc = 0;
|
||||
if (!s->keepalive_should_exit) {
|
||||
rc = pthread_cond_timedwait(&s->keepalive_cv, &s->keepalive_mutex, &timeout);
|
||||
}
|
||||
pthread_mutex_unlock(&s->keepalive_mutex);
|
||||
if (s->keepalive_should_exit) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc != ETIMEDOUT) {
|
||||
perror(__func__);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nat_traverse_info[s->traverse].init(s, s->video_rx_port, s->audio_rx_port, s->allocation_duration)) {
|
||||
log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Mapping renewed successfully for %d seconds.\n", s->allocation_duration);
|
||||
timeout.tv_sec = time(NULL) + s->allocation_duration - PREALLOCATE_S;
|
||||
} else {
|
||||
log_msg(LOG_LEVEL_WARNING, MOD_NAME "Mapping renewal failed! Trying again in 5 seconds\n");
|
||||
timeout.tv_sec = time(NULL) + 5;
|
||||
}
|
||||
}
|
||||
// other techniques may follow
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param config NULL - do not enable NAT traversal
|
||||
* "" - enable with default arguments
|
||||
* other - start with configuration
|
||||
* @returns state, NULL on error (or help)
|
||||
*/
|
||||
struct ug_nat_traverse *start_nat_traverse(const char *config, int video_rx_port, int audio_rx_port)
|
||||
{
|
||||
if (config == NULL) {
|
||||
if (video_rx_port != 0 || audio_rx_port != 0) {
|
||||
struct sockaddr_in out;
|
||||
if (get_outbound_ip(&out) && is_addr_private((struct sockaddr *) &out)) {
|
||||
log_msg(LOG_LEVEL_WARNING, MOD_NAME "Private outbound IPv4 address detected and bound to a non-dynamic port. Consider adding '-N' option for NAT traversal.\n");
|
||||
}
|
||||
|
||||
}
|
||||
return calloc(1, sizeof(struct ug_nat_traverse));
|
||||
}
|
||||
|
||||
assert(video_rx_port >= 0 && video_rx_port <= 65535 && audio_rx_port >= 0 && audio_rx_port <= 65535);
|
||||
|
||||
if (strcmp(config, "help") == 0) {
|
||||
printf("Usage:\n");
|
||||
color_out(COLOR_OUT_BOLD | COLOR_OUT_RED, "\t-N");
|
||||
color_out(COLOR_OUT_BOLD, "[protocol[:renewal-interval]]\n");
|
||||
printf("where:\n");
|
||||
color_out(COLOR_OUT_BOLD, "\tprotocol");
|
||||
printf(" - one of:");
|
||||
for (int i = UG_NAT_TRAVERSE_FIRST; i <= UG_NAT_TRAVERSE_LAST; ++i) {
|
||||
color_out(COLOR_OUT_BOLD, " %s", nat_traverse_info[i].name_short);
|
||||
}
|
||||
printf("\n");
|
||||
color_out(COLOR_OUT_BOLD, "\trenewal-interval");
|
||||
printf(" - mapping renew interval (in seconds, min: %d)\n", PREALLOCATE_S + 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ug_nat_traverse *s = calloc(1, sizeof(struct ug_nat_traverse));
|
||||
s->audio_rx_port = audio_rx_port;
|
||||
s->video_rx_port = video_rx_port;
|
||||
s->allocation_duration = DEFAULT_ALLOCATION_TIMEOUT_S;
|
||||
|
||||
bool not_found = true;
|
||||
char protocol[strlen(config) + 1];
|
||||
strcpy(protocol, config);
|
||||
if (strchr(protocol, ':') != NULL) {
|
||||
s->allocation_duration = atoi(strchr(protocol, ':') + 1);
|
||||
if (s->allocation_duration < PREALLOCATE_S + 1) {
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong renewal interval: %s, minimal: %d\n", strchr(protocol, ':') + 1, PREALLOCATE_S + 1);
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
*strchr(protocol, ':') = '\0';
|
||||
}
|
||||
for (int i = UG_NAT_TRAVERSE_FIRST; i <= UG_NAT_TRAVERSE_LAST; ++i) {
|
||||
if (strlen(protocol) > 0 && strcmp(nat_traverse_info[i].name_short, protocol) != 0) {
|
||||
continue;
|
||||
}
|
||||
log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Trying: %s\n", nat_traverse_info[i].name_long);
|
||||
not_found = false;
|
||||
if (nat_traverse_info[i].init(s, video_rx_port, audio_rx_port, s->allocation_duration)) {
|
||||
s->traverse = i;
|
||||
log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Set NAT traversal with %s for %d seconds (auto-renewed). Sender can send to external IP address.\n", nat_traverse_info[i].name_long, s->allocation_duration);
|
||||
break;
|
||||
}
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s initialization failed!\n", nat_traverse_info[i].name_long);
|
||||
}
|
||||
|
||||
if (s->traverse == UG_NAT_TRAVERSE_NONE) {
|
||||
if (not_found) {
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Wrong module name: %s.\n", protocol);
|
||||
} else {
|
||||
log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not initialize any NAT traversal.\n");
|
||||
}
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
rc |= pthread_mutex_init(&s->keepalive_mutex, NULL);
|
||||
rc |= pthread_cond_init(&s->keepalive_cv, NULL);
|
||||
rc |= pthread_create(&s->keepalive_thread, NULL, nat_traverse_keepalive, s);
|
||||
assert(rc == 0);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
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;
|
||||
pthread_mutex_lock(&s->keepalive_mutex);
|
||||
s->keepalive_should_exit = true;
|
||||
pthread_mutex_unlock(&s->keepalive_mutex);
|
||||
pthread_cond_signal(&s->keepalive_cv);
|
||||
pthread_join(s->keepalive_thread, NULL);
|
||||
|
||||
pthread_mutex_destroy(&s->keepalive_mutex);
|
||||
pthread_cond_destroy(&s->keepalive_cv);
|
||||
|
||||
if (nat_traverse_info[s->traverse].done) {
|
||||
nat_traverse_info[s->traverse].done(s);
|
||||
}
|
||||
|
||||
free(s);
|
||||
|
||||
@@ -48,7 +48,7 @@ extern "C" {
|
||||
|
||||
struct ug_nat_traverse;
|
||||
|
||||
struct ug_nat_traverse *start_nat_traverse(int video_rx_port, int audio_rx_port);
|
||||
struct ug_nat_traverse *start_nat_traverse(const char *config, int video_rx_port, int audio_rx_port);
|
||||
void stop_nat_traverse(struct ug_nat_traverse *);
|
||||
|
||||
|
||||
|
||||
@@ -120,6 +120,27 @@ bool is_addr_loopback(struct sockaddr *sa)
|
||||
}
|
||||
}
|
||||
|
||||
bool is_addr_private(struct sockaddr *sa)
|
||||
{
|
||||
switch (sa->sa_family) {
|
||||
case AF_UNIX:
|
||||
return true;
|
||||
case AF_INET:
|
||||
{
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
|
||||
uint32_t addr = ntohl(sin->sin_addr.s_addr);
|
||||
return ((addr >> 24U) == 10) || ((addr >> 20U) == 2753) || ((addr >> 16U) == 49320);
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
struct sockaddr_in6 *sin = (struct sockaddr_in6 *) sa;
|
||||
return (sin->sin6_addr.s6_addr[0] & 0xFEU) == 0xFCU; // ULA
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_host_loopback(const char *hostname)
|
||||
{
|
||||
int gai_err;
|
||||
|
||||
@@ -38,6 +38,15 @@
|
||||
#ifndef UTILS_NET_H_
|
||||
#define UTILS_NET_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#endif // __cplusplus
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -45,6 +54,7 @@ extern "C" {
|
||||
struct sockaddr_storage;
|
||||
bool is_addr_linklocal(struct sockaddr *sa);
|
||||
bool is_addr_loopback(struct sockaddr *sa);
|
||||
bool is_addr_private(struct sockaddr *sa);
|
||||
bool is_addr_multicast(const char *addr);
|
||||
bool is_host_loopback(const char *hostname);
|
||||
uint16_t socket_get_recv_port(int fd);
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#if LIBGPUJPEG_API_VERSION < 11
|
||||
#error "GPUJPEG API 10 or more requested!"
|
||||
#ifndef GPUJPEG_VERSION_INT
|
||||
#error "Old GPUJPEG API detected!"
|
||||
#endif
|
||||
|
||||
#define MOD_NAME "[GPUJPEG enc.] "
|
||||
@@ -449,9 +449,10 @@ state_video_compress_gpujpeg *state_video_compress_gpujpeg::create(struct module
|
||||
|
||||
struct module * gpujpeg_compress_init(struct module *parent, const char *opts)
|
||||
{
|
||||
if (gpujpeg_version() != LIBGPUJPEG_API_VERSION) {
|
||||
LOG(LOG_LEVEL_WARNING) << "GPUJPEG API version mismatch! (" <<
|
||||
gpujpeg_version() << " vs " << LIBGPUJPEG_API_VERSION << ")\n";
|
||||
if (gpujpeg_version() >> 8 != GPUJPEG_VERSION_INT >> 8) {
|
||||
LOG(LOG_LEVEL_WARNING) << "GPUJPEG API version mismatch! (compiled: " <<
|
||||
gpujpeg_version_to_string(GPUJPEG_VERSION_INT) << ", library present: " <<
|
||||
gpujpeg_version_to_string(gpujpeg_version()) << ", required same minor version)\n";
|
||||
}
|
||||
struct state_video_compress_gpujpeg *s;
|
||||
|
||||
|
||||
@@ -122,9 +122,13 @@ static int configure_with(struct state_decompress_gpujpeg *s, struct video_desc
|
||||
|
||||
static void * gpujpeg_decompress_init(void)
|
||||
{
|
||||
if (gpujpeg_version() != LIBGPUJPEG_API_VERSION) {
|
||||
log_msg(LOG_LEVEL_WARNING, "GPUJPEG API version mismatch! (%d vs %d)\n",
|
||||
gpujpeg_version(), LIBGPUJPEG_API_VERSION);
|
||||
if (gpujpeg_version() >> 8 != GPUJPEG_VERSION_INT >> 8) {
|
||||
char ver_req[128] = "";
|
||||
char ver_lib[128] = "";
|
||||
strncpy(ver_req, gpujpeg_version_to_string(GPUJPEG_VERSION_INT), sizeof ver_req - 1);
|
||||
strncpy(ver_lib, gpujpeg_version_to_string(gpujpeg_version()), sizeof ver_lib - 1);
|
||||
log_msg(LOG_LEVEL_WARNING, "GPUJPEG API version mismatch! (compiled: %s, library present: %s, required same minor version)\n",
|
||||
ver_req, ver_lib);
|
||||
}
|
||||
|
||||
struct state_decompress_gpujpeg *s = (struct state_decompress_gpujpeg *) calloc(1, sizeof(struct state_decompress_gpujpeg));
|
||||
|
||||
@@ -178,7 +178,7 @@ static void display_dummy_put_audio_frame(void *, struct audio_frame *)
|
||||
|
||||
static int display_dummy_reconfigure_audio(void *, int, int, int)
|
||||
{
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const struct video_display_info display_dummy_info = {
|
||||
|
||||
@@ -307,19 +307,17 @@ static int display_multiplier_reconfigure(void *state, struct video_desc desc)
|
||||
|
||||
static void display_multiplier_put_audio_frame(void *state, struct audio_frame *frame)
|
||||
{
|
||||
UNUSED(state);
|
||||
UNUSED(frame);
|
||||
auto *s = static_cast<struct state_multiplier *>(state);
|
||||
|
||||
display_put_audio_frame(s->common->displays.at(0).real_display, frame);
|
||||
}
|
||||
|
||||
static int display_multiplier_reconfigure_audio(void *state, int quant_samples, int channels,
|
||||
int sample_rate)
|
||||
{
|
||||
UNUSED(state);
|
||||
UNUSED(quant_samples);
|
||||
UNUSED(channels);
|
||||
UNUSED(sample_rate);
|
||||
auto *s = static_cast<struct state_multiplier *>(state);
|
||||
|
||||
return FALSE;
|
||||
return display_reconfigure_audio(s->common->displays.at(0).real_display, quant_samples, channels, sample_rate);
|
||||
}
|
||||
|
||||
static auto display_multiplier_needs_mainloop(void *state)
|
||||
|
||||
Reference in New Issue
Block a user