[FFmpeg-devel] [PATCH 2/2] Implement dynamic loading of NewTek NDI library
Maksym Veremeyenko
verem at m1stereo.tv
Tue Feb 20 18:32:52 EET 2018
Hi
attached patch implement dynamic loading of NewTek library and drop
dependencies from NewTek SDK (if previous patch with including headers
applied)
please review/comment/apply
--
Maksym Veremeyenko
-------------- next part --------------
From 8c0337878bdb8a1ccbc56ede42686e2a4d8e882e Mon Sep 17 00:00:00 2001
From: Maksym Veremeyenko <verem at m1.tv>
Date: Tue, 20 Feb 2018 17:16:46 +0200
Subject: [PATCH 2/2] Implement dynamic loading of NewTek NDI library
---
configure | 8 +--
libavdevice/Makefile | 4 +-
libavdevice/libndi_newtek_common.c | 105 +++++++++++++++++++++++++++++++++++++
libavdevice/libndi_newtek_common.h | 4 +-
libavdevice/libndi_newtek_dec.c | 32 ++++++-----
libavdevice/libndi_newtek_enc.c | 16 ++++--
6 files changed, 144 insertions(+), 25 deletions(-)
create mode 100644 libavdevice/libndi_newtek_common.c
diff --git a/configure b/configure
index 013308c..4782c77 100755
--- a/configure
+++ b/configure
@@ -1569,7 +1569,6 @@ EXTERNAL_LIBRARY_GPL_LIST="
EXTERNAL_LIBRARY_NONFREE_LIST="
decklink
- libndi_newtek
libfdk_aac
openssl
libtls
@@ -1648,6 +1647,7 @@ EXTERNAL_LIBRARY_LIST="
mediacodec
openal
opengl
+ libndi_newtek
"
HWACCEL_AUTODETECT_LIBRARY_LIST="
@@ -3093,10 +3093,11 @@ decklink_indev_deps="decklink threads"
decklink_indev_extralibs="-lstdc++"
decklink_outdev_deps="decklink threads"
decklink_outdev_extralibs="-lstdc++"
+libndi_newtek_deps_any="libdl LoadLibrary"
libndi_newtek_indev_deps="libndi_newtek"
-libndi_newtek_indev_extralibs="-lndi"
+libndi_newtek_indev_extralibs=""
libndi_newtek_outdev_deps="libndi_newtek"
-libndi_newtek_outdev_extralibs="-lndi"
+libndi_newtek_outdev_extralibs=""
dshow_indev_deps="IBaseFilter"
dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid -loleaut32 -lshlwapi"
fbdev_indev_deps="linux_fb_h"
@@ -5866,7 +5867,6 @@ enabled cuda_sdk && require cuda_sdk cuda.h cuCtxCreate -lcuda
enabled chromaprint && require chromaprint chromaprint.h chromaprint_get_version -lchromaprint
enabled decklink && { require_header DeckLinkAPI.h &&
{ check_cpp_condition DeckLinkAPIVersion.h "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a060100" || die "ERROR: Decklink API version must be >= 10.6.1."; } }
-enabled libndi_newtek && require_header Processing.NDI.Lib.h
enabled frei0r && require_header frei0r.h
enabled gmp && require gmp gmp.h mpz_export -lgmp
enabled gnutls && require_pkg_config gnutls gnutls gnutls/gnutls.h gnutls_global_init
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index 8228d62..2d3322e 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -19,8 +19,8 @@ OBJS-$(CONFIG_BKTR_INDEV) += bktr.o
OBJS-$(CONFIG_CACA_OUTDEV) += caca.o
OBJS-$(CONFIG_DECKLINK_OUTDEV) += decklink_enc.o decklink_enc_c.o decklink_common.o
OBJS-$(CONFIG_DECKLINK_INDEV) += decklink_dec.o decklink_dec_c.o decklink_common.o
-OBJS-$(CONFIG_LIBNDI_NEWTEK_OUTDEV) += libndi_newtek_enc.o
-OBJS-$(CONFIG_LIBNDI_NEWTEK_INDEV) += libndi_newtek_dec.o
+OBJS-$(CONFIG_LIBNDI_NEWTEK_OUTDEV) += libndi_newtek_enc.o libndi_newtek_common.o
+OBJS-$(CONFIG_LIBNDI_NEWTEK_INDEV) += libndi_newtek_dec.o libndi_newtek_common.o
OBJS-$(CONFIG_DSHOW_INDEV) += dshow_crossbar.o dshow.o dshow_enummediatypes.o \
dshow_enumpins.o dshow_filter.o \
dshow_pin.o dshow_common.o
diff --git a/libavdevice/libndi_newtek_common.c b/libavdevice/libndi_newtek_common.c
new file mode 100644
index 0000000..5202993
--- /dev/null
+++ b/libavdevice/libndi_newtek_common.c
@@ -0,0 +1,105 @@
+/*
+ * NewTek NDI common code
+ * Copyright (c) 2018 Maksym Veremeyenko
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavformat/avformat.h"
+#include "libavformat/internal.h"
+#include "libavutil/opt.h"
+#include "libavutil/imgutils.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#endif
+
+#include "libndi_newtek_common.h"
+
+#define NDI_LIB_LOAD_ERROR_TEXT "\nPlease re-install the NewTek NDI Runtimes from " NDILIB_REDIST_URL " to use this functionality."
+
+const NDIlib_v3* ndi_lib_load(AVFormatContext *avctx) {
+ char *path = NULL, *e;
+ const NDIlib_v3* (*NDIlib_v3_load)(void) = NULL;
+#ifdef _WIN32
+ HMODULE
+#else
+ void*
+#endif
+ hNDILib;
+
+ e = getenv(NDILIB_REDIST_FOLDER);
+ if (!e) {
+ path = av_strdup(NDILIB_LIBRARY_NAME);
+ if (!path)
+ return NULL;
+ }
+ else {
+ int s = strlen(NDILIB_LIBRARY_NAME) + 1 + strlen(e) + 1;
+ path = av_malloc(s);
+ if (!path)
+ return NULL;
+ snprintf(path, s, "%s"
+#ifdef _WIN32
+ "\\"
+#else
+ "/"
+#endif
+ "%s", e, NDILIB_LIBRARY_NAME);
+ }
+
+
+#ifdef _WIN32
+ /* Try to load the library */
+ hNDILib = LoadLibrary(path);
+
+ if (!hNDILib)
+ av_log(avctx, AV_LOG_ERROR, "LoadLibrary(%s) failed. " NDI_LIB_LOAD_ERROR_TEXT "\n", path);
+ else {
+
+ /* get NDIlib_v3_load address */
+ *((FARPROC*)&NDIlib_v3_load) = GetProcAddress(hNDILib, "NDIlib_v3_load");
+
+ if (!NDIlib_v3_load) {
+ av_log(avctx, AV_LOG_ERROR, "GetProcAddress(NDIlib_v3_load) failed in file [%s]. " NDI_LIB_LOAD_ERROR_TEXT "\n", path);
+ FreeLibrary(hNDILib);
+ }
+ }
+#else
+ /* Try to load the library */
+ hNDILib = dlopen(path, RTLD_LOCAL | RTLD_LAZY);
+
+ if (!hNDILib)
+ av_log(avctx, AV_LOG_ERROR, "dlopen(%s) failed. " NDI_LIB_LOAD_ERROR_TEXT "\n", path);
+ else {
+
+ /* get NDIlib_v3_load address */
+ *((void**)&NDIlib_v3_load) = dlsym(hNDILib, "NDIlib_v3_load");
+
+ if (!NDIlib_v3_load) {
+ av_log(avctx, AV_LOG_ERROR, "dlsym(NDIlib_v3_load) failed in file[%s]. " NDI_LIB_LOAD_ERROR_TEXT "\n", path);
+ dlclose(hNDILib);
+ }
+ }
+#endif
+
+ av_free(path);
+
+ return NDIlib_v3_load ? NDIlib_v3_load() : NULL;
+}
diff --git a/libavdevice/libndi_newtek_common.h b/libavdevice/libndi_newtek_common.h
index 8990317..16f794d 100644
--- a/libavdevice/libndi_newtek_common.h
+++ b/libavdevice/libndi_newtek_common.h
@@ -22,7 +22,9 @@
#ifndef AVDEVICE_LIBNDI_NEWTEK_COMMON_H
#define AVDEVICE_LIBNDI_NEWTEK_COMMON_H
-#include <Processing.NDI.Lib.h>
+#include "compat/libndi_newtek/Processing.NDI.Lib.h"
+
+const NDIlib_v3* ndi_lib_load(AVFormatContext *avctx);
#define NDI_TIME_BASE 10000000
#define NDI_TIME_BASE_Q (AVRational){1, NDI_TIME_BASE}
diff --git a/libavdevice/libndi_newtek_dec.c b/libavdevice/libndi_newtek_dec.c
index 4fb7197..beeb3fc 100644
--- a/libavdevice/libndi_newtek_dec.c
+++ b/libavdevice/libndi_newtek_dec.c
@@ -35,6 +35,7 @@ struct NDIContext {
int allow_video_fields;
/* Runtime */
+ const NDIlib_v3* lib;
NDIlib_recv_create_t *recv;
NDIlib_find_instance_t ndi_find;
@@ -87,7 +88,7 @@ static int ndi_set_audio_packet(AVFormatContext *avctx, NDIlib_audio_frame_t *a,
dst.reference_level = 0;
dst.p_data = (short *)pkt->data;
- NDIlib_util_audio_to_interleaved_16s(a, &dst);
+ ctx->lib->NDIlib_util_audio_to_interleaved_16s(a, &dst);
return 0;
}
@@ -102,7 +103,7 @@ static int ndi_find_sources(AVFormatContext *avctx, const char *name, NDIlib_sou
.p_groups = NULL, .p_extra_ips = NULL };
if (!ctx->ndi_find)
- ctx->ndi_find = NDIlib_find_create2(&find_create_desc);
+ ctx->ndi_find = ctx->lib->NDIlib_find_create_v2(&find_create_desc);
if (!ctx->ndi_find) {
av_log(avctx, AV_LOG_ERROR, "NDIlib_find_create failed.\n");
return AVERROR(EIO);
@@ -112,13 +113,13 @@ static int ndi_find_sources(AVFormatContext *avctx, const char *name, NDIlib_sou
{
int f, t = ctx->wait_sources / 1000;
av_log(avctx, AV_LOG_DEBUG, "Waiting for sources %d miliseconds\n", t);
- f = NDIlib_find_wait_for_sources(ctx->ndi_find, t);
+ f = ctx->lib->NDIlib_find_wait_for_sources(ctx->ndi_find, t);
av_log(avctx, AV_LOG_DEBUG, "NDIlib_find_wait_for_sources returns %d\n", f);
if (!f)
break;
};
- ndi_srcs = NDIlib_find_get_current_sources(ctx->ndi_find, &n);
+ ndi_srcs = ctx->lib->NDIlib_find_get_current_sources(ctx->ndi_find, &n);
if (ctx->find_sources)
av_log(avctx, AV_LOG_INFO, "Found %d NDI sources:\n", n);
@@ -143,7 +144,12 @@ static int ndi_read_header(AVFormatContext *avctx)
const NDIlib_tally_t tally_state = { .on_program = true, .on_preview = false };
struct NDIContext *ctx = avctx->priv_data;
- if (!NDIlib_initialize()) {
+ ctx->lib = ndi_lib_load(avctx);
+ if (!ctx->lib) {
+ return AVERROR_EXTERNAL;
+ }
+
+ if (!ctx->lib->NDIlib_initialize()) {
av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n");
return AVERROR_EXTERNAL;
}
@@ -162,14 +168,14 @@ static int ndi_read_header(AVFormatContext *avctx)
recv_create_desc.allow_video_fields = ctx->allow_video_fields;
/* Create the receiver */
- ctx->recv = NDIlib_recv_create(&recv_create_desc);
+ ctx->recv = ctx->lib->NDIlib_recv_create(&recv_create_desc);
if (!ctx->recv) {
av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_create2 failed.\n");
return AVERROR(EIO);
}
/* Set tally */
- NDIlib_recv_set_tally(ctx->recv, &tally_state);
+ ctx->lib->NDIlib_recv_set_tally(ctx->recv, &tally_state);
avctx->ctx_flags |= AVFMTCTX_NOHEADER;
@@ -267,7 +273,7 @@ static int ndi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
NDIlib_frame_type_e t;
av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture...\n");
- t = NDIlib_recv_capture(ctx->recv, &v, &a, &m, 40);
+ t = ctx->lib->NDIlib_recv_capture(ctx->recv, &v, &a, &m, 40);
av_log(avctx, AV_LOG_DEBUG, "NDIlib_recv_capture=%d\n", t);
if (t == NDIlib_frame_type_video) {
@@ -275,7 +281,7 @@ static int ndi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
ret = ndi_create_video_stream(avctx, &v);
if (!ret)
ret = ndi_set_video_packet(avctx, &v, pkt);
- NDIlib_recv_free_video(ctx->recv, &v);
+ ctx->lib->NDIlib_recv_free_video(ctx->recv, &v);
break;
}
else if (t == NDIlib_frame_type_audio) {
@@ -283,11 +289,11 @@ static int ndi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
ret = ndi_create_audio_stream(avctx, &a);
if (!ret)
ret = ndi_set_audio_packet(avctx, &a, pkt);
- NDIlib_recv_free_audio(ctx->recv, &a);
+ ctx->lib->NDIlib_recv_free_audio(ctx->recv, &a);
break;
}
else if (t == NDIlib_frame_type_metadata)
- NDIlib_recv_free_metadata(ctx->recv, &m);
+ ctx->lib->NDIlib_recv_free_metadata(ctx->recv, &m);
else if (t == NDIlib_frame_type_error){
av_log(avctx, AV_LOG_ERROR, "NDIlib_recv_capture failed with error\n");
ret = AVERROR(EIO);
@@ -302,10 +308,10 @@ static int ndi_read_close(AVFormatContext *avctx)
struct NDIContext *ctx = (struct NDIContext *)avctx->priv_data;
if (ctx->recv)
- NDIlib_recv_destroy(ctx->recv);
+ ctx->lib->NDIlib_recv_destroy(ctx->recv);
if (ctx->ndi_find)
- NDIlib_find_destroy(ctx->ndi_find);
+ ctx->lib->NDIlib_find_destroy(ctx->ndi_find);
return 0;
}
diff --git a/libavdevice/libndi_newtek_enc.c b/libavdevice/libndi_newtek_enc.c
index f3603f5..1243dbf 100644
--- a/libavdevice/libndi_newtek_enc.c
+++ b/libavdevice/libndi_newtek_enc.c
@@ -33,6 +33,7 @@ struct NDIContext {
int reference_level;
int clock_video, clock_audio;
+ const NDIlib_v3* lib;
NDIlib_video_frame_t *video;
NDIlib_audio_frame_interleaved_16s_t *audio;
NDIlib_send_instance_t ndi_send;
@@ -44,7 +45,7 @@ static int ndi_write_trailer(AVFormatContext *avctx)
struct NDIContext *ctx = avctx->priv_data;
if (ctx->ndi_send) {
- NDIlib_send_destroy(ctx->ndi_send);
+ ctx->lib->NDIlib_send_destroy(ctx->ndi_send);
av_frame_free(&ctx->last_avframe);
}
@@ -93,7 +94,7 @@ static int ndi_write_video_packet(AVFormatContext *avctx, AVStream *st, AVPacket
/* asynchronous for one frame, but will block if a second frame
is given before the first one has been sent */
- NDIlib_send_send_video_async(ctx->ndi_send, ctx->video);
+ ctx->lib->NDIlib_send_send_video_async(ctx->ndi_send, ctx->video);
av_frame_free(&ctx->last_avframe);
ctx->last_avframe = avframe;
@@ -112,7 +113,7 @@ static int ndi_write_audio_packet(AVFormatContext *avctx, AVStream *st, AVPacket
av_log(avctx, AV_LOG_DEBUG, "%s: pkt->pts=%"PRId64", timecode=%"PRId64", st->time_base=%d/%d\n",
__func__, pkt->pts, ctx->audio->timecode, st->time_base.num, st->time_base.den);
- NDIlib_util_send_send_audio_interleaved_16s(ctx->ndi_send, ctx->audio);
+ ctx->lib->NDIlib_util_send_send_audio_interleaved_16s(ctx->ndi_send, ctx->audio);
return 0;
}
@@ -236,7 +237,12 @@ static int ndi_write_header(AVFormatContext *avctx)
const NDIlib_send_create_t ndi_send_desc = { .p_ndi_name = avctx->url,
.p_groups = NULL, .clock_video = ctx->clock_video, .clock_audio = ctx->clock_audio };
- if (!NDIlib_initialize()) {
+ ctx->lib = ndi_lib_load(avctx);
+ if (!ctx->lib) {
+ return AVERROR_EXTERNAL;
+ }
+
+ if (!ctx->lib->NDIlib_initialize()) {
av_log(avctx, AV_LOG_ERROR, "NDIlib_initialize failed.\n");
return AVERROR_EXTERNAL;
}
@@ -258,7 +264,7 @@ static int ndi_write_header(AVFormatContext *avctx)
}
}
- ctx->ndi_send = NDIlib_send_create(&ndi_send_desc);
+ ctx->ndi_send = ctx->lib->NDIlib_send_create(&ndi_send_desc);
if (!ctx->ndi_send) {
av_log(avctx, AV_LOG_ERROR, "Failed to create NDI output %s\n", avctx->url);
ret = AVERROR_EXTERNAL;
--
1.8.3.1
More information about the ffmpeg-devel
mailing list