diff --git a/src/modules/newtek/CMakeLists.txt b/src/modules/newtek/CMakeLists.txt
index a1678ad50b..2797628f51 100644
--- a/src/modules/newtek/CMakeLists.txt
+++ b/src/modules/newtek/CMakeLists.txt
@@ -4,8 +4,14 @@ project (newtek)
set(SOURCES
consumer/newtek_ivga_consumer.cpp
+ consumer/newtek_ndi_consumer.cpp
+
+ producer/newtek_ndi_producer.cpp
+
util/air_send.cpp
+ util/ndi.cpp
+
newtek.cpp
StdAfx.cpp
@@ -13,10 +19,30 @@ set(SOURCES
set(HEADERS
consumer/newtek_ivga_consumer.h
+ consumer/newtek_ndi_consumer.h
+
+ producer/newtek_ndi_producer.h
+
util/air_send.h
+ util/ndi.h
+
newtek.h
+ interop/Processing.NDI.compat.h
+ interop/Processing.NDI.deprecated.h
+ interop/Processing.NDI.DynamicLoad.h
+ interop/Processing.NDI.Find.h
+ interop/Processing.NDI.FrameSync.h
+ interop/Processing.NDI.Lib.cplusplus.h
+ interop/Processing.NDI.Lib.h
+ interop/Processing.NDI.Recv.ex.h
+ interop/Processing.NDI.Recv.h
+ interop/Processing.NDI.Routing.h
+ interop/Processing.NDI.Send.h
+ interop/Processing.NDI.structs.h
+ interop/Processing.NDI.utilities.h
+
StdAfx.h
)
@@ -28,15 +54,19 @@ include_directories(..)
include_directories(../..)
include_directories(${BOOST_INCLUDE_PATH})
include_directories(${TBB_INCLUDE_PATH})
+include_directories(${FFMPEG_INCLUDE_PATH})
set_target_properties(newtek PROPERTIES FOLDER modules)
source_group(sources\\consumer consumer/*)
+source_group(sources\\producer producer/*)
+source_group(sources\\interop interop/*)
source_group(sources\\util util/*)
source_group(sources ./*)
target_link_libraries(newtek
common
core
+ ffmpeg
)
casparcg_add_include_statement("modules/newtek/newtek.h")
diff --git a/src/modules/newtek/consumer/newtek_ndi_consumer.cpp b/src/modules/newtek/consumer/newtek_ndi_consumer.cpp
new file mode 100644
index 0000000000..84c392775f
--- /dev/null
+++ b/src/modules/newtek/consumer/newtek_ndi_consumer.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2018
+ *
+ * This file is part of CasparCG (www.casparcg.com).
+ *
+ * CasparCG 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.
+ *
+ * CasparCG 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 CasparCG. If not, see .
+ *
+ * Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
+ * based on work of Robert Nagy, ronag89@gmail.com
+ */
+
+#include "../StdAfx.h"
+
+#include "newtek_ndi_consumer.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "../util/ndi.h"
+
+namespace caspar { namespace newtek {
+
+struct newtek_ndi_consumer : public core::frame_consumer
+{
+ static int instances_;
+ const int instance_no_;
+ const std::wstring name_;
+ const bool allow_fields_;
+
+ core::video_format_desc format_desc_;
+ NDIlib_v3* ndi_lib_;
+ NDIlib_send_instance_t ndi_send_instance_;
+ NDIlib_video_frame_v2_t ndi_video_frame_;
+ NDIlib_audio_frame_interleaved_32f_t ndi_audio_frame_;
+ std::shared_ptr field_data_;
+ spl::shared_ptr graph_;
+ caspar::timer tick_timer_;
+ caspar::timer frame_timer_;
+ int frame_no_;
+
+ public:
+ newtek_ndi_consumer(std::wstring name, bool allow_fields)
+ : name_(!name.empty() ? name : default_ndi_name())
+ , instance_no_(instances_++)
+ , frame_no_(0)
+ , allow_fields_(allow_fields)
+ {
+ if (!ndi::load_library()) {
+ ndi::not_installed();
+ }
+
+ graph_->set_text(print());
+ graph_->set_color("frame-time", diagnostics::color(0.5f, 1.0f, 0.2f));
+ graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
+ graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
+ diagnostics::register_graph(graph_);
+ }
+
+ ~newtek_ndi_consumer() { ndi_lib_->NDIlib_send_destroy(ndi_send_instance_); }
+
+ // frame_consumer
+
+ void initialize(const core::video_format_desc& format_desc, int channel_index) override
+ {
+ format_desc_ = format_desc;
+
+ ndi_lib_ = ndi::load_library();
+ NDIlib_send_create_t NDI_send_create_desc;
+
+ auto tmp_name = u8(name_);
+ NDI_send_create_desc.p_ndi_name = tmp_name.c_str();
+
+ ndi_send_instance_ = ndi_lib_->NDIlib_send_create(&NDI_send_create_desc);
+
+ ndi_video_frame_.xres = format_desc.width;
+ ndi_video_frame_.yres = format_desc.height;
+ ndi_video_frame_.frame_rate_N = format_desc.framerate.numerator();
+ ndi_video_frame_.frame_rate_D = format_desc.framerate.denominator();
+ ndi_video_frame_.FourCC = NDIlib_FourCC_type_BGRA;
+ ndi_video_frame_.line_stride_in_bytes = format_desc.width * 4;
+ ndi_video_frame_.frame_format_type = NDIlib_frame_format_type_progressive;
+
+ if (format_desc.field_count == 2 && allow_fields_) {
+ ndi_video_frame_.yres /= 2;
+ ndi_video_frame_.frame_rate_N /= 2;
+ ndi_video_frame_.picture_aspect_ratio = format_desc.width * 1.0f / format_desc.height;
+ field_data_.reset(new uint8_t[ndi_video_frame_.line_stride_in_bytes * ndi_video_frame_.yres],
+ std::default_delete());
+ ndi_video_frame_.p_data = field_data_.get();
+ }
+
+ ndi_audio_frame_.sample_rate = format_desc_.audio_sample_rate;
+ ndi_audio_frame_.no_channels = format_desc_.audio_channels;
+ ndi_audio_frame_.timecode = NDIlib_send_timecode_synthesize;
+
+ CASPAR_VERIFY(ndi_send_instance_);
+ }
+
+ std::future send(core::const_frame frame) override
+ {
+ CASPAR_VERIFY(format_desc_.height * format_desc_.width * 4 == frame.image_data(0).size());
+
+ graph_->set_value("tick-time", tick_timer_.elapsed() * format_desc_.fps * 0.5);
+ tick_timer_.restart();
+ frame_timer_.restart();
+ auto audio_data = frame.audio_data();
+ int audio_data_size = static_cast(audio_data.size());
+ ndi_audio_frame_.no_samples = audio_data_size / format_desc_.audio_channels;
+ std::vector audio_buffer = ndi::audio_32_to_32f(audio_data.data(), audio_data_size);
+ ndi_audio_frame_.p_data = const_cast(audio_buffer.data());
+ ndi_lib_->NDIlib_util_send_send_audio_interleaved_32f(ndi_send_instance_, &ndi_audio_frame_);
+ if (format_desc_.field_count == 2 && allow_fields_) {
+ ndi_video_frame_.frame_format_type =
+ (frame_no_ % 2 ? NDIlib_frame_format_type_field_1 : NDIlib_frame_format_type_field_0);
+ for (auto y = 0; y < ndi_video_frame_.yres; ++y) {
+ std::memcpy(reinterpret_cast(ndi_video_frame_.p_data) + y * format_desc_.width * 4,
+ frame.image_data(0).data() + (y * 2 + frame_no_ % 2) * format_desc_.width * 4,
+ format_desc_.width * 4);
+ }
+ } else {
+ ndi_video_frame_.p_data = const_cast(frame.image_data(0).begin());
+ }
+ ndi_lib_->NDIlib_send_send_video_v2(ndi_send_instance_, &ndi_video_frame_);
+ frame_no_++;
+ graph_->set_value("frame-time", frame_timer_.elapsed() * format_desc_.fps * 0.5);
+
+ return make_ready_future(true);
+ }
+
+ std::wstring print() const override { return L"ndi[" + name_ + L"]"; } // TODO: maybe put tally status in the name
+
+ std::wstring name() const override { return L"ndi"; }
+
+ std::wstring default_ndi_name() const
+ {
+ return L"CasparCG" + (instance_no_ ? L" " + boost::lexical_cast(instance_no_) : L"");
+ }
+
+ int index() const override { return 900; }
+
+ bool has_synchronization_clock() const override { return false; }
+};
+
+int newtek_ndi_consumer::instances_ = 0;
+
+spl::shared_ptr create_ndi_consumer(const std::vector& params,
+ std::vector> channels)
+{
+ if (params.size() < 1 || !boost::iequals(params.at(0), L"NDI"))
+ return core::frame_consumer::empty();
+ std::wstring name;
+ if (contains_param(L"NAME", params)) {
+ name = get_param(L"NAME", params);
+ }
+ bool allow_fields = contains_param(L"ALLOW_FIELDS", params);
+ return spl::make_shared(name, allow_fields);
+}
+
+spl::shared_ptr
+create_preconfigured_ndi_consumer(const boost::property_tree::wptree& ptree,
+ std::vector> channels)
+{
+ auto name = ptree.get(L"name", L"");
+ bool allow_fields = ptree.get(L"allow-fields", false);
+ return spl::make_shared(name, allow_fields);
+}
+
+}} // namespace caspar::newtek
diff --git a/src/modules/newtek/consumer/newtek_ndi_consumer.h b/src/modules/newtek/consumer/newtek_ndi_consumer.h
new file mode 100644
index 0000000000..ed03d03559
--- /dev/null
+++ b/src/modules/newtek/consumer/newtek_ndi_consumer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Sveriges Television AB http://casparcg.com/
+ *
+ * This file is part of CasparCG (www.casparcg.com).
+ *
+ * CasparCG 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.
+ *
+ * CasparCG 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 CasparCG. If not, see .
+ *
+ * Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
+ * based on work of Robert Nagy, ronag89@gmail.com work
+ */
+
+#pragma once
+
+#include
+
+#include
+
+#include
+
+#include
+
+namespace caspar { namespace newtek {
+
+spl::shared_ptr create_ndi_consumer(const std::vector& params,
+ std::vector> channels);
+spl::shared_ptr
+create_preconfigured_ndi_consumer(const boost::property_tree::wptree& ptree,
+ std::vector> channels);
+
+}} // namespace caspar::newtek
diff --git a/src/modules/newtek/interop/Processing.NDI.DynamicLoad.h b/src/modules/newtek/interop/Processing.NDI.DynamicLoad.h
new file mode 100644
index 0000000000..038beafa0d
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.DynamicLoad.h
@@ -0,0 +1,130 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+//****************************************************************************************************************
+typedef struct NDIlib_v3
+{ // V1.5
+ bool(*NDIlib_initialize)(void);
+ void(*NDIlib_destroy)(void);
+ const char* (*NDIlib_version)(void);
+ bool(*NDIlib_is_supported_CPU)(void);
+ PROCESSINGNDILIB_DEPRECATED NDIlib_find_instance_t(*NDIlib_find_create)(const NDIlib_find_create_t* p_create_settings);
+ NDIlib_find_instance_t(*NDIlib_find_create_v2)(const NDIlib_find_create_t* p_create_settings);
+ void(*NDIlib_find_destroy)(NDIlib_find_instance_t p_instance);
+ const NDIlib_source_t* (*NDIlib_find_get_sources)(NDIlib_find_instance_t p_instance, uint32_t* p_no_sources, uint32_t timeout_in_ms);
+ NDIlib_send_instance_t(*NDIlib_send_create)(const NDIlib_send_create_t* p_create_settings);
+ void(*NDIlib_send_destroy)(NDIlib_send_instance_t p_instance);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_send_send_video)(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_t* p_video_data);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_send_send_video_async)(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_t* p_video_data);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_send_send_audio)(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_t* p_audio_data);
+ void(*NDIlib_send_send_metadata)(NDIlib_send_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+ NDIlib_frame_type_e(*NDIlib_send_capture)(NDIlib_send_instance_t p_instance, NDIlib_metadata_frame_t* p_metadata, uint32_t timeout_in_ms);
+ void(*NDIlib_send_free_metadata)(NDIlib_send_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+ bool(*NDIlib_send_get_tally)(NDIlib_send_instance_t p_instance, NDIlib_tally_t* p_tally, uint32_t timeout_in_ms);
+ int(*NDIlib_send_get_no_connections)(NDIlib_send_instance_t p_instance, uint32_t timeout_in_ms);
+ void(*NDIlib_send_clear_connection_metadata)(NDIlib_send_instance_t p_instance);
+ void(*NDIlib_send_add_connection_metadata)(NDIlib_send_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+ void(*NDIlib_send_set_failover)(NDIlib_send_instance_t p_instance, const NDIlib_source_t* p_failover_source);
+ PROCESSINGNDILIB_DEPRECATED NDIlib_recv_instance_t(*NDIlib_recv_create_v2)(const NDIlib_recv_create_t* p_create_settings);
+ PROCESSINGNDILIB_DEPRECATED NDIlib_recv_instance_t(*NDIlib_recv_create)(const NDIlib_recv_create_t* p_create_settings);
+ void(*NDIlib_recv_destroy)(NDIlib_recv_instance_t p_instance);
+ PROCESSINGNDILIB_DEPRECATED NDIlib_frame_type_e(*NDIlib_recv_capture)(NDIlib_recv_instance_t p_instance, NDIlib_video_frame_t* p_video_data, NDIlib_audio_frame_t* p_audio_data, NDIlib_metadata_frame_t* p_metadata, uint32_t timeout_in_ms);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_recv_free_video)(NDIlib_recv_instance_t p_instance, const NDIlib_video_frame_t* p_video_data);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_recv_free_audio)(NDIlib_recv_instance_t p_instance, const NDIlib_audio_frame_t* p_audio_data);
+ void(*NDIlib_recv_free_metadata)(NDIlib_recv_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+ bool(*NDIlib_recv_send_metadata)(NDIlib_recv_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+ bool(*NDIlib_recv_set_tally)(NDIlib_recv_instance_t p_instance, const NDIlib_tally_t* p_tally);
+ void(*NDIlib_recv_get_performance)(NDIlib_recv_instance_t p_instance, NDIlib_recv_performance_t* p_total, NDIlib_recv_performance_t* p_dropped);
+ void(*NDIlib_recv_get_queue)(NDIlib_recv_instance_t p_instance, NDIlib_recv_queue_t* p_total);
+ void(*NDIlib_recv_clear_connection_metadata)(NDIlib_recv_instance_t p_instance);
+ void(*NDIlib_recv_add_connection_metadata)(NDIlib_recv_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+ int(*NDIlib_recv_get_no_connections)(NDIlib_recv_instance_t p_instance);
+ NDIlib_routing_instance_t(*NDIlib_routing_create)(const NDIlib_routing_create_t* p_create_settings);
+ void(*NDIlib_routing_destroy)(NDIlib_routing_instance_t p_instance);
+ bool(*NDIlib_routing_change)(NDIlib_routing_instance_t p_instance, const NDIlib_source_t* p_source);
+ bool(*NDIlib_routing_clear)(NDIlib_routing_instance_t p_instance);
+ void(*NDIlib_util_send_send_audio_interleaved_16s)(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_interleaved_16s_t* p_audio_data);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_util_audio_to_interleaved_16s)(const NDIlib_audio_frame_t* p_src, NDIlib_audio_frame_interleaved_16s_t* p_dst);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_util_audio_from_interleaved_16s)(const NDIlib_audio_frame_interleaved_16s_t* p_src, NDIlib_audio_frame_t* p_dst);
+ // V2
+ bool(*NDIlib_find_wait_for_sources)(NDIlib_find_instance_t p_instance, uint32_t timeout_in_ms);
+ const NDIlib_source_t* (*NDIlib_find_get_current_sources)(NDIlib_find_instance_t p_instance, uint32_t* p_no_sources);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_util_audio_to_interleaved_32f)(const NDIlib_audio_frame_t* p_src, NDIlib_audio_frame_interleaved_32f_t* p_dst);
+ PROCESSINGNDILIB_DEPRECATED void(*NDIlib_util_audio_from_interleaved_32f)(const NDIlib_audio_frame_interleaved_32f_t* p_src, NDIlib_audio_frame_t* p_dst);
+ void(*NDIlib_util_send_send_audio_interleaved_32f)(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_interleaved_32f_t* p_audio_data);
+ // V3
+ void(*NDIlib_recv_free_video_v2)(NDIlib_recv_instance_t p_instance, const NDIlib_video_frame_v2_t* p_video_data);
+ void(*NDIlib_recv_free_audio_v2)(NDIlib_recv_instance_t p_instance, const NDIlib_audio_frame_v2_t* p_audio_data);
+ NDIlib_frame_type_e(*NDIlib_recv_capture_v2)(NDIlib_recv_instance_t p_instance, NDIlib_video_frame_v2_t* p_video_data, NDIlib_audio_frame_v2_t* p_audio_data, NDIlib_metadata_frame_t* p_metadata, uint32_t timeout_in_ms); // The amount of time in milliseconds to wait for data.
+ void(*NDIlib_send_send_video_v2)(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_v2_t* p_video_data);
+ void(*NDIlib_send_send_video_async_v2)(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_v2_t* p_video_data);
+ void(*NDIlib_send_send_audio_v2)(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_v2_t* p_audio_data);
+ void(*NDIlib_util_audio_to_interleaved_16s_v2)(const NDIlib_audio_frame_v2_t* p_src, NDIlib_audio_frame_interleaved_16s_t* p_dst);
+ void(*NDIlib_util_audio_from_interleaved_16s_v2)(const NDIlib_audio_frame_interleaved_16s_t* p_src, NDIlib_audio_frame_v2_t* p_dst);
+ void(*NDIlib_util_audio_to_interleaved_32f_v2)(const NDIlib_audio_frame_v2_t* p_src, NDIlib_audio_frame_interleaved_32f_t* p_dst);
+ void(*NDIlib_util_audio_from_interleaved_32f_v2)(const NDIlib_audio_frame_interleaved_32f_t* p_src, NDIlib_audio_frame_v2_t* p_dst);
+ // V3.01
+ void(*NDIlib_recv_free_string)(NDIlib_recv_instance_t p_instance, const char* p_string);
+ bool(*NDIlib_recv_ptz_is_supported)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_recording_is_supported)(NDIlib_recv_instance_t p_instance);
+ const char*(*NDIlib_recv_get_web_control)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_ptz_zoom)(NDIlib_recv_instance_t p_instance, const float zoom_value);
+ bool(*NDIlib_recv_ptz_zoom_speed)(NDIlib_recv_instance_t p_instance, const float zoom_speed);
+ bool(*NDIlib_recv_ptz_pan_tilt)(NDIlib_recv_instance_t p_instance, const float pan_value, const float tilt_value);
+ bool(*NDIlib_recv_ptz_pan_tilt_speed)(NDIlib_recv_instance_t p_instance, const float pan_speed, const float tilt_speed);
+ bool(*NDIlib_recv_ptz_store_preset)(NDIlib_recv_instance_t p_instance, const int preset_no);
+ bool(*NDIlib_recv_ptz_recall_preset)(NDIlib_recv_instance_t p_instance, const int preset_no, const float speed);
+ bool(*NDIlib_recv_ptz_auto_focus)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_ptz_focus)(NDIlib_recv_instance_t p_instance, const float focus_value);
+ bool(*NDIlib_recv_ptz_focus_speed)(NDIlib_recv_instance_t p_instance, const float focus_speed);
+ bool(*NDIlib_recv_ptz_white_balance_auto)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_ptz_white_balance_indoor)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_ptz_white_balance_outdoor)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_ptz_white_balance_oneshot)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_ptz_white_balance_manual)(NDIlib_recv_instance_t p_instance, const float red, const float blue);
+ bool(*NDIlib_recv_ptz_exposure_auto)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_ptz_exposure_manual)(NDIlib_recv_instance_t p_instance, const float exposure_level);
+ bool(*NDIlib_recv_recording_start)(NDIlib_recv_instance_t p_instance, const char* p_filename_hint);
+ bool(*NDIlib_recv_recording_stop)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_recording_set_audio_level)(NDIlib_recv_instance_t p_instance, const float level_dB);
+ bool(*NDIlib_recv_recording_is_recording)(NDIlib_recv_instance_t p_instance);
+ const char*(*NDIlib_recv_recording_get_filename)(NDIlib_recv_instance_t p_instance);
+ const char*(*NDIlib_recv_recording_get_error)(NDIlib_recv_instance_t p_instance);
+ bool(*NDIlib_recv_recording_get_times)(NDIlib_recv_instance_t p_instance, NDIlib_recv_recording_time_t* p_times);
+ // V3.10
+ NDIlib_recv_instance_t(*NDIlib_recv_create_v3)(const NDIlib_recv_create_v3_t* p_create_settings);
+ // V3.5
+ void(*NDIlib_recv_connect)(NDIlib_recv_instance_t p_instance, const NDIlib_source_t* p_src);
+
+} NDIlib_v3;
+
+typedef struct NDIlib_v3 NDIlib_v2;
+
+// Load the library
+PROCESSINGNDILIB_API
+const NDIlib_v3* NDIlib_v3_load(void);
+
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+const NDIlib_v2* NDIlib_v2_load(void);
diff --git a/src/modules/newtek/interop/Processing.NDI.Find.h b/src/modules/newtek/interop/Processing.NDI.Find.h
new file mode 100644
index 0000000000..8b7d9bf0e2
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.Find.h
@@ -0,0 +1,76 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+//**************************************************************************************************************************
+// Structures and type definitions required by NDI finding
+// The reference to an instance of the finder
+typedef void* NDIlib_find_instance_t;
+
+// The creation structure that is used when you are creating a finder
+typedef struct NDIlib_find_create_t
+{ // Do we want to incluide the list of NDI sources that are running
+ // on the local machine ?
+ // If TRUE then local sources will be visible, if FALSE then they
+ // will not.
+ bool show_local_sources;
+
+ // Which groups do you want to search in for sources
+ const char* p_groups;
+
+ // The list of additional IP addresses that exist that we should query for
+ // sources on. For instance, if you want to find the sources on a remote machine
+ // that is not on your local sub-net then you can put a comma seperated list of
+ // those IP addresses here and those sources will be available locally even though
+ // they are not mDNS discoverable. An example might be "12.0.0.8,13.0.12.8".
+ // When none is specified the registry is used.
+ // Default = nullptr;
+ const char* p_extra_ips;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_find_create_t(bool show_local_sources_ = true, const char* p_groups_ = nullptr, const char* p_extra_ips_ = nullptr);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_find_create_t;
+
+//**************************************************************************************************************************
+// Create a new finder instance. This will return nullptr if it fails.
+// This function is deprecated, please use NDIlib_find_create_v2 if you can. This function
+// ignores the p_extra_ips member and sets it to the default.
+PROCESSINGNDILIB_API
+NDIlib_find_instance_t NDIlib_find_create_v2(const NDIlib_find_create_t* p_create_settings NDILIB_CPP_DEFAULT_VALUE(nullptr));
+
+// This will destroy an existing finder instance.
+PROCESSINGNDILIB_API
+void NDIlib_find_destroy(NDIlib_find_instance_t p_instance);
+
+// This function will recover the current set of sources (i.e. the ones that exist right this second).
+// The char* memory buffers returned in NDIlib_source_t are valid until the next call to NDIlib_find_get_current_sources or a call to NDIlib_find_destroy.
+// For a given NDIlib_find_instance_t, do not call NDIlib_find_get_current_sources asynchronously.
+PROCESSINGNDILIB_API
+const NDIlib_source_t* NDIlib_find_get_current_sources(NDIlib_find_instance_t p_instance, uint32_t* p_no_sources);
+
+// This will allow you to wait until the number of online sources have changed.
+PROCESSINGNDILIB_API
+bool NDIlib_find_wait_for_sources(NDIlib_find_instance_t p_instance, uint32_t timeout_in_ms);
diff --git a/src/modules/newtek/interop/Processing.NDI.FrameSync.h b/src/modules/newtek/interop/Processing.NDI.FrameSync.h
new file mode 100644
index 0000000000..cf6e4c615a
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.FrameSync.h
@@ -0,0 +1,123 @@
+#pragma once
+
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+//***********************************************************************************************************************************************
+
+// It is important when using video to realize that often you are using differenc clocks
+// for different parts of the signal chain. Within NDI, the sender can send at the clock rate
+// that it wants and the receiver will receive it at that rate. The receiver however is very
+// unlikely to share the exact same clock rate in many cases. For instance, bear in mind that
+// computer clocks rely on crystals which while all rated for the same frequency are still not
+// exact. If you sending computer has an audio clock that it "thinks" is 48000Hz, to the receiver
+// computer that has a different audio clock this might be 48001Hz or 47998Hz. While these
+// differences might appear small they accumulate over time and can cause audio to either
+// slightly drift out of sync (it is receiving more audio sample sthan it needs to play back)
+// or might cause audio glitches because it is not receiving enough audio samples. While we
+// have described the situation for audio, the same exact problem occurs for video sources;
+// it is commonly thought that this can be solved by simply having a "frame buffer" and that
+// displaying the "most recently received video frame" will solve these timing discrepencies.
+// Unfortunately this is not the case and when it is done because of the variance in clock
+// timings, it is veyr common the the video will appear the "jitter" when the sending and
+// receiving closks are almost in alignment. The solution to these problems is to implement
+// a "time base corrector" for the video clock which is a device that uses hysterisis to know
+// when the best time is to either drop or insert a video frame such that the video is most
+// likely to play back smoothly, and audio should be dynamically audio sampled (with a high
+// order resampling filter) to adaptively track any clocking differences. Implementing these
+// components is very difficult to get entirely correct under all scenarios and this
+// implementation is provided to facilitate this and help people who are building real time
+// video applications to receive audio and video without needing to undertake the full
+// complexity of implementing such clock devices.
+//
+// Another way to look at what this class does is that it transforms "push" sources (i.e.
+// NDI sources in which the data is pushed from the sender to the receiver) into "pull"
+// sources in which a host application is pulling the data down-stream. The frame-sync
+// automatically tracks all clocks to acheive the best video performance doing this
+// operation.
+//
+// In addition to time-base correction operations, these implementations also will automatically
+// detect and correct timing jitter that might occur. This will internally correct for timing
+// anomolies that might be caused by network, sender or receiver side timing errors caused
+// by CPU limitatoins, network bandwidth fluctuations, etc...
+//
+// A very common use of a frame-synchronizer might be if you are displaying video on screen
+// timed to the GPU v-sync, you should use sych a device to convert from the incoming time-base
+// into the time-base of the GPU.
+//
+// The following are common times that you want to use a frame-synchronizer
+// Video playback on screen : Yes, you want the clock to be synced with vertical refresh.
+// Audio playback through sound card : Yes you want the clock to be synced with your sound card clock.
+// Video mixing : Yes you want the input video clocks to all be synced to your output video clock.
+// Audio mixing : Yes, you want all input audio clocks to be brough into sync with your output audio clock.
+// Recording a single channel : No, you want to record the signal in it's raw form without any reclocking.
+// Recording multiple channels : Maybe. If you want to sync some input channels to match a master clock so that
+// they can be ISO edited, then you might want a frame-sync.
+
+// The type instance for a frame-synchronizer
+typedef void* NDIlib_framesync_instance_t;
+
+// Create a frame synchronizer instance that can be used to get frames
+// from a receiver. Once this receiver has been bound to a frame-sync
+// then you should use it in order to receover video frames. You can
+// continue to use the underlying receiver for other operations (tally,
+// PTZ, etc...). Note that it remains your responsability to destroy the
+// receiver even when a frame-sync is using it. You should always destroy
+// the receiver after the framesync has been destroyed.
+PROCESSINGNDILIB_API
+NDIlib_framesync_instance_t NDIlib_framesync_create(NDIlib_recv_instance_t p_receiver);
+
+// Destroy a frame-sync implemenration
+PROCESSINGNDILIB_API
+void NDIlib_framesync_destroy(NDIlib_framesync_instance_t p_instance);
+
+// This function will pull audio samples from the frame-sync queue. This function
+// will always return data immediately, inserting silence if no current audio
+// data is present. You should call this at the rate that you want audio and it
+// will automatically adapt the incoming audio signal to match the rate at which
+// you are calling by using dynamic audio sampling. Note that you have no obligation
+// that your requested sample rate, no channels and no samples match the incoming signal
+// and all combinations of conversions are supported. If you specify a sample-rate=0
+// or no_channels=0 then it will use the original sample rate of the buffer.
+PROCESSINGNDILIB_API
+void NDIlib_framesync_capture_audio(// The frame sync instance data
+ NDIlib_framesync_instance_t p_instance,
+ // The destination audio buffer that you wish to have filled in.
+ NDIlib_audio_frame_v2_t* p_audio_data,
+ // Your desired sample rate, number of channels and the number of desired samples.
+ int sample_rate, int no_channels, int no_samples);
+
+// Free audio returned by NDIlib_framesync_capture_audio
+PROCESSINGNDILIB_API
+void NDIlib_framesync_free_audio(// The frame sync instance data
+ NDIlib_framesync_instance_t p_instance,
+ // The destination audio buffer that you wish to have filled in.
+ NDIlib_audio_frame_v2_t* p_audio_data);
+
+// This function will pull video samples from the frame-sync queue. This function
+// will always immediately return a video sample by using time-base correction. You can
+// specify the desired field type which is then used to return the best possible frame.
+// Note that field based frame-synronization means that the frame-synchronizer attempts
+// to match the fielded input phase with the frame requests so that you have the most
+// correct possible field ordering on output. Note that the same frame can be returned
+// multiple times.
+//
+// If no video frame has evern been received, this will return NDIlib_video_frame_v2_t as
+// an empty (all zero) structure. The reason for this is that it allows you to determine
+// that there has not yet been any video and act accordingly. For instamce you might want
+// to display a constant frame output at a particular video format, or black.
+PROCESSINGNDILIB_API
+void NDIlib_framesync_capture_video(// The frame sync instance data
+ NDIlib_framesync_instance_t p_instance,
+ // The destination audio buffer that you wish to have filled in.
+ NDIlib_video_frame_v2_t* p_video_data,
+ // The frame type that you would prefer, all effort is made to match these.
+ NDIlib_frame_format_type_e field_type NDILIB_CPP_DEFAULT_VALUE(NDIlib_frame_format_type_progressive));
+
+// Free audio returned by NDIlib_framesync_capture_video
+PROCESSINGNDILIB_API
+void NDIlib_framesync_free_video(// The frame sync instance data
+ NDIlib_framesync_instance_t p_instance,
+ // The destination audio buffer that you wish to have filled in.
+ NDIlib_video_frame_v2_t* p_video_data);
diff --git a/src/modules/newtek/interop/Processing.NDI.Lib.cplusplus.h b/src/modules/newtek/interop/Processing.NDI.Lib.cplusplus.h
new file mode 100644
index 0000000000..dddad74797
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.Lib.cplusplus.h
@@ -0,0 +1,95 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+
+// C++ implementations of default constructors are here to avoid them needing to be inline with
+// all of the rest of the code.
+
+// All the structs used and reasonable defaults are here
+inline NDIlib_source_t::NDIlib_source_t(const char* p_ndi_name_, const char* p_url_address_)
+ : p_ndi_name(p_ndi_name_), p_url_address(p_url_address_) {}
+
+inline NDIlib_video_frame_v2_t::NDIlib_video_frame_v2_t(int xres_, int yres_, NDIlib_FourCC_type_e FourCC_, int frame_rate_N_, int frame_rate_D_,
+ float picture_aspect_ratio_, NDIlib_frame_format_type_e frame_format_type_,
+ int64_t timecode_, uint8_t* p_data_, int line_stride_in_bytes_, const char* p_metadata_, int64_t timestamp_)
+ : xres(xres_), yres(yres_), FourCC(FourCC_), frame_rate_N(frame_rate_N_), frame_rate_D(frame_rate_D_),
+ picture_aspect_ratio(picture_aspect_ratio_), frame_format_type(frame_format_type_),
+ timecode(timecode_), p_data(p_data_), line_stride_in_bytes(line_stride_in_bytes_), p_metadata(p_metadata_), timestamp(timestamp_) {}
+
+inline NDIlib_audio_frame_v2_t::NDIlib_audio_frame_v2_t(int sample_rate_, int no_channels_, int no_samples_, int64_t timecode_, float* p_data_,
+ int channel_stride_in_bytes_, const char* p_metadata_, int64_t timestamp_)
+ : sample_rate(sample_rate_), no_channels(no_channels_), no_samples(no_samples_), timecode(timecode_),
+ p_data(p_data_), channel_stride_in_bytes(channel_stride_in_bytes_), p_metadata(p_metadata_), timestamp(timestamp_) {}
+
+inline NDIlib_video_frame_t::NDIlib_video_frame_t(int xres_, int yres_, NDIlib_FourCC_type_e FourCC_, int frame_rate_N_, int frame_rate_D_,
+ float picture_aspect_ratio_, NDIlib_frame_format_type_e frame_format_type_,
+ int64_t timecode_, uint8_t* p_data_, int line_stride_in_bytes_)
+ : xres(xres_), yres(yres_), FourCC(FourCC_), frame_rate_N(frame_rate_N_), frame_rate_D(frame_rate_D_),
+ picture_aspect_ratio(picture_aspect_ratio_), frame_format_type(frame_format_type_),
+ timecode(timecode_), p_data(p_data_), line_stride_in_bytes(line_stride_in_bytes_) {}
+
+inline NDIlib_audio_frame_t::NDIlib_audio_frame_t(int sample_rate_, int no_channels_, int no_samples_, int64_t timecode_, float* p_data_,
+ int channel_stride_in_bytes_)
+ : sample_rate(sample_rate_), no_channels(no_channels_), no_samples(no_samples_), timecode(timecode_),
+ p_data(p_data_), channel_stride_in_bytes(channel_stride_in_bytes_) {}
+
+inline NDIlib_metadata_frame_t::NDIlib_metadata_frame_t(int length_, int64_t timecode_, char* p_data_)
+ : length(length_), timecode(timecode_), p_data(p_data_) {}
+
+inline NDIlib_tally_t::NDIlib_tally_t(bool on_program_, bool on_preview_)
+ : on_program(on_program_), on_preview(on_preview_) {}
+
+inline NDIlib_routing_create_t::NDIlib_routing_create_t(const char* p_ndi_name_, const char* p_groups_)
+ : p_ndi_name(p_ndi_name_), p_groups(p_groups_) {}
+
+inline NDIlib_recv_create_v3_t::NDIlib_recv_create_v3_t(const NDIlib_source_t source_to_connect_to_, NDIlib_recv_color_format_e color_format_,
+ NDIlib_recv_bandwidth_e bandwidth_, bool allow_video_fields_, const char* p_ndi_name_)
+ : source_to_connect_to(source_to_connect_to_), color_format(color_format_), bandwidth(bandwidth_), allow_video_fields(allow_video_fields_), p_ndi_recv_name(p_ndi_name_) {}
+
+inline NDIlib_recv_create_t::NDIlib_recv_create_t(const NDIlib_source_t source_to_connect_to_, NDIlib_recv_color_format_e color_format_,
+ NDIlib_recv_bandwidth_e bandwidth_, bool allow_video_fields_)
+ : source_to_connect_to(source_to_connect_to_), color_format(color_format_), bandwidth(bandwidth_), allow_video_fields(allow_video_fields_) {}
+
+inline NDIlib_recv_performance_t::NDIlib_recv_performance_t(void)
+ : video_frames(0), audio_frames(0), metadata_frames(0) {}
+
+inline NDIlib_recv_queue_t::NDIlib_recv_queue_t(void)
+ : video_frames(0), audio_frames(0), metadata_frames(0) {}
+
+inline NDIlib_recv_recording_time_t::NDIlib_recv_recording_time_t(void)
+ : no_frames(0), start_time(0), last_time(0) {}
+
+inline NDIlib_send_create_t::NDIlib_send_create_t(const char* p_ndi_name_, const char* p_groups_, bool clock_video_, bool clock_audio_)
+ : p_ndi_name(p_ndi_name_), p_groups(p_groups_), clock_video(clock_video_), clock_audio(clock_audio_) {}
+
+inline NDIlib_find_create_t::NDIlib_find_create_t(bool show_local_sources_, const char* p_groups_, const char* p_extra_ips_)
+ : show_local_sources(show_local_sources_), p_groups(p_groups_), p_extra_ips(p_extra_ips_) {}
+
+inline NDIlib_audio_frame_interleaved_16s_t::NDIlib_audio_frame_interleaved_16s_t(int sample_rate_, int no_channels_, int no_samples_, int64_t timecode_, int reference_level_, short* p_data_)
+ : sample_rate(sample_rate_), no_channels(no_channels_), no_samples(no_samples_), timecode(timecode_),
+ reference_level(reference_level_), p_data(p_data_) {}
+
+inline NDIlib_audio_frame_interleaved_32f_t::NDIlib_audio_frame_interleaved_32f_t(int sample_rate_, int no_channels_, int no_samples_, int64_t timecode_, float* p_data_)
+ : sample_rate(sample_rate_), no_channels(no_channels_), no_samples(no_samples_), timecode(timecode_), p_data(p_data_) {}
diff --git a/src/modules/newtek/interop/Processing.NDI.Lib.h b/src/modules/newtek/interop/Processing.NDI.Lib.h
new file mode 100644
index 0000000000..54c152b8db
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.Lib.h
@@ -0,0 +1,139 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+// Is this library being compiled, or imported by another application.
+#ifdef _WIN32
+#define PROCESSINGNDILIB_DEPRECATED __declspec(deprecated)
+#ifdef PROCESSINGNDILIB_EXPORTS
+#ifdef __cplusplus
+#define PROCESSINGNDILIB_API extern "C" __declspec(dllexport)
+#else // __cplusplus
+#define PROCESSINGNDILIB_API __declspec(dllexport)
+#endif // __cplusplus
+#else // PROCESSINGNDILIB_EXPORTS
+#ifdef __cplusplus
+#define PROCESSINGNDILIB_API extern "C" __declspec(dllimport)
+#else // __cplusplus
+#define PROCESSINGNDILIB_API __declspec(dllimport)
+#endif // __cplusplus
+#ifdef _WIN64
+#define NDILIB_LIBRARY_NAME "Processing.NDI.Lib.x64.dll"
+#define NDILIB_REDIST_FOLDER "NDI_RUNTIME_DIR_V3"
+#define NDILIB_REDIST_URL "http://new.tk/NDIRedistV3"
+#else // _WIN64
+#define NDILIB_LIBRARY_NAME "Processing.NDI.Lib.x86.dll"
+#define NDILIB_REDIST_FOLDER "NDI_RUNTIME_DIR_V3"
+#define NDILIB_REDIST_URL "http://new.tk/NDIRedistV3"
+#endif // _WIN64
+#endif // PROCESSINGNDILIB_EXPORTS
+#else // _WIN32
+#ifdef __APPLE__
+#define NDILIB_LIBRARY_NAME "libndi.3.dylib"
+#define NDILIB_REDIST_FOLDER "NDI_RUNTIME_DIR_V3"
+#define NDILIB_REDIST_URL "http://new.tk/NDIRedistV3Apple"
+#else // __APPLE__
+#define NDILIB_LIBRARY_NAME "libndi.so.3"
+#define NDILIB_REDIST_FOLDER "NDI_RUNTIME_DIR_V3"
+#define NDILIB_REDIST_URL ""
+#endif // __APPLE__
+#define PROCESSINGNDILIB_DEPRECATED
+#ifdef __cplusplus
+#define PROCESSINGNDILIB_API extern "C" __attribute((visibility("default")))
+#else // __cplusplus
+#define PROCESSINGNDILIB_API __attribute((visibility("default")))
+#endif // __cplusplus
+#endif // _WIN32
+
+#ifndef NDILIB_CPP_DEFAULT_CONSTRUCTORS
+#ifdef __cplusplus
+#define NDILIB_CPP_DEFAULT_CONSTRUCTORS 1
+#else // __cplusplus
+#define NDILIB_CPP_DEFAULT_CONSTRUCTORS 0
+#endif // __cplusplus
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+#ifndef NDILIB_CPP_DEFAULT_VALUE
+#ifdef __cplusplus
+#define NDILIB_CPP_DEFAULT_VALUE(a) =(a)
+#else // __cplusplus
+#define NDILIB_CPP_DEFAULT_VALUE(a)
+#endif // __cplusplus
+#endif // NDILIB_CPP_DEFAULT_VALUE
+
+// Data structures shared by multiple SDKs
+#include "Processing.NDI.compat.h"
+#include "Processing.NDI.structs.h"
+
+// This is not actually required, but will start and end the libraries which might get
+// you slightly better performance in some cases. In general it is more "correct" to
+// call these although it is not required. There is no way to call these that would have
+// an adverse impact on anything (even calling destroy before you've deleted all your
+// objects). This will return false if the CPU is not sufficiently capable to run NDILib
+// currently NDILib requires SSE4.2 instructions (see documentation). You can verify
+// a specific CPU against the library with a call to NDIlib_is_supported_CPU()
+PROCESSINGNDILIB_API
+bool NDIlib_initialize(void);
+
+PROCESSINGNDILIB_API
+void NDIlib_destroy(void);
+
+PROCESSINGNDILIB_API
+const char* NDIlib_version(void);
+
+// Recover whether the current CPU in the system is capable of running NDILib.
+PROCESSINGNDILIB_API
+bool NDIlib_is_supported_CPU(void);
+
+// The finding (discovery API)
+#include "Processing.NDI.Find.h"
+
+// The receiving video and audio API
+#include "Processing.NDI.Recv.h"
+
+// Extensions to support PTZ control, etc...
+#include "Processing.NDI.Recv.ex.h"
+
+// The sending video API
+#include "Processing.NDI.Send.h"
+
+// The routing of inputs API
+#include "Processing.NDI.Routing.h"
+
+// Utility functions
+#include "Processing.NDI.utilities.h"
+
+// Deprecated structures and functions
+#include "Processing.NDI.deprecated.h"
+
+// Dynamic loading used for OSS libraries
+#include "Processing.NDI.DynamicLoad.h"
+
+// The frame synchronizer
+#include "Processing.NDI.FrameSync.h"
+
+// The C++ implementations
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+#include "Processing.NDI.Lib.cplusplus.h"
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
\ No newline at end of file
diff --git a/src/modules/newtek/interop/Processing.NDI.Recv.ex.h b/src/modules/newtek/interop/Processing.NDI.Recv.ex.h
new file mode 100644
index 0000000000..f92b12055e
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.Recv.ex.h
@@ -0,0 +1,171 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+//**************************************************************************************************************************************************************
+// Has this receiver got PTZ control. Note that it might take a second or two after the connection for this value to be set.
+// To avoid the need to poll this function, you can know when the value of this function might have changed when the
+// NDILib_recv_capture* call would return NDIlib_frame_type_status_change
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_is_supported(NDIlib_recv_instance_t p_instance);
+
+// Has this receiver got recording control. Note that it might take a second or two after the connection for this value to be set.
+// To avoid the need to poll this function, you can know when the value of this function might have changed when the
+// NDILib_recv_capture* call would return NDIlib_frame_type_status_change
+PROCESSINGNDILIB_API
+bool NDIlib_recv_recording_is_supported(NDIlib_recv_instance_t p_instance);
+
+//**************************************************************************************************************************************************************
+// PTZ Controls
+// Zoom to an absolute value.
+// zoom_value = 0.0 (zoomed in) ... 1.0 (zoomed out)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_zoom(NDIlib_recv_instance_t p_instance, const float zoom_value);
+
+// Zoom at a particular speed
+// zoom_speed = -1.0 (zoom outwards) ... +1.0 (zoom inwards)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_zoom_speed(NDIlib_recv_instance_t p_instance, const float zoom_speed);
+
+// Set the pan and tilt to an absolute value
+// pan_value = -1.0 (left) ... 0.0 (centred) ... +1.0 (right)
+// tilt_value = -1.0 (bottom) ... 0.0 (centred) ... +1.0 (top)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_pan_tilt(NDIlib_recv_instance_t p_instance, const float pan_value, const float tilt_value);
+
+// Set the pan and tilt direction and speed
+// pan_speed = -1.0 (moving right) ... 0.0 (stopped) ... +1.0 (moving left)
+// tilt_speed = -1.0 (down) ... 0.0 (stopped) ... +1.0 (moving up)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_pan_tilt_speed(NDIlib_recv_instance_t p_instance, const float pan_speed, const float tilt_speed);
+
+// Store the current position, focus, etc... as a preset.
+// preset_no = 0 ... 99
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_store_preset(NDIlib_recv_instance_t p_instance, const int preset_no);
+
+// Recall a preset, including position, focus, etc...
+// preset_no = 0 ... 99
+// speed = 0.0(as slow as possible) ... 1.0(as fast as possible) The speed at which to move to the new preset
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_recall_preset(NDIlib_recv_instance_t p_instance, const int preset_no, const float speed);
+
+// Put the camera in auto-focus
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_auto_focus(NDIlib_recv_instance_t p_instance);
+
+// Focus to an absolute value.
+// focus_value = 0.0 (focussed to infinity) ... 1.0 (focussed as close as possible)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_focus(NDIlib_recv_instance_t p_instance, const float focus_value);
+
+// Focus at a particular speed
+// focus_speed = -1.0 (focus outwards) ... +1.0 (focus inwards)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_focus_speed(NDIlib_recv_instance_t p_instance, const float focus_speed);
+
+// Put the camera in auto white balance moce
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_white_balance_auto(NDIlib_recv_instance_t p_instance);
+
+// Put the camera in indoor white balance
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_white_balance_indoor(NDIlib_recv_instance_t p_instance);
+
+// Put the camera in indoor white balance
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_white_balance_outdoor(NDIlib_recv_instance_t p_instance);
+
+// Use the current brightness to automatically set the current white balance
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_white_balance_oneshot(NDIlib_recv_instance_t p_instance);
+
+// Set the manual camera white balance using the R, B values
+// red = 0.0(not red) ... 1.0(very red)
+// blue = 0.0(not blue) ... 1.0(very blue)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_white_balance_manual(NDIlib_recv_instance_t p_instance, const float red, const float blue);
+
+// Put the camera in auto-exposure mode
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_exposure_auto(NDIlib_recv_instance_t p_instance);
+
+// Manually set the camera exposure
+// exposure_level = 0.0(dark) ... 1.0(light)
+PROCESSINGNDILIB_API
+bool NDIlib_recv_ptz_exposure_manual(NDIlib_recv_instance_t p_instance, const float exposure_level);
+
+//**************************************************************************************************************************************************************
+// Recording control
+// This will start recording.If the recorder was already recording then the message is ignored.A filename is passed in as a "hint".Since the recorder might
+// already be recording(or might not allow complete flexibility over its filename), the filename might or might not be used.If the filename is empty, or
+// not present, a name will be chosen automatically. If you do not with to provide a filename hint you can simply pass nullptr.
+PROCESSINGNDILIB_API
+bool NDIlib_recv_recording_start(NDIlib_recv_instance_t p_instance, const char* p_filename_hint);
+
+// Stop recording.
+PROCESSINGNDILIB_API
+bool NDIlib_recv_recording_stop(NDIlib_recv_instance_t p_instance);
+
+// This will control the audio level for the recording.dB is specified in decibels relative to the reference level of the source. Not all recording sources support
+// controlling audio levels.For instance, a digital audio device would not be able to avoid clipping on sources already at the wrong level, thus
+// might not support this message.
+PROCESSINGNDILIB_API
+bool NDIlib_recv_recording_set_audio_level(NDIlib_recv_instance_t p_instance, const float level_dB);
+
+// This will determine if the source is currently recording. It will return true while recording is in progress and false when it is not. Because there is
+// one recorded and multiple people might be connected to it, there is a chance that it is recording which was initiated by someone else.
+PROCESSINGNDILIB_API
+bool NDIlib_recv_recording_is_recording(NDIlib_recv_instance_t p_instance);
+
+// Get the current filename for recording. When this is set it will return a non-nullptr value which is owned by you and freed using NDIlib_recv_free_string.
+// If a file was already being recorded by another client, the massage will contain the name of that file. The filename contains a UNC path (when one is available)
+// to the recorded file, and can be used to access the file on your local machine for playback. If a UNC path is not available, then this will represent the local
+// filename. This will remain valid even after the file has stopped being recorded until the next file is started.
+PROCESSINGNDILIB_API
+const char* NDIlib_recv_recording_get_filename(NDIlib_recv_instance_t p_instance);
+
+// This will tell you whether there was a recording error and what that string is. When this is set it will return a non-nullptr value which is owned by you and
+// freed using NDIlib_recv_free_string. When there is no error it will return nullptr.
+PROCESSINGNDILIB_API
+const char* NDIlib_recv_recording_get_error(NDIlib_recv_instance_t p_instance);
+
+// In order to get the duration
+typedef struct NDIlib_recv_recording_time_t
+{ // The number of actual video frames recorded.
+ int64_t no_frames;
+
+ // The starting time and current largest time of the record, in UTC time, at 100ns unit intervals. This allows you to know the record
+ // time irrespective of frame-rate. For instance, last_time - start_time woudl give you the recording length in 100ns intervals.
+ int64_t start_time, last_time;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_recv_recording_time_t(void);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_recv_recording_time_t;
+
+// Get the current recording times. These remain
+PROCESSINGNDILIB_API
+bool NDIlib_recv_recording_get_times(NDIlib_recv_instance_t p_instance, NDIlib_recv_recording_time_t* p_times);
\ No newline at end of file
diff --git a/src/modules/newtek/interop/Processing.NDI.Recv.h b/src/modules/newtek/interop/Processing.NDI.Recv.h
new file mode 100644
index 0000000000..b7c8ac7f71
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.Recv.h
@@ -0,0 +1,222 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+//**************************************************************************************************************************
+// Structures and type definitions required by NDI finding
+// The reference to an instance of the receiver
+typedef void* NDIlib_recv_instance_t;
+
+typedef enum NDIlib_recv_bandwidth_e
+{
+ NDIlib_recv_bandwidth_metadata_only = -10, // Receive metadata.
+ NDIlib_recv_bandwidth_audio_only = 10, // Receive metadata, audio.
+ NDIlib_recv_bandwidth_lowest = 0, // Receive metadata, audio, video at a lower bandwidth and resolution.
+ NDIlib_recv_bandwidth_highest = 100 // Receive metadata, audio, video at full resolution.
+
+} NDIlib_recv_bandwidth_e;
+
+typedef enum NDIlib_recv_color_format_e
+{
+ NDIlib_recv_color_format_BGRX_BGRA = 0, // No alpha channel: BGRX, Alpha channel: BGRA
+ NDIlib_recv_color_format_UYVY_BGRA = 1, // No alpha channel: UYVY, Alpha channel: BGRA
+ NDIlib_recv_color_format_RGBX_RGBA = 2, // No alpha channel: RGBX, Alpha channel: RGBA
+ NDIlib_recv_color_format_UYVY_RGBA = 3, // No alpha channel: UYVY, Alpha channel: RGBA
+
+#ifdef _WIN32
+ // On Windows there are some APIs that require bottom to top images in RGBA format. Specifying
+ // this format will return images in this format. The image data pointer will still point to the
+ // "top" of the image, althought he stride will be negative. You can get the "bottom" line of the image
+ // using : video_data.p_data + (video_data.yres - 1)*video_data.line_stride_in_bytes
+ NDIlib_recv_color_format_BGRX_BGRA_flipped = 200,
+#endif
+
+ // Read the SDK documentation to understand the pros and cons of this format.
+ NDIlib_recv_color_format_fastest = 100,
+
+ // Legacy definitions for backwards compatibility
+ NDIlib_recv_color_format_e_BGRX_BGRA = NDIlib_recv_color_format_BGRX_BGRA,
+ NDIlib_recv_color_format_e_UYVY_BGRA = NDIlib_recv_color_format_UYVY_BGRA,
+ NDIlib_recv_color_format_e_RGBX_RGBA = NDIlib_recv_color_format_RGBX_RGBA,
+ NDIlib_recv_color_format_e_UYVY_RGBA = NDIlib_recv_color_format_UYVY_RGBA
+
+} NDIlib_recv_color_format_e;
+
+// The creation structure that is used when you are creating a receiver
+typedef struct NDIlib_recv_create_v3_t
+{ // The source that you wish to connect to.
+ NDIlib_source_t source_to_connect_to;
+
+ // Your preference of color space. See above.
+ NDIlib_recv_color_format_e color_format;
+
+ // The bandwidth setting that you wish to use for this video source. Bandwidth
+ // controlled by changing both the compression level and the resolution of the source.
+ // A good use for low bandwidth is working on WIFI connections.
+ NDIlib_recv_bandwidth_e bandwidth;
+
+ // When this flag is FALSE, all video that you receive will be progressive. For sources
+ // that provide fields, this is de-interlaced on the receiving side (because we cannot change
+ // what the up-stream source was actually rendering. This is provided as a convenience to
+ // down-stream sources that do not wish to understand fielded video. There is almost no
+ // performance impact of using this function.
+ bool allow_video_fields;
+
+ // The name of the NDI receiver to create. This is a nullptr terminated UTF8 string and should be
+ // the name of receive channel that you have. This is in many ways symettric with the name of
+ // senders, so this might be "Channel 1" on your system. If this is nullptr then it will use the
+ // filename of your application indexed with the number of the instance number of this receiver.
+ // If your applicaiton is My
+ const char* p_ndi_recv_name;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_recv_create_v3_t(const NDIlib_source_t source_to_connect_to_ = NDIlib_source_t(), NDIlib_recv_color_format_e color_format_ = NDIlib_recv_color_format_UYVY_BGRA,
+ NDIlib_recv_bandwidth_e bandwidth_ = NDIlib_recv_bandwidth_highest, bool allow_video_fields_ = true, const char* p_ndi_name_ = nullptr);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_recv_create_v3_t;
+
+
+// This allows you determine the current performance levels of the receiving to be able to detect whether frames have been dropped
+typedef struct NDIlib_recv_performance_t
+{ // The number of video frames
+ int64_t video_frames;
+
+ // The number of audio frames
+ int64_t audio_frames;
+
+ // The number of metadata frames
+ int64_t metadata_frames;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_recv_performance_t(void);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_recv_performance_t;
+
+// Get the current queue depths
+typedef struct NDIlib_recv_queue_t
+{ // The number of video frames
+ int video_frames;
+
+ // The number of audio frames
+ int audio_frames;
+
+ // The number of metadata frames
+ int metadata_frames;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_recv_queue_t(void);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_recv_queue_t;
+
+//**************************************************************************************************************************
+// Create a new receiver instance. This will return nullptr if it fails. If you create this with the default settings (nullptr)
+// then it will automatically determine a receiver name.
+PROCESSINGNDILIB_API
+NDIlib_recv_instance_t NDIlib_recv_create_v3(const NDIlib_recv_create_v3_t* p_create_settings NDILIB_CPP_DEFAULT_VALUE(nullptr));
+
+// This will destroy an existing receiver instance.
+PROCESSINGNDILIB_API
+void NDIlib_recv_destroy(NDIlib_recv_instance_t p_instance);
+
+// This function allows you to change the connection to another video source, you can also disconnect it by specifying a nullptr here.
+// This allows you to preserve a receiver without needing to
+PROCESSINGNDILIB_API
+void NDIlib_recv_connect(NDIlib_recv_instance_t p_instance, const NDIlib_source_t* p_src NDILIB_CPP_DEFAULT_VALUE(nullptr));
+
+// This will allow you to receive video, audio and metadata frames.
+// Any of the buffers can be nullptr, in which case data of that type
+// will not be captured in this call. This call can be called simultaneously
+// on separate threads, so it is entirely possible to receive audio, video, metadata
+// all on separate threads. This function will return NDIlib_frame_type_none if no
+// data is received within the specified timeout and NDIlib_frame_type_error if the connection is lost.
+// Buffers captured with this must be freed with the appropriate free function below.
+PROCESSINGNDILIB_API
+NDIlib_frame_type_e NDIlib_recv_capture_v2(
+ NDIlib_recv_instance_t p_instance, // The library instance
+ NDIlib_video_frame_v2_t* p_video_data, // The video data received (can be nullptr)
+ NDIlib_audio_frame_v2_t* p_audio_data, // The audio data received (can be nullptr)
+ NDIlib_metadata_frame_t* p_metadata, // The metadata received (can be nullptr)
+ uint32_t timeout_in_ms); // The amount of time in milliseconds to wait for data.
+
+// Free the buffers returned by capture for video
+PROCESSINGNDILIB_API
+void NDIlib_recv_free_video_v2(NDIlib_recv_instance_t p_instance, const NDIlib_video_frame_v2_t* p_video_data);
+
+// Free the buffers returned by capture for audio
+PROCESSINGNDILIB_API
+void NDIlib_recv_free_audio_v2(NDIlib_recv_instance_t p_instance, const NDIlib_audio_frame_v2_t* p_audio_data);
+
+// Free the buffers returned by capture for metadata
+PROCESSINGNDILIB_API
+void NDIlib_recv_free_metadata(NDIlib_recv_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+
+// This will free a string that was allocated and returned by NDIlib_recv (for instance the NDIlib_recv_get_web_control) function.
+PROCESSINGNDILIB_API
+void NDIlib_recv_free_string(NDIlib_recv_instance_t p_instance, const char* p_string);
+
+// This function will send a meta message to the source that we are connected too. This returns FALSE if we are
+// not currently connected to anything.
+PROCESSINGNDILIB_API
+bool NDIlib_recv_send_metadata(NDIlib_recv_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+
+// Set the up-stream tally notifications. This returns FALSE if we are not currently connected to anything. That
+// said, the moment that we do connect to something it will automatically be sent the tally state.
+PROCESSINGNDILIB_API
+bool NDIlib_recv_set_tally(NDIlib_recv_instance_t p_instance, const NDIlib_tally_t* p_tally);
+
+// Get the current performance structures. This can be used to determine if you have been calling NDIlib_recv_capture fast
+// enough, or if your processing of data is not keeping up with real-time. The total structure will give you the total frame
+// counts received, the dropped structure will tell you how many frames have been dropped. Either of these could be nullptr.
+PROCESSINGNDILIB_API
+void NDIlib_recv_get_performance(NDIlib_recv_instance_t p_instance, NDIlib_recv_performance_t* p_total, NDIlib_recv_performance_t* p_dropped);
+
+// This will allow you to determine the current queue depth for all of the frame sources at any time.
+PROCESSINGNDILIB_API
+void NDIlib_recv_get_queue(NDIlib_recv_instance_t p_instance, NDIlib_recv_queue_t* p_total);
+
+// Connection based metadata is data that is sent automatically each time a new connection is received. You queue all of these
+// up and they are sent on each connection. To reset them you need to clear them all and set them up again.
+PROCESSINGNDILIB_API
+void NDIlib_recv_clear_connection_metadata(NDIlib_recv_instance_t p_instance);
+
+// Add a connection metadata string to the list of what is sent on each new connection. If someone is already connected then
+// this string will be sent to them immediately.
+PROCESSINGNDILIB_API
+void NDIlib_recv_add_connection_metadata(NDIlib_recv_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+
+// Is this receiver currently connected to a source on the other end, or has the source not yet been found or is no longe ronline.
+// This will normally return 0 or 1
+PROCESSINGNDILIB_API
+int NDIlib_recv_get_no_connections(NDIlib_recv_instance_t p_instance);
+
+// Get the URL that might be used for configuration of this input. Note that it might take a second or two after the connection for
+// this value to be set. This function will return nullptr if there is no web control user interface. You should call NDIlib_recv_free_string
+// to free the string that is returned by this function. The returned value will be a fully formed URL, for instamce "http://10.28.1.192/configuration/"
+// To avoid the need to poll this function, you can know when the value of this function might have changed when the
+// NDILib_recv_capture* call would return NDIlib_frame_type_status_change
+PROCESSINGNDILIB_API
+const char* NDIlib_recv_get_web_control(NDIlib_recv_instance_t p_instance);
diff --git a/src/modules/newtek/interop/Processing.NDI.Routing.h b/src/modules/newtek/interop/Processing.NDI.Routing.h
new file mode 100644
index 0000000000..9e26c002f8
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.Routing.h
@@ -0,0 +1,59 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+//**************************************************************************************************************************
+// Structures and type definitions required by NDI sending
+// The reference to an instance of the sender
+typedef void* NDIlib_routing_instance_t;
+
+// The creation structure that is used when you are creating a sender
+typedef struct NDIlib_routing_create_t
+{ // The name of the NDI source to create. This is a nullptr terminated UTF8 string.
+ const char* p_ndi_name;
+
+ // What groups should this source be part of
+ const char* p_groups;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_routing_create_t(const char* p_ndi_name_ = nullptr, const char* p_groups_ = nullptr);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_routing_create_t;
+
+// Create an NDI routing source
+PROCESSINGNDILIB_API
+NDIlib_routing_instance_t NDIlib_routing_create(const NDIlib_routing_create_t* p_create_settings);
+
+// Destroy and NDI routing source
+PROCESSINGNDILIB_API
+void NDIlib_routing_destroy(NDIlib_routing_instance_t p_instance);
+
+// Change the routing of this source to another destination
+PROCESSINGNDILIB_API
+bool NDIlib_routing_change(NDIlib_routing_instance_t p_instance, const NDIlib_source_t* p_source);
+
+// Change the routing of this source to another destination
+PROCESSINGNDILIB_API
+bool NDIlib_routing_clear(NDIlib_routing_instance_t p_instance);
\ No newline at end of file
diff --git a/src/modules/newtek/interop/Processing.NDI.Send.h b/src/modules/newtek/interop/Processing.NDI.Send.h
new file mode 100644
index 0000000000..ca2598ca7b
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.Send.h
@@ -0,0 +1,127 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+//**************************************************************************************************************************
+// Structures and type definitions required by NDI sending
+// The reference to an instance of the sender
+typedef void* NDIlib_send_instance_t;
+
+// The creation structure that is used when you are creating a sender
+typedef struct NDIlib_send_create_t
+{ // The name of the NDI source to create. This is a nullptr terminated UTF8 string.
+ const char* p_ndi_name;
+
+ // What groups should this source be part of. nullptr means default.
+ const char* p_groups;
+
+ // Do you want audio and video to "clock" themselves. When they are clocked then
+ // by adding video frames, they will be rate limited to match the current frame-rate
+ // that you are submitting at. The same is true for audio. In general if you are submitting
+ // video and audio off a single thread then you should only clock one of them (video is
+ // probably the better of the two to clock off). If you are submtiting audio and video
+ // of separate threads then having both clocked can be useful.
+ bool clock_video, clock_audio;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_send_create_t(const char* p_ndi_name_ = nullptr, const char* p_groups_ = nullptr, bool clock_video_ = true, bool clock_audio_ = true);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_send_create_t;
+
+// Create a new sender instance. This will return nullptr if it fails. If you specify leave p_create_settings null then
+// the sender will be created with default settings.
+PROCESSINGNDILIB_API
+NDIlib_send_instance_t NDIlib_send_create(const NDIlib_send_create_t* p_create_settings NDILIB_CPP_DEFAULT_VALUE(nullptr) );
+
+// This will destroy an existing finder instance.
+PROCESSINGNDILIB_API
+void NDIlib_send_destroy(NDIlib_send_instance_t p_instance);
+
+// This will add a video frame
+PROCESSINGNDILIB_API
+void NDIlib_send_send_video_v2(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_v2_t* p_video_data);
+
+// This will add a video frame and will return immediately, having scheduled the frame to be displayed.
+// All processing and sending of the video will occur asynchronously. The memory accessed by NDIlib_video_frame_t
+// cannot be freed or re-used by the caller until a synchronizing event has occurred. In general the API is better
+// able to take advantage of asynchronous processing than you might be able to by simple having a separate thread
+// to submit frames.
+//
+// This call is particularly beneficial when processing BGRA video since it allows any color conversion, compression
+// and network sending to all be done on separate threads from your main rendering thread.
+//
+// Synchronozing events are :
+// - a call to NDIlib_send_send_video
+// - a call to NDIlib_send_send_video_async with another frame to be sent
+// - a call to NDIlib_send_send_video with p_video_data=nullptr
+// - a call to NDIlib_send_destroy
+PROCESSINGNDILIB_API
+void NDIlib_send_send_video_async_v2(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_v2_t* p_video_data);
+
+// This will add an audio frame
+PROCESSINGNDILIB_API
+void NDIlib_send_send_audio_v2(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_v2_t* p_audio_data);
+
+// This will add a metadata frame
+PROCESSINGNDILIB_API
+void NDIlib_send_send_metadata(NDIlib_send_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+
+// This allows you to receive metadata from the other end of the connection
+PROCESSINGNDILIB_API
+NDIlib_frame_type_e NDIlib_send_capture(
+ NDIlib_send_instance_t p_instance, // The instance data
+ NDIlib_metadata_frame_t* p_metadata, // The metadata received (can be nullptr)
+ uint32_t timeout_in_ms); // The amount of time in milliseconds to wait for data.
+
+// Free the buffers returned by capture for metadata
+PROCESSINGNDILIB_API
+void NDIlib_send_free_metadata(NDIlib_send_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+
+// Determine the current tally sate. If you specify a timeout then it will wait until it has changed, otherwise it will simply poll it
+// and return the current tally immediately. The return value is whether anything has actually change (true) or whether it timed out (false)
+PROCESSINGNDILIB_API
+bool NDIlib_send_get_tally(NDIlib_send_instance_t p_instance, NDIlib_tally_t* p_tally, uint32_t timeout_in_ms);
+
+// Get the current number of receivers connected to this source. This can be used to avoid even rendering when nothing is connected to the video source.
+// which can significantly improve the efficiency if you want to make a lot of sources available on the network. If you specify a timeout that is not
+// 0 then it will wait until there are connections for this amount of time.
+PROCESSINGNDILIB_API
+int NDIlib_send_get_no_connections(NDIlib_send_instance_t p_instance, uint32_t timeout_in_ms);
+
+// Connection based metadata is data that is sent automatically each time a new connection is received. You queue all of these
+// up and they are sent on each connection. To reset them you need to clear them all and set them up again.
+PROCESSINGNDILIB_API
+void NDIlib_send_clear_connection_metadata(NDIlib_send_instance_t p_instance);
+
+// Add a connection metadata string to the list of what is sent on each new connection. If someone is already connected then
+// this string will be sent to them immediately.
+PROCESSINGNDILIB_API
+void NDIlib_send_add_connection_metadata(NDIlib_send_instance_t p_instance, const NDIlib_metadata_frame_t* p_metadata);
+
+// This will assign a new fail-over source for this video source. What this means is that if this video source was to fail
+// any receivers would automatically switch over to use this source, unless this source then came back online. You can specify
+// nullptr to clear the source.
+PROCESSINGNDILIB_API
+void NDIlib_send_set_failover(NDIlib_send_instance_t p_instance, const NDIlib_source_t* p_failover_source);
diff --git a/src/modules/newtek/interop/Processing.NDI.compat.h b/src/modules/newtek/interop/Processing.NDI.compat.h
new file mode 100644
index 0000000000..31d9963569
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.compat.h
@@ -0,0 +1,39 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+#ifdef __cplusplus
+
+#include
+
+#else
+
+#include
+#include
+
+#endif
+
+#ifndef INFINITE
+#define INFINITE 0xFFFFFFFF
+#endif
diff --git a/src/modules/newtek/interop/Processing.NDI.deprecated.h b/src/modules/newtek/interop/Processing.NDI.deprecated.h
new file mode 100644
index 0000000000..bc5e62bfa4
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.deprecated.h
@@ -0,0 +1,216 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+// This describes a video frame
+PROCESSINGNDILIB_DEPRECATED
+typedef struct NDIlib_video_frame_t
+{ // The resolution of this frame
+ int xres, yres;
+
+ // What FourCC this is with. This can be two values
+ NDIlib_FourCC_type_e FourCC;
+
+ // What is the frame-rate of this frame.
+ // For instance NTSC is 30000,1001 = 30000/1001 = 29.97fps
+ int frame_rate_N, frame_rate_D;
+
+ // What is the picture aspect ratio of this frame.
+ // For instance 16.0/9.0 = 1.778 is 16:9 video. If this is zero, then square pixels are assumed (xres/yres)
+ float picture_aspect_ratio;
+
+ // Is this a fielded frame, or is it progressive
+ NDIlib_frame_format_type_e frame_format_type;
+
+ // The timecode of this frame in 100ns intervals
+ int64_t timecode;
+
+ // The video data itself
+ uint8_t* p_data;
+
+ // The inter line stride of the video data, in bytes.
+ int line_stride_in_bytes;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_video_frame_t(int xres_ = 0, int yres_ = 0, NDIlib_FourCC_type_e FourCC_ = NDIlib_FourCC_type_UYVY, int frame_rate_N_ = 30000, int frame_rate_D_ = 1001,
+ float picture_aspect_ratio_ = 0.0f, NDIlib_frame_format_type_e frame_format_type_ = NDIlib_frame_format_type_progressive,
+ int64_t timecode_ = NDIlib_send_timecode_synthesize, uint8_t* p_data_ = nullptr, int line_stride_in_bytes_ = 0);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_video_frame_t;
+
+// This describes an audio frame
+PROCESSINGNDILIB_DEPRECATED
+typedef struct NDIlib_audio_frame_t
+{ // The sample-rate of this buffer
+ int sample_rate;
+
+ // The number of audio channels
+ int no_channels;
+
+ // The number of audio samples per channel
+ int no_samples;
+
+ // The timecode of this frame in 100ns intervals
+ int64_t timecode;
+
+ // The audio data
+ float* p_data;
+
+ // The inter channel stride of the audio channels, in bytes
+ int channel_stride_in_bytes;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_audio_frame_t(int sample_rate_ = 48000, int no_channels_ = 2, int no_samples_ = 0, int64_t timecode_ = NDIlib_send_timecode_synthesize,
+ float* p_data_ = nullptr, int channel_stride_in_bytes_ = 0);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_audio_frame_t;
+
+// For legacy reasons I called this the wrong thing. For backwards compatibility.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+NDIlib_find_instance_t NDIlib_find_create2(const NDIlib_find_create_t* p_create_settings NDILIB_CPP_DEFAULT_VALUE(nullptr));
+
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+NDIlib_find_instance_t NDIlib_find_create(const NDIlib_find_create_t* p_create_settings NDILIB_CPP_DEFAULT_VALUE(nullptr));
+
+// DEPRECATED. This function is basically exactly the following and was confusing to use.
+// if ((!timeout_in_ms) || (NDIlib_find_wait_for_sources(timeout_in_ms)))
+// return NDIlib_find_get_current_sources(p_instance, p_no_sources);
+// return nullptr;
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+const NDIlib_source_t* NDIlib_find_get_sources(NDIlib_find_instance_t p_instance, uint32_t* p_no_sources, uint32_t timeout_in_ms);
+
+// The creation structure that is used when you are creating a receiver
+PROCESSINGNDILIB_DEPRECATED
+typedef struct NDIlib_recv_create_t
+{ // The source that you wish to connect to.
+ NDIlib_source_t source_to_connect_to;
+
+ // Your preference of color space. See above.
+ NDIlib_recv_color_format_e color_format;
+
+ // The bandwidth setting that you wish to use for this video source. Bandwidth
+ // controlled by changing both the compression level and the resolution of the source.
+ // A good use for low bandwidth is working on WIFI connections.
+ NDIlib_recv_bandwidth_e bandwidth;
+
+ // When this flag is FALSE, all video that you receive will be progressive. For sources
+ // that provide fields, this is de-interlaced on the receiving side (because we cannot change
+ // what the up-stream source was actually rendering. This is provided as a convenience to
+ // down-stream sources that do not wish to understand fielded video. There is almost no
+ // performance impact of using this function.
+ bool allow_video_fields;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_recv_create_t(const NDIlib_source_t source_to_connect_to_ = NDIlib_source_t(), NDIlib_recv_color_format_e color_format_ = NDIlib_recv_color_format_UYVY_BGRA,
+ NDIlib_recv_bandwidth_e bandwidth_ = NDIlib_recv_bandwidth_highest, bool allow_video_fields_ = true);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_recv_create_t;
+
+// This function is deprecated, please use NDIlib_recv_create_v3 if you can. Using this function will continue to work, and be
+// supported for backwards compatibility. If the input parameter is nullptr it will be created with default settings and an automatically
+// determined receiver name,
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+NDIlib_recv_instance_t NDIlib_recv_create_v2(const NDIlib_recv_create_t* p_create_settings NDILIB_CPP_DEFAULT_VALUE(nullptr) );
+
+// For legacy reasons I called this the wrong thing. For backwards compatibility. If the input parameter is nullptr it will be created with
+// default settings and an automatically determined receiver name.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+NDIlib_recv_instance_t NDIlib_recv_create2(const NDIlib_recv_create_t* p_create_settings NDILIB_CPP_DEFAULT_VALUE(nullptr));
+
+// This function is deprecated, please use NDIlib_recv_create_v3 if you can. Using this function will continue to work, and be
+// supported for backwards compatibility. This version sets bandwidth to highest and allow fields to true. If the input parameter is nullptr it
+// will be created with default settings and an automatically determined receiver name.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+NDIlib_recv_instance_t NDIlib_recv_create(const NDIlib_recv_create_t* p_create_settings);
+
+// This will allow you to receive video, audio and metadata frames.
+// Any of the buffers can be nullptr, in which case data of that type
+// will not be captured in this call. This call can be called simultaneously
+// on separate threads, so it is entirely possible to receive audio, video, metadata
+// all on separate threads. This function will return NDIlib_frame_type_none if no
+// data is received within the specified timeout and NDIlib_frame_type_error if the connection is lost.
+// Buffers captured with this must be freed with the appropriate free function below.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+NDIlib_frame_type_e NDIlib_recv_capture(
+NDIlib_recv_instance_t p_instance, // The library instance
+NDIlib_video_frame_t* p_video_data, // The video data received (can be nullptr)
+NDIlib_audio_frame_t* p_audio_data, // The audio data received (can be nullptr)
+NDIlib_metadata_frame_t* p_metadata, // The metadata received (can be nullptr)
+uint32_t timeout_in_ms); // The amount of time in milliseconds to wait for data.
+
+// Free the buffers returned by capture for video
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_recv_free_video(NDIlib_recv_instance_t p_instance, const NDIlib_video_frame_t* p_video_data);
+
+// Free the buffers returned by capture for audio
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_recv_free_audio(NDIlib_recv_instance_t p_instance, const NDIlib_audio_frame_t* p_audio_data);
+
+// This will add a video frame
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_send_send_video(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_t* p_video_data);
+
+// This will add a video frame and will return immediately, having scheduled the frame to be displayed.
+// All processing and sending of the video will occur asynchronously. The memory accessed by NDIlib_video_frame_t
+// cannot be freed or re-used by the caller until a synchronizing event has occurred. In general the API is better
+// able to take advantage of asynchronous processing than you might be able to by simple having a separate thread
+// to submit frames.
+//
+// This call is particularly beneficial when processing BGRA video since it allows any color conversion, compression
+// and network sending to all be done on separate threads from your main rendering thread.
+//
+// Synchronizing events are :
+// - a call to NDIlib_send_send_video
+// - a call to NDIlib_send_send_video_async with another frame to be sent
+// - a call to NDIlib_send_send_video with p_video_data=nullptr
+// - a call to NDIlib_send_destroy
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_send_send_video_async(NDIlib_send_instance_t p_instance, const NDIlib_video_frame_t* p_video_data);
+
+// This will add an audio frame
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_send_send_audio(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_t* p_audio_data);
+
+// Convert an planar floating point audio buffer into a interleaved short audio buffer.
+// IMPORTANT : You must allocate the space for the samples in the destination to allow for your own memory management.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_util_audio_to_interleaved_16s(const NDIlib_audio_frame_t* p_src, NDIlib_audio_frame_interleaved_16s_t* p_dst);
+
+// Convert an interleaved short audio buffer audio buffer into a planar floating point one.
+// IMPORTANT : You must allocate the space for the samples in the destination to allow for your own memory management.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_util_audio_from_interleaved_16s(const NDIlib_audio_frame_interleaved_16s_t* p_src, NDIlib_audio_frame_t* p_dst);
+
+// Convert an planar floating point audio buffer into a interleaved floating point audio buffer.
+// IMPORTANT : You must allocate the space for the samples in the destination to allow for your own memory management.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_util_audio_to_interleaved_32f(const NDIlib_audio_frame_t* p_src, NDIlib_audio_frame_interleaved_32f_t* p_dst);
+
+// Convert an interleaved floating point audio buffer into a planar floating point one.
+// IMPORTANT : You must allocate the space for the samples in the destination to allow for your own memory management.
+PROCESSINGNDILIB_API PROCESSINGNDILIB_DEPRECATED
+void NDIlib_util_audio_from_interleaved_32f(const NDIlib_audio_frame_interleaved_32f_t* p_src, NDIlib_audio_frame_t* p_dst);
diff --git a/src/modules/newtek/interop/Processing.NDI.structs.h b/src/modules/newtek/interop/Processing.NDI.structs.h
new file mode 100644
index 0000000000..eb7598d4e7
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.structs.h
@@ -0,0 +1,258 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+#ifndef NDI_LIB_FOURCC
+#define NDI_LIB_FOURCC(ch0, ch1, ch2, ch3) \
+ ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24))
+#endif
+
+// An enumeration to specify the type of a packet returned by the functions
+typedef enum NDIlib_frame_type_e
+{ // What frame type is this ?
+ NDIlib_frame_type_none = 0,
+ NDIlib_frame_type_video = 1,
+ NDIlib_frame_type_audio = 2,
+ NDIlib_frame_type_metadata = 3,
+ NDIlib_frame_type_error = 4,
+
+ // This indicates that the settings on this input have changed.
+ // For instamce, this value will be returned from NDIlib_recv_capture_v2 and NDIlib_recv_capture
+ // when the device is known to have new settings, for instance the web-url has changed ot the device
+ // is now known to be a PTZ camera.
+ NDIlib_frame_type_status_change = 100
+
+} NDIlib_frame_type_e;
+
+typedef enum NDIlib_FourCC_type_e
+{ // YCbCr color space
+ NDIlib_FourCC_type_UYVY = NDI_LIB_FOURCC('U', 'Y', 'V', 'Y'),
+
+ // 4:2:0 formats
+ NDIlib_FourCC_type_YV12 = NDI_LIB_FOURCC('Y', 'V', '1', '2'),
+ NDIlib_FourCC_type_NV12 = NDI_LIB_FOURCC('N', 'V', '1', '2'),
+ NDIlib_FourCC_type_I420 = NDI_LIB_FOURCC('I', '4', '2', '0'),
+
+ // BGRA
+ NDIlib_FourCC_type_BGRA = NDI_LIB_FOURCC('B', 'G', 'R', 'A'),
+ NDIlib_FourCC_type_BGRX = NDI_LIB_FOURCC('B', 'G', 'R', 'X'),
+
+ // RGBA
+ NDIlib_FourCC_type_RGBA = NDI_LIB_FOURCC('R', 'G', 'B', 'A'),
+ NDIlib_FourCC_type_RGBX = NDI_LIB_FOURCC('R', 'G', 'B', 'X'),
+
+ // This is a UYVY buffer followed immediately by an alpha channel buffer.
+ // If the stride of the YCbCr component is "stride", then the alpha channel
+ // starts at image_ptr + yres*stride. The alpha channel stride is stride/2.
+ NDIlib_FourCC_type_UYVA = NDI_LIB_FOURCC('U', 'Y', 'V', 'A')
+
+} NDIlib_FourCC_type_e;
+
+typedef enum NDIlib_frame_format_type_e
+{ // A progressive frame
+ NDIlib_frame_format_type_progressive = 1,
+
+ // A fielded frame with the field 0 being on the even lines and field 1 being
+ // on the odd lines/
+ NDIlib_frame_format_type_interleaved = 0,
+
+ // Individual fields
+ NDIlib_frame_format_type_field_0 = 2,
+ NDIlib_frame_format_type_field_1 = 3
+
+} NDIlib_frame_format_type_e;
+
+// When you specify this as a timecode, the timecode will be synthesized for you. This may
+// be used when sending video, audio or metadata. If you never specify a timecode at all,
+// asking for each to be synthesized, then this will use the current system time as the
+// starting timecode and then generate synthetic ones, keeping your streams exactly in
+// sync as long as the frames you are sending do not deviate from the system time in any
+// meaningful way. In practice this means that if you never specify timecodes that they
+// will always be generated for you correctly. Timecodes coming from different senders on
+// the same machine will always be in sync with eachother when working in this way. If you
+// have NTP installed on your local network, then streams can be synchronized between
+// multiple machines with very high precision.
+//
+// If you specify a timecode at a particular frame (audio or video), then ask for all subsequent
+// ones to be synthesized. The subsequent ones will be generated to continue this sequency
+// maintining the correct relationship both the between streams and samples generated, avoiding
+// them deviating in time from teh timecode that you specified in any meanginfful way.
+//
+// If you specify timecodes on one stream (e.g. video) and ask for the other stream (audio) to
+// be sythesized, the correct timecodes will be generated for the other stream and will be synthesize
+// exactly to match (they are not quantized inter-streams) the correct sample positions.
+//
+// When you send metadata messagesa and ask for the timecode to be synthesized, then it is chosen
+// to match the closest audio or video frame timecode so that it looks close to something you might
+// want ... unless there is no sample that looks close in which a timecode is synthesized from the
+// last ones known and the time since it was sent.
+//
+static const int64_t NDIlib_send_timecode_synthesize = INT64_MAX;
+
+// If the time-stamp is not available (i.e. a version of a sender before v2.5)
+static const int64_t NDIlib_recv_timestamp_undefined = INT64_MAX;
+
+// This is a descriptor of a NDI source available on the network.
+typedef struct NDIlib_source_t
+{ // A UTF8 string that provides a user readable name for this source.
+ // This can be used for serialization, etc... and comprises the machine
+ // name and the source name on that machine. In the form
+ // MACHINE_NAME (NDI_SOURCE_NAME)
+ // If you specify this parameter either as nullptr, or an EMPTY string then the
+ // specific ip addres adn port number from below is used.
+ const char* p_ndi_name;
+
+ // A UTF8 string that provides the actual network address and any parameters.
+ // This is not meant to be application readable and might well change in teh future.
+ // This can be nullptr if you do not know it and the API internally will instantiate
+ // a finder that is used to discover it even if it is not yet available on the network.
+ union
+ { // The current way of addressing the value
+ const char* p_url_address;
+
+ // We used to use an IP address before we used the more general URL notification
+ // this is now depreciated byt maintained for compatability.
+ PROCESSINGNDILIB_DEPRECATED const char* p_ip_address;
+ };
+
+ // Default constructor in C++
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_source_t(const char* p_ndi_name_ = nullptr, const char* p_url_address_ = nullptr);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_source_t;
+
+// This describes a video frame
+typedef struct NDIlib_video_frame_v2_t
+{ // The resolution of this frame
+ int xres, yres;
+
+ // What FourCC this is with. This can be two values
+ NDIlib_FourCC_type_e FourCC;
+
+ // What is the frame-rate of this frame.
+ // For instance NTSC is 30000,1001 = 30000/1001 = 29.97fps
+ int frame_rate_N, frame_rate_D;
+
+ // What is the picture aspect ratio of this frame.
+ // For instance 16.0/9.0 = 1.778 is 16:9 video
+ // 0 means square pixels
+ float picture_aspect_ratio;
+
+ // Is this a fielded frame, or is it progressive
+ NDIlib_frame_format_type_e frame_format_type;
+
+ // The timecode of this frame in 100ns intervals
+ int64_t timecode;
+
+ // The video data itself
+ uint8_t* p_data;
+
+ // The inter line stride of the video data, in bytes. If the stride is 0 then it is sizeof(one pixel)*xres
+ int line_stride_in_bytes;
+
+ // Per frame metadata for this frame. This is a nullptr terminated UTF8 string that should be
+ // in XML format. If you do not want any metadata then you may specify nullptr here.
+ const char* p_metadata; // Present in >= v2.5
+
+ // This is only valid when receiving a frame and is specified as a 100ns time that was the exact
+ // moment that the frame was submitted by the sending side and is generated by the SDK. If this
+ // value is NDIlib_recv_timestamp_undefined then this value is not available and is NDIlib_recv_timestamp_undefined.
+ int64_t timestamp; // Present in >= v2.5
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_video_frame_v2_t(int xres_ = 0, int yres_ = 0, NDIlib_FourCC_type_e FourCC_ = NDIlib_FourCC_type_UYVY, int frame_rate_N_ = 30000, int frame_rate_D_ = 1001,
+ float picture_aspect_ratio_ = 0.0f, NDIlib_frame_format_type_e frame_format_type_ = NDIlib_frame_format_type_progressive,
+ int64_t timecode_ = NDIlib_send_timecode_synthesize, uint8_t* p_data_ = nullptr, int line_stride_in_bytes_ = 0, const char* p_metadata_ = nullptr, int64_t timestamp_ = 0);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_video_frame_v2_t;
+
+// This describes an audio frame
+typedef struct NDIlib_audio_frame_v2_t
+{ // The sample-rate of this buffer
+ int sample_rate;
+
+ // The number of audio channels
+ int no_channels;
+
+ // The number of audio samples per channel
+ int no_samples;
+
+ // The timecode of this frame in 100ns intervals
+ int64_t timecode;
+
+ // The audio data
+ float* p_data;
+
+ // The inter channel stride of the audio channels, in bytes
+ int channel_stride_in_bytes;
+
+ // Per frame metadata for this frame. This is a nullptr terminated UTF8 string that should be
+ // in XML format. If you do not want any metadata then you may specify nullptr here.
+ const char* p_metadata; // Present in >= v2.5
+
+ // This is only valid when receiving a frame and is specified as a 100ns time that was the exact
+ // moment that the frame was submitted by the sending side and is generated by the SDK. If this
+ // value is NDIlib_recv_timestamp_undefined then this value is not available and is NDIlib_recv_timestamp_undefined.
+ int64_t timestamp; // Present in >= v2.5
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_audio_frame_v2_t(int sample_rate_ = 48000, int no_channels_ = 2, int no_samples_ = 0, int64_t timecode_ = NDIlib_send_timecode_synthesize,
+ float* p_data_ = nullptr, int channel_stride_in_bytes_ = 0, const char* p_metadata_ = nullptr, int64_t timestamp_ = 0);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_audio_frame_v2_t;
+
+// The data description for metadata
+typedef struct NDIlib_metadata_frame_t
+{ // The length of the string in UTF8 characters. This includes the nullptr terminating character.
+ // If this is 0, then the length is assume to be the length of a nullptr terminated string.
+ int length;
+
+ // The timecode of this frame in 100ns intervals
+ int64_t timecode;
+
+ // The metadata as a UTF8 XML string. This is a nullptr terminated string.
+ char* p_data;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_metadata_frame_t(int length_ = 0, int64_t timecode_ = NDIlib_send_timecode_synthesize, char* p_data_ = nullptr);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_metadata_frame_t;
+
+// Tally structures
+typedef struct NDIlib_tally_t
+{ // Is this currently on program output
+ bool on_program;
+
+ // Is this currently on preview output
+ bool on_preview;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_tally_t(bool on_program_ = false, bool on_preview_ = false);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_tally_t;
diff --git a/src/modules/newtek/interop/Processing.NDI.utilities.h b/src/modules/newtek/interop/Processing.NDI.utilities.h
new file mode 100644
index 0000000000..a7312a4d28
--- /dev/null
+++ b/src/modules/newtek/interop/Processing.NDI.utilities.h
@@ -0,0 +1,108 @@
+#pragma once
+
+// NOTE : The following MIT license applies to this file ONLY and not to the SDK as a whole. Please review the SDK documentation
+// for the description of the full license terms, which are also provided in the file "NDI License Agreement.pdf" within the SDK or
+// online at http://new.tk/ndisdk_license/. Your use of any part of this SDK is acknowledgment that you agree to the SDK license
+// terms. The full NDI SDK may be downloaded at https://www.newtek.com/ndi/sdk/
+//
+//***********************************************************************************************************************************************
+//
+// Copyright(c) 2014-2018 NewTek, inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+// files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
+// merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//***********************************************************************************************************************************************
+
+// Because many applications like submitting 16bit interleaved audio, these functions will convert in
+// and out of that format. It is important to note that the NDI SDK does define fully audio levels, something
+// that most applications that you use do not. Specifically, the floating point -1.0 to +1.0 range is defined
+// as a professional audio reference level of +4dBU. If we take 16bit audio and scale it into this range
+// it is almost always correct for sending and will cause no problems. For receiving however it is not at
+// all uncommon that the user has audio that exceeds reference level and in this case it is likely that audio
+// exceeds the reference level and so if you are not careful you will end up having audio clipping when
+// you use the 16 bit range.
+
+// This describes an audio frame
+typedef struct NDIlib_audio_frame_interleaved_16s_t
+{ // The sample-rate of this buffer
+ int sample_rate;
+
+ // The number of audio channels
+ int no_channels;
+
+ // The number of audio samples per channel
+ int no_samples;
+
+ // The timecode of this frame in 100ns intervals
+ int64_t timecode;
+
+ // The audio reference level in dB. This specifies how many dB above the reference level (+4dBU) is the full range of 16 bit audio.
+ // If you do not understand this and want to just use numbers :
+ // - If you are sending audio, specify +0dB. Most common applications produce audio at reference level.
+ // - If receiving audio, specify +20dB. This means that the full 16 bit range corresponds to professional level audio with 20dB of headroom. Note that
+ // if you are writing it into a file it might sound soft because you have 20dB of headroom before clipping.
+ int reference_level;
+
+ // The audio data, interleaved 16bpp
+ short* p_data;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_audio_frame_interleaved_16s_t(int sample_rate_ = 48000, int no_channels_ = 2, int no_samples_ = 0, int64_t timecode_ = NDIlib_send_timecode_synthesize,
+ int reference_level_ = 0, short* p_data_ = nullptr);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_audio_frame_interleaved_16s_t;
+
+// This describes an audio frame
+typedef struct NDIlib_audio_frame_interleaved_32f_t
+{ // The sample-rate of this buffer
+ int sample_rate;
+
+ // The number of audio channels
+ int no_channels;
+
+ // The number of audio samples per channel
+ int no_samples;
+
+ // The timecode of this frame in 100ns intervals
+ int64_t timecode;
+
+ // The audio data, interleaved 32bpp
+ float* p_data;
+
+#if NDILIB_CPP_DEFAULT_CONSTRUCTORS
+ NDIlib_audio_frame_interleaved_32f_t(int sample_rate_ = 48000, int no_channels_ = 2, int no_samples_ = 0, int64_t timecode_ = NDIlib_send_timecode_synthesize,
+ float* p_data_ = nullptr);
+#endif // NDILIB_CPP_DEFAULT_CONSTRUCTORS
+
+} NDIlib_audio_frame_interleaved_32f_t;
+
+// This will add an audio frame in 16bpp
+PROCESSINGNDILIB_API
+void NDIlib_util_send_send_audio_interleaved_16s(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_interleaved_16s_t* p_audio_data);
+
+// This will add an audio frame interleaved floating point
+PROCESSINGNDILIB_API
+void NDIlib_util_send_send_audio_interleaved_32f(NDIlib_send_instance_t p_instance, const NDIlib_audio_frame_interleaved_32f_t* p_audio_data);
+
+PROCESSINGNDILIB_API
+void NDIlib_util_audio_to_interleaved_16s_v2(const NDIlib_audio_frame_v2_t* p_src, NDIlib_audio_frame_interleaved_16s_t* p_dst);
+
+PROCESSINGNDILIB_API
+void NDIlib_util_audio_from_interleaved_16s_v2(const NDIlib_audio_frame_interleaved_16s_t* p_src, NDIlib_audio_frame_v2_t* p_dst);
+
+PROCESSINGNDILIB_API
+void NDIlib_util_audio_to_interleaved_32f_v2(const NDIlib_audio_frame_v2_t* p_src, NDIlib_audio_frame_interleaved_32f_t* p_dst);
+
+PROCESSINGNDILIB_API
+void NDIlib_util_audio_from_interleaved_32f_v2(const NDIlib_audio_frame_interleaved_32f_t* p_src, NDIlib_audio_frame_v2_t* p_dst);
diff --git a/src/modules/newtek/newtek.cpp b/src/modules/newtek/newtek.cpp
index 3c4d9ca051..cacd78cd53 100644
--- a/src/modules/newtek/newtek.cpp
+++ b/src/modules/newtek/newtek.cpp
@@ -24,8 +24,16 @@
#include "consumer/newtek_ivga_consumer.h"
#include "util/air_send.h"
+#include "consumer/newtek_ndi_consumer.h"
+#include "producer/newtek_ndi_producer.h"
+
+#include "util/ndi.h"
+
#include
+#include
+#include
+
namespace caspar { namespace newtek {
void init(core::module_dependencies dependencies)
@@ -34,8 +42,19 @@ void init(core::module_dependencies dependencies)
dependencies.consumer_registry->register_consumer_factory(L"iVGA Consumer", create_ivga_consumer);
dependencies.consumer_registry->register_preconfigured_consumer_factory(L"newtek-ivga",
create_preconfigured_ivga_consumer);
+
+ dependencies.consumer_registry->register_consumer_factory(L"NDI Consumer", create_ndi_consumer);
+ dependencies.consumer_registry->register_preconfigured_consumer_factory(L"ndi",
+ create_preconfigured_ndi_consumer);
+
+ dependencies.producer_registry->register_producer_factory(L"NDI Producer", create_ndi_producer);
+
+ bool autoload = caspar::env::properties().get(L"configuration.ndi.auto-load", false);
+ if (autoload)
+ ndi::load_library();
+
} catch (...) {
}
}
-}} // namespace caspar::newtek
\ No newline at end of file
+}} // namespace caspar::newtek
diff --git a/src/modules/newtek/producer/newtek_ndi_producer.cpp b/src/modules/newtek/producer/newtek_ndi_producer.cpp
new file mode 100644
index 0000000000..16bbc31482
--- /dev/null
+++ b/src/modules/newtek/producer/newtek_ndi_producer.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2018
+ *
+ * This file is part of CasparCG (www.casparcg.com).
+ *
+ * CasparCG 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.
+ *
+ * CasparCG 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 CasparCG. If not, see .
+ *
+ * Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
+ * based on work of Robert Nagy, ronag89@gmail.com and Jerzy JaĆkiewicz, jurek@tvp.pl
+ */
+
+#include "../StdAfx.h"
+
+#include "newtek_ndi_producer.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+extern "C" {
+#include
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#include "../util/ndi.h"
+
+namespace caspar { namespace newtek {
+
+struct newtek_ndi_producer : public core::frame_producer
+{
+ static int instances_;
+ const int instance_no_;
+ const std::wstring name_;
+ const bool low_bandwidth_;
+
+ spl::shared_ptr frame_factory_;
+ core::video_format_desc format_desc_;
+ NDIlib_v3* ndi_lib_;
+ NDIlib_framesync_instance_t ndi_framesync_;
+ NDIlib_recv_instance_t ndi_recv_instance_;
+ NDIlib_video_frame_v2_t ndi_video_frame_;
+ NDIlib_audio_frame_v2_t ndi_audio_frame_;
+ spl::shared_ptr graph_;
+ timer tick_timer_;
+ timer frame_timer_;
+
+ std::queue frames_;
+ mutable std::mutex frames_mutex_;
+ core::draw_frame last_frame_ = core::draw_frame::empty();
+ int frame_no_;
+ executor executor_;
+
+ int cadence_counter_;
+ int cadence_length_;
+
+ public:
+ explicit newtek_ndi_producer(spl::shared_ptr frame_factory,
+ core::video_format_desc format_desc,
+ std::wstring name,
+ bool low_bandwidth)
+ : format_desc_(format_desc)
+ , frame_factory_(frame_factory)
+ , name_(name)
+ , low_bandwidth_(low_bandwidth)
+ , executor_(print())
+ , instance_no_(instances_++)
+ , cadence_counter_(0)
+ , frame_no_(0)
+ {
+ if (!ndi::load_library()) {
+ ndi::not_installed();
+ }
+
+ graph_->set_text(print());
+ graph_->set_color("frame-time", diagnostics::color(0.5f, 1.0f, 0.2f));
+ graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
+ graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
+ diagnostics::register_graph(graph_);
+ executor_.set_capacity(1);
+ cadence_length_ = static_cast(format_desc_.audio_cadence.size());
+ initialize();
+ }
+
+ ~newtek_ndi_producer()
+ {
+ executor_.stop();
+ ndi::fs_destroy(ndi_framesync_);
+ ndi_lib_->NDIlib_recv_destroy(ndi_recv_instance_);
+ }
+
+ std::wstring print() const override
+ {
+ return L"ndi[" + boost::lexical_cast(instance_no_) + L"|" + name_ + L"]";
+ }
+
+ std::wstring name() const override { return L"ndi"; }
+
+ core::draw_frame receive_impl(int hints) override
+ {
+ graph_->set_value("tick-time", tick_timer_.elapsed() * format_desc_.fps * 0.5);
+ tick_timer_.restart();
+ core::draw_frame frame;
+ if (0 == frame_no_++) {
+ executor_.invoke([this]() { prepare_next_frame(); });
+ }
+ executor_.begin_invoke([this]() { prepare_next_frame(); });
+ {
+ std::lock_guard lock(frames_mutex_);
+ if (frames_.size() > 0) {
+ frame = frames_.front();
+ frames_.pop();
+ last_frame_ = frame;
+ } else {
+ frame = last_frame_;
+ }
+ return frame;
+ }
+ }
+ bool prepare_next_frame()
+ {
+ try {
+ frame_timer_.restart();
+ NDIlib_video_frame_v2_t video_frame;
+ NDIlib_audio_frame_v2_t audio_frame;
+ ndi::fs_capture_video(ndi_framesync_, &video_frame, NDIlib_frame_format_type_progressive);
+ ndi::fs_capture_audio(ndi_framesync_,
+ &audio_frame,
+ format_desc_.audio_sample_rate,
+ format_desc_.audio_channels,
+ format_desc_.audio_cadence[++cadence_counter_ %= cadence_length_]);
+ if (video_frame.p_data != nullptr) {
+ std::shared_ptr av_frame(av_frame_alloc(), [](AVFrame* frame) { av_frame_free(&frame); });
+ std::shared_ptr a_frame(av_frame_alloc(), [](AVFrame* frame) { av_frame_free(&frame); });
+ av_frame->data[0] = video_frame.p_data;
+ av_frame->linesize[0] = video_frame.line_stride_in_bytes;
+ switch (video_frame.FourCC) {
+ case NDIlib_FourCC_type_BGRA:
+ av_frame->format = AV_PIX_FMT_BGRA;
+ break;
+ case NDIlib_FourCC_type_BGRX:
+ av_frame->format = AV_PIX_FMT_BGRA;
+ break;
+ case NDIlib_FourCC_type_RGBA:
+ av_frame->format = AV_PIX_FMT_RGBA;
+ break;
+ case NDIlib_FourCC_type_RGBX:
+ av_frame->format = AV_PIX_FMT_RGBA;
+ break;
+ default: // should never happen because library handles the conversion for us
+ av_frame->format = AV_PIX_FMT_BGRA;
+ // cannot log here:
+ /*CASPAR_LOG(warning)
+ << print() << L" NDI frame format not supported (" << video_frame.FourCC << L").";*/
+ break;
+ }
+ av_frame->width = video_frame.xres;
+ av_frame->height = video_frame.yres;
+ av_frame->interlaced_frame =
+ video_frame.frame_format_type == NDIlib_frame_format_type_interleaved ? 1 : 0;
+ av_frame->top_field_first = av_frame->interlaced_frame;
+ NDIlib_audio_frame_interleaved_16s_t audio_frame_16s;
+ std::vector audio_data_32s;
+ audio_frame_16s.p_data = new short[audio_frame.no_samples * audio_frame.no_channels];
+ if (audio_frame.p_data != nullptr) {
+ audio_frame_16s.reference_level = 0;
+ ndi_lib_->NDIlib_util_audio_to_interleaved_16s_v2(&audio_frame, &audio_frame_16s);
+ a_frame->channels = audio_frame_16s.no_channels;
+ a_frame->sample_rate = audio_frame_16s.sample_rate;
+ a_frame->nb_samples = audio_frame_16s.no_samples;
+ audio_data_32s =
+ ndi::audio_16_to_32(audio_frame_16s.p_data, audio_frame.no_samples * audio_frame.no_channels);
+ a_frame->data[0] = reinterpret_cast(audio_data_32s.data());
+ }
+ auto mframe =
+ ffmpeg::make_frame(this, *(frame_factory_.get()), std::move(av_frame), std::move(a_frame));
+ auto dframe = core::draw_frame(std::move(mframe));
+ {
+ std::lock_guard lock(frames_mutex_);
+ frames_.push(dframe);
+ while (frames_.size() > 2) { // should never happen because frame sync takes care of it
+ frames_.pop();
+ graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
+ CASPAR_LOG(info) << print() << "Frame dropped";
+ }
+ }
+ ndi::fs_free_audio(ndi_framesync_, &audio_frame);
+ ndi::fs_free_video(ndi_framesync_, &video_frame);
+ delete[] audio_frame_16s.p_data;
+ }
+
+ graph_->set_value("frame-time", frame_timer_.elapsed() * format_desc_.fps * 0.5);
+ } catch (...) {
+ CASPAR_LOG_CURRENT_EXCEPTION();
+ return false;
+ }
+ return true;
+ }
+
+ // frame_producer
+
+ void initialize()
+ {
+ ndi_lib_ = ndi::load_library();
+ auto sources = ndi::get_current_sources();
+
+ NDIlib_recv_create_v3_t NDI_recv_create_desc;
+ NDI_recv_create_desc.allow_video_fields = false;
+ NDI_recv_create_desc.bandwidth = low_bandwidth_ ? NDIlib_recv_bandwidth_lowest : NDIlib_recv_bandwidth_highest;
+ NDI_recv_create_desc.color_format = NDIlib_recv_color_format_BGRX_BGRA;
+ std::string src_name = u8(name_);
+
+ auto found_source = sources.find(src_name);
+ if (found_source != sources.end()) {
+ NDI_recv_create_desc.source_to_connect_to = found_source->second;
+ } else {
+ NDI_recv_create_desc.source_to_connect_to.p_ndi_name = src_name.c_str();
+ }
+ NDI_recv_create_desc.p_ndi_recv_name = src_name.c_str();
+ ndi_recv_instance_ = ndi_lib_->NDIlib_recv_create_v3(&NDI_recv_create_desc);
+ ndi_framesync_ = ndi::fs_create(ndi_recv_instance_);
+ CASPAR_VERIFY(ndi_recv_instance_);
+ }
+
+ core::draw_frame last_frame() override
+ {
+ if (!last_frame_) {
+ last_frame_ = receive_impl(0);
+ }
+ return core::draw_frame::still(last_frame_);
+ }
+
+}; // namespace newtek
+
+int newtek_ndi_producer::instances_ = 0;
+spl::shared_ptr create_ndi_producer(const core::frame_producer_dependencies& dependencies,
+ const std::vector& params)
+{
+ if (params.size() < 2 || !boost::iequals(params.at(0), "ndi"))
+ return core::frame_producer::empty();
+
+ auto name = params.at(1);
+ bool low_bandwidth = contains_param(L"LOW_BANDWIDTH", params);
+
+ try {
+ auto producer = spl::make_shared(
+ dependencies.frame_factory, dependencies.format_desc, name, low_bandwidth);
+ return core::create_destroy_proxy(std::move(producer));
+ } catch (...) {
+ CASPAR_LOG_CURRENT_EXCEPTION();
+ }
+ return core::frame_producer::empty();
+}
+}} // namespace caspar::newtek
diff --git a/src/modules/newtek/producer/newtek_ndi_producer.h b/src/modules/newtek/producer/newtek_ndi_producer.h
new file mode 100644
index 0000000000..cf9741e450
--- /dev/null
+++ b/src/modules/newtek/producer/newtek_ndi_producer.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2018
+ *
+ * This file is part of CasparCG (www.casparcg.com).
+ *
+ * CasparCG 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.
+ *
+ * CasparCG 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 CasparCG. If not, see .
+ *
+ * Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
+ * based on work of Robert Nagy, ronag89@gmail.com
+ */
+
+#pragma once
+
+#include
+
+#include
+
+#include
+
+#include
+
+namespace caspar { namespace newtek {
+
+spl::shared_ptr create_ndi_producer(const core::frame_producer_dependencies& dependencies,
+ const std::vector& params);
+
+}} // namespace caspar::newtek
diff --git a/src/modules/newtek/util/ndi.cpp b/src/modules/newtek/util/ndi.cpp
new file mode 100644
index 0000000000..876c04cb3c
--- /dev/null
+++ b/src/modules/newtek/util/ndi.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2018
+ *
+ * This file is part of CasparCG (www.casparcg.com).
+ *
+ * CasparCG 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.
+ *
+ * CasparCG 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 CasparCG. If not, see .
+ *
+ * Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
+ */
+
+#include "../StdAfx.h"
+
+#include "ndi.h"
+
+#include
+#include
+
+#ifdef _WIN32
+#include
+#else
+#include
+#include
+#endif
+
+#include
+
+#include
+
+namespace caspar { namespace newtek { namespace ndi {
+
+NDIlib_framesync_instance_t (*fs_create)(NDIlib_recv_instance_t p_receiver) = nullptr;
+void (*fs_destroy)(NDIlib_framesync_instance_t p_instance) = nullptr;
+void (*fs_capture_audio)(NDIlib_framesync_instance_t p_instance,
+ NDIlib_audio_frame_v2_t* p_audio_data,
+ int sample_rate,
+ int no_channels,
+ int no_samples) = nullptr;
+
+void (*fs_free_audio)(NDIlib_framesync_instance_t p_instance, NDIlib_audio_frame_v2_t* p_audio_data) = nullptr;
+
+void (*fs_capture_video)(NDIlib_framesync_instance_t p_instance,
+ NDIlib_video_frame_v2_t* p_video_data,
+ NDIlib_frame_format_type_e field_type) = nullptr;
+
+void (*fs_free_video)(NDIlib_framesync_instance_t p_instance, NDIlib_video_frame_v2_t* p_video_data) = nullptr;
+
+const std::wstring& dll_name()
+{
+ static std::wstring name = u16(NDILIB_LIBRARY_NAME);
+
+ return name;
+}
+
+static std::mutex find_instance_mutex;
+static std::shared_ptr find_instance;
+
+NDIlib_v3* load_library()
+{
+ static NDIlib_v3* ndi_lib = nullptr;
+
+ if (ndi_lib)
+ return ndi_lib;
+ const char* runtime_dir = getenv(NDILIB_REDIST_FOLDER);
+
+ if (runtime_dir == NULL)
+ return nullptr;
+
+ auto dll_path = boost::filesystem::path(runtime_dir) / NDILIB_LIBRARY_NAME;
+
+#ifdef _WIN32
+ auto module = LoadLibrary(dll_path.c_str());
+
+ if (!module)
+ return nullptr;
+
+ static std::shared_ptr lib(module, FreeLibrary);
+
+ // wchar_t actualFilename[256];
+ // GetModuleFileNameW(module, actualFilename, sizeof(actualFilename));
+ CASPAR_LOG(info) << L"Loaded " << dll_path;
+
+ auto NDIlib_v3_load = GetProcAddress(module, "NDIlib_v3_load");
+ if (!NDIlib_v3_load)
+ return nullptr;
+
+#else
+ // Try to load the library
+ void* hNDILib = dlopen(dll_path.c_str(), RTLD_LOCAL | RTLD_LAZY);
+
+ // The main NDI entry point for dynamic loading if we got the library
+ const NDIlib_v3* (*NDIlib_v3_load)(void) = NULL;
+ if (hNDILib) {
+ CASPAR_LOG(info) << L"Loaded " << dll_path;
+ static std::shared_ptr lib(hNDILib, dlclose);
+ *((void**)&NDIlib_v3_load) = dlsym(hNDILib, "NDIlib_v3_load");
+ }
+ if (!NDIlib_v3_load)
+ return nullptr;
+
+#endif
+
+ ndi_lib = (NDIlib_v3*)(NDIlib_v3_load());
+
+ if (!ndi_lib->NDIlib_initialize()) {
+ not_initialized();
+ }
+
+#ifdef _WIN32
+ // these functions have to be loaded this way because they aren't in NDIlib_v3 struct
+ fs_create = reinterpret_cast(GetProcAddress(module, "NDIlib_framesync_create"));
+ fs_destroy = reinterpret_cast(GetProcAddress(module, "NDIlib_framesync_destroy"));
+ fs_capture_audio =
+ reinterpret_cast(GetProcAddress(module, "NDIlib_framesync_capture_audio"));
+ fs_free_audio = reinterpret_cast(GetProcAddress(module, "NDIlib_framesync_free_audio"));
+ fs_capture_video =
+ reinterpret_cast(GetProcAddress(module, "NDIlib_framesync_capture_video"));
+ fs_free_video = reinterpret_cast(GetProcAddress(module, "NDIlib_framesync_free_video"));
+
+#else
+ *((void**)&fs_create) = dlsym(hNDILib, "NDIlib_framesync_create");
+ *((void**)&fs_destroy) = dlsym(hNDILib, "NDIlib_framesync_destroy");
+ *((void**)&fs_capture_audio) = dlsym(hNDILib, "NDIlib_framesync_capture_audio");
+ *((void**)&fs_free_audio) = dlsym(hNDILib, "NDIlib_framesync_free_audio");
+ *((void**)&fs_capture_video) = dlsym(hNDILib, "NDIlib_framesync_capture_video");
+ *((void**)&fs_free_video) = dlsym(hNDILib, "NDIlib_framesync_free_video");
+
+#endif
+ find_instance.reset(new NDIlib_find_instance_t(ndi_lib->NDIlib_find_create_v2(nullptr)),
+ [](NDIlib_find_instance_t* p) { ndi_lib->NDIlib_find_destroy(*p); });
+ return ndi_lib;
+}
+
+std::map get_current_sources()
+{
+ auto sources_map = std::map();
+ uint32_t no_sources;
+ std::lock_guard guard(find_instance_mutex);
+ const NDIlib_source_t* sources =
+ load_library()->NDIlib_find_get_current_sources(*(find_instance.get()), &no_sources);
+ for (uint32_t i = 0; i < no_sources; i++) {
+ sources_map.emplace(std::string(sources[i].p_ndi_name), sources[i]);
+ }
+ return sources_map;
+}
+
+void not_installed()
+{
+ CASPAR_THROW_EXCEPTION(not_supported()
+ << msg_info(dll_name() + L" not available. Install NDI Redist version 3.7 or higher from " +
+ u16(NDILIB_REDIST_URL)));
+}
+
+void not_initialized()
+{
+ CASPAR_THROW_EXCEPTION(not_supported() << msg_info("Unable to initialize NDI on this system."));
+}
+
+std::vector audio_16_to_32(const short* audio_data, int size)
+{
+ auto output32 = std::vector();
+
+ output32.reserve(size);
+ for (int n = 0; n < size; ++n)
+ output32.push_back((audio_data[n] & 0xFFFFFFFF) << 16);
+
+ return output32;
+}
+
+std::vector audio_32_to_32f(const int* audio_data, int size)
+{
+ auto output32 = std::vector();
+
+ output32.reserve(size);
+ for (int n = 0; n < size; ++n)
+ output32.push_back((1.0f * audio_data[n]) / INT32_MAX);
+
+ return output32;
+}
+
+}}} // namespace caspar::newtek::ndi
diff --git a/src/modules/newtek/util/ndi.h b/src/modules/newtek/util/ndi.h
new file mode 100644
index 0000000000..bc03cd8194
--- /dev/null
+++ b/src/modules/newtek/util/ndi.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Sveriges Television AB http://casparcg.com/
+ *
+ * This file is part of CasparCG (www.casparcg.com).
+ *
+ * CasparCG 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.
+ *
+ * CasparCG 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 CasparCG. If not, see .
+ *
+ * Author: Krzysztof Zegzula, zegzulakrzysztof@gmail.com
+ */
+#pragma once
+
+#include "../interop/Processing.NDI.Lib.h"
+#include
+
+namespace caspar { namespace newtek { namespace ndi {
+extern NDIlib_framesync_instance_t (*fs_create)(NDIlib_recv_instance_t p_receiver);
+extern void (*fs_destroy)(NDIlib_framesync_instance_t p_instance);
+extern void (*fs_capture_audio)(NDIlib_framesync_instance_t p_instance,
+ NDIlib_audio_frame_v2_t* p_audio_data,
+ int sample_rate,
+ int no_channels,
+ int no_samples);
+
+extern void (*fs_free_audio)(NDIlib_framesync_instance_t p_instance, NDIlib_audio_frame_v2_t* p_audio_data);
+
+extern void (*fs_capture_video)(NDIlib_framesync_instance_t p_instance,
+ NDIlib_video_frame_v2_t* p_video_data,
+ NDIlib_frame_format_type_e field_type);
+
+extern void (*fs_free_video)(NDIlib_framesync_instance_t p_instance, NDIlib_video_frame_v2_t* p_video_data);
+
+const std::wstring& dll_name();
+NDIlib_v3* load_library();
+std::map get_current_sources();
+void not_initialized();
+void not_installed();
+
+std::vector audio_16_to_32(const short* audio_data, int size);
+std::vector audio_32_to_32f(const int* audio_data, int size);
+
+}}} // namespace caspar::newtek::ndi
diff --git a/src/shell/casparcg.config b/src/shell/casparcg.config
index 3a87bea5fb..3d54d02220 100644
--- a/src/shell/casparcg.config
+++ b/src/shell/casparcg.config
@@ -49,6 +49,9 @@
0 [0|1024-65535]
false [true|false]