3535
3636#include < common/assert.h>
3737#include < common/diagnostics/graph.h>
38+ #include < common/env.h>
3839#include < common/except.h>
3940#include < common/executor.h>
4041#include < common/future.h>
@@ -57,10 +58,13 @@ struct newtek_ndi_consumer : public core::frame_consumer
5758 const int instance_no_;
5859 const std::wstring name_;
5960 const bool allow_fields_;
61+ const std::string discovery_server_url_;
62+ const bool use_advertiser_;
63+ const bool allow_monitoring_;
6064
6165 core::video_format_desc format_desc_;
6266 int channel_index_;
63- NDIlib_v5 * ndi_lib_;
67+ NDIlib_v6 * ndi_lib_;
6468 NDIlib_video_frame_v2_t ndi_video_frame_;
6569 NDIlib_audio_frame_interleaved_32s_t ndi_audio_frame_;
6670 std::shared_ptr<uint8_t > field_data_;
@@ -78,13 +82,17 @@ struct newtek_ndi_consumer : public core::frame_consumer
7882 executor executor_;
7983
8084 std::unique_ptr<NDIlib_send_instance_t, std::function<void (NDIlib_send_instance_t*)>> ndi_send_instance_;
85+ std::unique_ptr<NDIlib_send_advertiser_instance_t, std::function<void (NDIlib_send_advertiser_instance_t*)>> ndi_advertiser_instance_;
8186
8287 public:
83- newtek_ndi_consumer (std::wstring name, bool allow_fields)
88+ newtek_ndi_consumer (std::wstring name, bool allow_fields, std::string discovery_server_url = " " , bool use_advertiser = false , bool allow_monitoring = true )
8489 : name_(!name.empty() ? name : default_ndi_name())
8590 , instance_no_(instances_++)
8691 , frame_no_(0 )
8792 , allow_fields_(allow_fields)
93+ , discovery_server_url_(discovery_server_url)
94+ , use_advertiser_(use_advertiser)
95+ , allow_monitoring_(allow_monitoring)
8896 , channel_index_(0 )
8997 , executor_(L" ndi_consumer[" + std::to_wstring(instance_no_) + L" ]" )
9098 {
@@ -114,6 +122,9 @@ struct newtek_ndi_consumer : public core::frame_consumer
114122 format_desc_ = format_desc;
115123 channel_index_ = channel_info.index ;
116124
125+ // Make sure to stop the advertiser before recreating the sender
126+ ndi_advertiser_instance_.reset ();
127+
117128 NDIlib_send_create_t NDI_send_create_desc;
118129
119130 auto tmp_name = u8 (name_);
@@ -125,6 +136,51 @@ struct newtek_ndi_consumer : public core::frame_consumer
125136 ndi_send_instance_ = {new NDIlib_send_instance_t (ndi_lib_->send_create (&NDI_send_create_desc)),
126137 [this ](auto p) { this ->ndi_lib_ ->send_destroy (*p); }};
127138
139+ // Create and configure NDI advertiser if enabled
140+ if (use_advertiser_) {
141+ if (!ndi_lib_->send_advertiser_create ) {
142+ CASPAR_LOG (warning) << L" NDI advertiser requested but not supported by this NDI SDK version (requires NDI 5.5+)" ;
143+ } else {
144+ // Use constructor for proper initialization
145+ NDIlib_send_advertiser_create_t advertiser_create_desc (
146+ discovery_server_url_.empty () ? nullptr : discovery_server_url_.c_str ()
147+ );
148+
149+ auto advertiser_instance = ndi_lib_->send_advertiser_create (&advertiser_create_desc);
150+
151+ if (!advertiser_instance) {
152+ CASPAR_LOG (warning) << L" Failed to create NDI advertiser for sender '" << name_ << L" '"
153+ << (discovery_server_url_.empty ()
154+ ? L" (using default discovery)"
155+ : L" with server: " + u16 (discovery_server_url_));
156+ } else {
157+ ndi_advertiser_instance_ = {
158+ new NDIlib_send_advertiser_instance_t (advertiser_instance),
159+ [this ](auto p) {
160+ if (p && *p && this ->ndi_lib_ ->send_advertiser_del_sender && this ->ndi_lib_ ->send_advertiser_destroy ) {
161+ // Remove sender before destroying advertiser
162+ this ->ndi_lib_ ->send_advertiser_del_sender (*p, *ndi_send_instance_);
163+ this ->ndi_lib_ ->send_advertiser_destroy (*p);
164+ }
165+ }
166+ };
167+
168+ bool added = ndi_lib_->send_advertiser_add_sender (
169+ *ndi_advertiser_instance_,
170+ *ndi_send_instance_,
171+ allow_monitoring_
172+ );
173+
174+ if (added) {
175+ CASPAR_LOG (info) << L" NDI sender '" << name_ << L" ' registered with discovery server"
176+ << (discovery_server_url_.empty () ? L" " : L" at " + u16 (discovery_server_url_));
177+ } else {
178+ CASPAR_LOG (warning) << L" Failed to register NDI sender '" << name_ << L" ' with advertiser (sender may already be registered)" ;
179+ }
180+ }
181+ }
182+ }
183+
128184 ndi_video_frame_.xres = format_desc.width ;
129185 ndi_video_frame_.yres = format_desc.height ;
130186 ndi_video_frame_.frame_rate_N = format_desc.framerate .numerator () * format_desc.field_count ;
@@ -250,8 +306,11 @@ struct newtek_ndi_consumer : public core::frame_consumer
250306 core::monitor::state state () const override
251307 {
252308 core::monitor::state state;
253- state[" ndi/name" ] = name_;
254- state[" ndi/allow_fields" ] = allow_fields_;
309+ state[" ndi/name" ] = name_;
310+ state[" ndi/allow_fields" ] = allow_fields_;
311+ state[" ndi/use_advertiser" ] = use_advertiser_;
312+ state[" ndi/allow_monitoring" ] = allow_monitoring_;
313+ state[" ndi/discovery_server_url" ] = discovery_server_url_;
255314 return state;
256315 }
257316};
@@ -270,9 +329,16 @@ create_ndi_consumer(const std::vector<std::wstring>& par
270329 if (channel_info.depth != common::bit_depth::bit8)
271330 CASPAR_THROW_EXCEPTION (caspar_exception () << msg_info (" Newtek NDI consumer only supports 8-bit color depth." ));
272331
273- std::wstring name = get_param (L" NAME" , params, L" " );
274- bool allow_fields = contains_param (L" ALLOW_FIELDS" , params);
275- return spl::make_shared<newtek_ndi_consumer>(name, allow_fields);
332+ std::wstring name = get_param (L" NAME" , params, L" " );
333+ bool allow_fields = contains_param (L" ALLOW_FIELDS" , params);
334+ bool use_advertiser = contains_param (L" USE_ADVERTISER" , params);
335+ bool allow_monitoring = get_param (L" ALLOW_MONITORING" , params, true );
336+ std::wstring discovery_server_url_w = get_param (L" DISCOVERY_SERVER" , params, L" " );
337+ if (discovery_server_url_w.empty ())
338+ discovery_server_url_w = env::properties ().get (L" configuration.ndi.discovery-server" , L" " );
339+ std::string discovery_server_url = ndi::apply_default_discovery_port (u8 (discovery_server_url_w));
340+
341+ return spl::make_shared<newtek_ndi_consumer>(name, allow_fields, discovery_server_url, use_advertiser, allow_monitoring);
276342}
277343
278344spl::shared_ptr<core::frame_consumer>
@@ -281,13 +347,19 @@ create_preconfigured_ndi_consumer(const boost::property_tree::wptree&
281347 const std::vector<spl::shared_ptr<core::video_channel>>& channels,
282348 const core::channel_info& channel_info)
283349{
284- auto name = ptree.get (L" name" , L" " );
285- bool allow_fields = ptree.get (L" allow-fields" , false );
350+ auto name = ptree.get (L" name" , L" " );
351+ bool allow_fields = ptree.get (L" allow-fields" , false );
352+ bool use_advertiser = ptree.get (L" use-advertiser" , false );
353+ bool allow_monitoring = ptree.get (L" allow-monitoring" , true );
354+ std::wstring discovery_server_url_w = ptree.get (L" discovery-server" , L" " );
355+ if (discovery_server_url_w.empty ())
356+ discovery_server_url_w = env::properties ().get (L" configuration.ndi.discovery-server" , L" " );
357+ std::string discovery_server_url = ndi::apply_default_discovery_port (u8 (discovery_server_url_w));
286358
287359 if (channel_info.depth != common::bit_depth::bit8)
288360 CASPAR_THROW_EXCEPTION (caspar_exception () << msg_info (" Newtek NDI consumer only supports 8-bit color depth." ));
289361
290- return spl::make_shared<newtek_ndi_consumer>(name, allow_fields);
362+ return spl::make_shared<newtek_ndi_consumer>(name, allow_fields, discovery_server_url, use_advertiser, allow_monitoring );
291363}
292364
293365}} // namespace caspar::newtek
0 commit comments