11#include <promeki/config.h>
41struct NDIlib_send_instance_type;
42struct NDIlib_recv_instance_type;
44PROMEKI_NAMESPACE_BEGIN
124class NdiMediaIO :
public DedicatedThreadMediaIO {
125 PROMEKI_OBJECT(NdiMediaIO, DedicatedThreadMediaIO)
128 static inline const MediaIOStats::ID StatsFramesSent{
"NdiFramesSent"};
130 static inline const MediaIOStats::ID StatsAudioFramesSent{
"NdiAudioFramesSent"};
132 static inline const MediaIOStats::ID StatsBytesSent{
"NdiBytesSent"};
135 static inline const MediaIOStats::ID StatsFramesReceived{
"NdiFramesReceived"};
137 static inline const MediaIOStats::ID StatsAudioFramesReceived{
"NdiAudioFramesReceived"};
139 static inline const MediaIOStats::ID StatsMetadataReceived{
"NdiMetadataReceived"};
141 static inline const MediaIOStats::ID StatsDroppedReceives{
"NdiDroppedReceives"};
148 static inline const MediaIOStats::ID StatsAudioSilenceFilled{
"NdiAudioSilenceFilled"};
154 static inline const MediaIOStats::ID StatsAudioGapEvents{
"NdiAudioGapEvents"};
182 static MediaIOAllocator::Ptr makePinnedHostAllocator();
185 NdiMediaIO(ObjectBase *parent =
nullptr);
188 ~NdiMediaIO()
override;
199 int instanceID()
const {
return _instanceId; }
213 Error proposeInput(
const MediaDesc &offered, MediaDesc *preferred)
const override;
216 Error executeCmd(MediaIOCommandOpen &cmd)
override;
217 Error executeCmd(MediaIOCommandClose &cmd)
override;
218 Error executeCmd(MediaIOCommandRead &cmd)
override;
219 Error executeCmd(MediaIOCommandWrite &cmd)
override;
220 Error executeCmd(MediaIOCommandStats &cmd)
override;
250 Error executeCmd(MediaIOCommandSetClock &cmd)
override;
251 void cancelBlockingWork()
override;
254 Error openSink(
const MediaIO::Config &cfg,
const MediaDesc &mediaDesc);
257 Error openSource(
const MediaIO::Config &cfg);
261 Error sendVideo(
const UncompressedVideoPayload &vp);
262 Error sendAudio(
const PcmAudioPayload &ap);
302 void ingestNdiAudio(int64_t timestampTicks,
size_t samples,
size_t channels,
303 float rate,
const uint8_t *planarFloatData,
304 size_t channelStrideBytes);
306 friend struct NdiMediaIOTestAccess;
309 NDIlib_send_instance_type *_send =
nullptr;
310 NDIlib_recv_instance_type *_recv =
nullptr;
314 size_t _audioChannels = 0;
315 float _audioSampleRate = 0.0f;
321 bool _sendClockVideo =
false;
322 bool _sendClockAudio =
false;
323 bool _sinkMode =
false;
326 ImageDesc _imageDesc;
327 FrameRate _frameRate;
331 Atomic<int64_t> _framesSent{0};
332 Atomic<int64_t> _audioFramesSent{0};
333 Atomic<int64_t> _bytesSent{0};
343 BasicThread _captureThread;
345 Atomic<bool> _stopFlag{
false};
346 Atomic<bool> _readCancelled{
false};
351 static constexpr int VideoQueueDepth = 2;
352 Queue<UncompressedVideoPayload::Ptr> _videoQueue;
353 AudioBuffer _audioRing;
374 int64_t _audioNextSampleTicks = 0;
383 AudioMarkerList _audioMarkersSinceDrain;
389 Atomic<int64_t> _audioSilenceSamples{0};
392 Atomic<int64_t> _audioGapEvents{0};
393 Mutex _metadataMutex;
394 Metadata _pendingMetadata;
395 bool _hasPendingMetadata =
false;
400 int _captureTimeoutMs = 100;
401 int _bitDepthHint = 0;
404 Atomic<int64_t> _framesReceived{0};
405 Atomic<int64_t> _audioFramesReceived{0};
406 Atomic<int64_t> _metadataReceived{0};
407 Atomic<int64_t> _droppedReceives{0};
414 Clock::Ptr _sourceClock;
427 PacingGate _videoGate;
428 PacingGate _audioGate;
435class NdiFactory :
public MediaIOFactory {
437 NdiFactory() =
default;
439 String name()
const override {
return String(
"Ndi"); }
440 String displayName()
const override {
return String(
"NDI Stream"); }
441 String description()
const override {
442 return String(
"NDI (Network Device Interface) media transport "
443 "(uncompressed video + audio + metadata over IP)");
446 bool canBeSource()
const override {
return true; }
447 bool canBeSink()
const override {
return true; }
449 StringList schemes()
const override {
return {String(
"ndi")}; }
450 bool canHandlePath(
const String &path)
const override;
451 StringList enumerate()
const override;
452 Error urlToConfig(
const Url &url, Config *outConfig)
const override;
454 Config::SpecMap configSpecs()
const override;
456 MediaIO *create(
const Config &config, ObjectBase *parent =
nullptr)
const override;