UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
decklink.cpp
Go to the documentation of this file.
1
14/*
15 * Copyright (c) 2010-2024 CESNET, z. s. p. o.
16 * All rights reserved.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, is permitted provided that the following conditions
20 * are met:
21 *
22 * 1. Redistributions of source code must retain the above copyright
23 * notice, this list of conditions and the following disclaimer.
24 *
25 * 2. Redistributions in binary form must reproduce the above copyright
26 * notice, this list of conditions and the following disclaimer in the
27 * documentation and/or other materials provided with the distribution.
28 *
29 * 3. Neither the name of CESNET nor the names of its contributors may be
30 * used to endorse or promote products derived from this software without
31 * specific prior written permission.
32 *
33 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
34 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
35 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
36 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
37 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
38 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
40 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
42 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
43 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
44 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 */
46
47#include <DeckLinkAPIVersion.h>
48#include <algorithm>
49#include <array>
50#include <atomic> // for atomic, __atomic_base
51#include <cctype> // for isdigit, toupper
52#include <chrono>
53#include <cinttypes>
54#include <cmath> // for ceil, fabs
55#include <cstdint>
56#include <cstdio> // for printf, snprintf
57#include <cstdlib> // for abort, atoi, realloc
58#include <cstring> // for strlen, strchr, NULL
59#include <exception> // for exception
60#include <iostream> // for cout
61#include <iterator> // for begin, end
62#include <map> // for map, operator!=, ope...
63#include <memory> // for unique_ptr
64#include <mutex>
65#include <queue>
66#include <sstream>
67#include <stdexcept> // for invalid_argument
68#include <string>
69#include <utility> // for pair
70#include <vector>
71
72#define DECKLINK_MAGIC to_fourcc('v', 'd', 'D', 'L')
73#define MOD_NAME "[DeckLink display] "
74
75#include "audio/types.h"
76#include "blackmagic_common.hpp"
77#include "compat/htonl.h"
78#include "compat/strings.h"
79#include "debug.h"
80#include "host.h"
81#include "lib_common.h"
82#include "module.h"
83#include "tv.h"
84#include "ug_runtime_error.hpp"
85#include "utils/macros.h"
86#include "utils/math.h"
87#include "utils/misc.h"
88#include "utils/string.h" // is_prefix_of
89#include "video.h"
90#include "video_display.h"
92
93#ifndef _WIN32
94#define STDMETHODCALLTYPE
95#endif
96
97#define RELEASE_IF_NOT_NULL(x) if ((x) != nullptr) { (x)->Release(); (x) = nullptr; }
98
99static void print_output_modes(IDeckLink *deckLink, const char *query_prop_fcc);
100static void display_decklink_done(void *state);
101static bool display_decklink_reconfigure(void *state, struct video_desc desc);
102
103// performs command, if failed, displays error and jumps to error label
104#define EXIT_IF_FAILED(cmd, name) \
105 do {\
106 const HRESULT result = cmd;\
107 if (FAILED(result)) {;\
108 LOG(LOG_LEVEL_ERROR) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
109 return false;\
110 }\
111 } while (0)
112
113// similar as above, but only displays warning
114#define CALL_AND_CHECK(cmd, name) \
115 do {\
116 const HRESULT result = cmd;\
117 if (FAILED(result)) {;\
118 LOG(LOG_LEVEL_WARNING) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
119 }\
120 } while (0)
121
122using hrc = std::chrono::high_resolution_clock;
123using namespace std::string_literals;
124using std::chrono::duration_cast;
125using std::chrono::seconds;
126using std::array;
127using std::atomic;
128using std::atomic_bool;
129using std::cout;
130using std::copy;
131using std::exception;
132using std::get;
133using std::invalid_argument;
134using std::lock_guard;
135using std::map;
136using std::mutex;
137using std::ostringstream;
138using std::out_of_range;
139using std::queue;
140using std::stod;
141using std::stoi;
142using std::string;
143using std::unique_lock;
144using std::unique_ptr;
145using std::vector;
146
147struct state_decklink;
148
149namespace {
150class DeckLinkFrame;
151
153class PlaybackDelegate : public IDeckLinkVideoOutputCallback // , public IDeckLinkAudioOutputCallback
154{
155 private:
156 struct audio_vals {
157 int64_t saved_sync_ts = INT64_MIN;
158 int64_t last_sync_ts = INT64_MIN;
159 };
160
161 enum audio_sync_val : int64_t {
162 deinit = INT64_MIN,
163 resync = INT64_MIN + 1,
164 };
165
166 hrc::time_point t0 = hrc::now();
167 uint64_t frames_dropped = 0;
168 uint64_t frames_flushed = 0;
169 uint64_t frames_late = 0;
170
171 IDeckLinkOutput *m_deckLinkOutput{};
172 mutex schedLock;
173 queue<DeckLinkFrame *> schedFrames{};
174 DeckLinkFrame *lastSchedFrame{};
175 long schedSeq{};
176 atomic<int64_t> m_audio_sync_ts = audio_sync_val::deinit;
177 struct audio_vals m_adata;
178
179 unsigned m_min_sched_frames = DEFAULT_MIN_SCHED_FRAMES;
180 unsigned m_max_sched_frames = DEFAULT_MAX_SCHED_FRAMES;
181 BMDTimeValue frameRateDuration{};
182 BMDTimeScale frameRateScale{};
183
184 public:
185 enum {
186 SCHED_RANGE = 2,
187 DEFAULT_MIN_SCHED_FRAMES = 4,
188 DEFAULT_MAX_SCHED_FRAMES =
189 DEFAULT_MIN_SCHED_FRAMES + SCHED_RANGE,
190 MAX_UNPROC_QUEUE_SIZE = 10,
191 };
192
193 void SetDecklinkOutput(IDeckLinkOutput *ido) { m_deckLinkOutput = ido; }
194 void SetFrameTimes(BMDTimeValue fd, BMDTimeScale fs)
195 {
196 frameRateDuration = fd;
197 frameRateScale = fs;
198 }
199 void SetSynchronized(const char *cfg);
200 bool Preroll(struct state_decklink *s);
201 void Reset();
202 void ResetAudio() { m_audio_sync_ts = audio_sync_val::deinit; }
203
204 virtual ~PlaybackDelegate() {
205 Reset();
206 };
207 void PrintStats();
208
209 // IUnknown needs only a dummy implementation
210 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID /*iid*/,
211 LPVOID * /*ppv*/) override
212 {
213 return E_NOINTERFACE;
214 }
215 ULONG STDMETHODCALLTYPE AddRef() override { return 1; }
216 ULONG STDMETHODCALLTYPE Release() override { return 1; }
217
218 bool EnqueueFrame(DeckLinkFrame *deckLinkFrame);
219 void ScheduleNextFrame();
220 void ScheduleAudio(const struct audio_frame *frame, uint32_t *samples,
221 bool underflow);
222
223 HRESULT STDMETHODCALLTYPE
224 ScheduledFrameCompleted(IDeckLinkVideoFrame *completedFrame,
225 BMDOutputFrameCompletionResult result) override
226 {
227 if (result == bmdOutputFrameDisplayedLate){
228 frames_late += 1;
229 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME "Late frame (total: " << frames_late << ")\n";
230 } else if (result == bmdOutputFrameDropped){
231 frames_dropped += 1;
232 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Dropped frame (total: " << frames_dropped << ")\n";
233 } else if (result == bmdOutputFrameFlushed){
234 frames_flushed += 1;
235 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Flushed frame (total: " << frames_flushed << ")\n";
236 }
237
238 if (log_level >= LOG_LEVEL_DEBUG) {
239 IDeckLinkTimecode *timecode = NULL;
240 if (completedFrame->GetTimecode ((BMDTimecodeFormat) 0, &timecode) == S_OK) {
241 BMD_STR timecode_str;
242 if (timecode && timecode->GetString(&timecode_str) == S_OK) {
244 << "Frame "
246 timecode_str)
247 << " output at "
248 << (double) get_time_in_ns() /
250 << '\n';
251 release_bmd_api_str(timecode_str);
252 }
253 }
254 }
255
256 ScheduleNextFrame();
257
258 completedFrame->Release();
259 return S_OK;
260 }
261
262 HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped() override
263 {
264 return S_OK;
265 }
266 // virtual HRESULT RenderAudioSamples (bool preroll);
267};
268
269void PlaybackDelegate::PrintStats()
270{
271 auto now = hrc::now();
272 if (duration_cast<seconds>(now - t0).count() >= 5) {
274 << MOD_NAME << frames_late << " frames late, "
275 << frames_dropped << " dropped, " << frames_flushed
276 << " flushed cumulative\n";
277 t0 = now;
278 }
279}
280
281struct buffer_pool_t {
282 queue<DeckLinkFrame *> frame_queue;
283 mutex lock;
284};
285
286class DeckLinkTimecode : public IDeckLinkTimecode{
287 BMDTimecodeBCD timecode;
288 public:
289 DeckLinkTimecode() : timecode(0) {}
290 virtual ~DeckLinkTimecode() = default;
291 /* IDeckLinkTimecode */
292 virtual BMDTimecodeBCD STDMETHODCALLTYPE GetBCD (void) { return timecode; }
293 virtual HRESULT STDMETHODCALLTYPE GetComponents (/* out */ uint8_t *hours, /* out */ uint8_t *minutes, /* out */ uint8_t *seconds, /* out */ uint8_t *frames) {
294 *frames = (timecode & 0xf) + ((timecode & 0xf0) >> 4) * 10;
295 *seconds = ((timecode & 0xf00) >> 8) + ((timecode & 0xf000) >> 12) * 10;
296 *minutes = ((timecode & 0xf0000) >> 16) + ((timecode & 0xf00000) >> 20) * 10;
297 *hours = ((timecode & 0xf000000) >> 24) + ((timecode & 0xf0000000) >> 28) * 10;
298 return S_OK;
299 }
300 virtual HRESULT STDMETHODCALLTYPE GetString (/* out */ BMD_STR *timecode) {
301 uint8_t hours, minutes, seconds, frames;
302 GetComponents(&hours, &minutes, &seconds, &frames);
303 char timecode_c[16];
304 assert(hours <= 99 && minutes <= 59 && seconds <= 60 && frames <= 99);
305 snprintf(timecode_c, sizeof timecode_c, "%02" PRIu8 ":%02" PRIu8 ":%02" PRIu8 ":%02" PRIu8, hours, minutes, seconds, frames);
306 *timecode = get_bmd_api_str_from_cstr(timecode_c);
307 return *timecode ? S_OK : E_FAIL;
308 }
309 virtual BMDTimecodeFlags STDMETHODCALLTYPE GetFlags (void) { return bmdTimecodeFlagDefault; }
310 virtual HRESULT STDMETHODCALLTYPE GetTimecodeUserBits (/* out */ BMDTimecodeUserBits *userBits) { if (!userBits) return E_POINTER; else return S_OK; }
311
312 /* IUnknown */
313 virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID , LPVOID *) {return E_NOINTERFACE;}
314 virtual ULONG STDMETHODCALLTYPE AddRef () {return 1;}
315 virtual ULONG STDMETHODCALLTYPE Release () {return 1;}
316
317 void STDMETHODCALLTYPE SetBCD(BMDTimecodeBCD timecode) { this->timecode = timecode; }
318};
319
320struct ChromaticityCoordinates
321{
322 double RedX;
323 double RedY;
324 double GreenX;
325 double GreenY;
326 double BlueX;
327 double BlueY;
328 double WhiteX;
329 double WhiteY;
330};
331
332constexpr ChromaticityCoordinates kDefaultRec2020Colorimetrics = { 0.708, 0.292, 0.170, 0.797, 0.131, 0.046, 0.3127, 0.3290 };
333constexpr double kDefaultMaxDisplayMasteringLuminance = 1000.0;
334constexpr double kDefaultMinDisplayMasteringLuminance = 0.0001;
335constexpr double kDefaultMaxCLL = 1000.0;
336constexpr double kDefaultMaxFALL = 50.0;
337enum class HDR_EOTF { NONE = -1, SDR = 0, HDR = 1, PQ = 2, HLG = 3 };
338
339struct HDRMetadata
340{
341 int64_t EOTF{static_cast<int64_t>(HDR_EOTF::NONE)};
342 ChromaticityCoordinates referencePrimaries{kDefaultRec2020Colorimetrics};
343 double maxDisplayMasteringLuminance{kDefaultMaxDisplayMasteringLuminance};
344 double minDisplayMasteringLuminance{kDefaultMinDisplayMasteringLuminance};
345 double maxCLL{kDefaultMaxCLL};
346 double maxFALL{kDefaultMaxFALL};
347
348 void Init(const string & fmt);
349};
350
351class DeckLinkFrame : public IDeckLinkMutableVideoFrame, public IDeckLinkVideoFrameMetadataExtensions
352{
353 long width;
354 long height;
355 long rawBytes;
356 BMDPixelFormat pixelFormat;
357 unique_ptr<char []> data;
358
359 IDeckLinkTimecode *timecode;
360
361 atomic<ULONG> ref = 1;
362
363 buffer_pool_t &buffer_pool;
364 struct HDRMetadata m_metadata;
365 protected:
366 DeckLinkFrame(long w, long h, long rb, BMDPixelFormat pf, buffer_pool_t & bp, HDRMetadata const & hdr_metadata);
367
368 public:
369 virtual ~DeckLinkFrame();
370 static DeckLinkFrame *Create(long width, long height, long rawBytes, BMDPixelFormat pixelFormat, buffer_pool_t & buffer_pool, HDRMetadata const & hdr_metadata);
371
372 /* IUnknown */
373 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**) override;
374 ULONG STDMETHODCALLTYPE AddRef() override;
375 ULONG STDMETHODCALLTYPE Release() override;
376
377 /* IDeckLinkVideoFrame */
378 long STDMETHODCALLTYPE GetWidth (void) override;
379 long STDMETHODCALLTYPE GetHeight (void) override;
380 long STDMETHODCALLTYPE GetRowBytes (void) override;
381 BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat (void) override;
382 BMDFrameFlags STDMETHODCALLTYPE GetFlags (void) override;
383 HRESULT STDMETHODCALLTYPE GetBytes (/* out */ void **buffer) override;
384 HRESULT STDMETHODCALLTYPE GetTimecode (/* in */ BMDTimecodeFormat format, /* out */ IDeckLinkTimecode **timecode) override;
385 HRESULT STDMETHODCALLTYPE GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **ancillary) override;
386
387 /* IDeckLinkMutableVideoFrame */
388 HRESULT STDMETHODCALLTYPE SetFlags(BMDFrameFlags) override;
389 HRESULT STDMETHODCALLTYPE SetTimecode(BMDTimecodeFormat, IDeckLinkTimecode*) override;
390 HRESULT STDMETHODCALLTYPE SetTimecodeFromComponents(BMDTimecodeFormat, uint8_t, uint8_t, uint8_t, uint8_t, BMDTimecodeFlags) override;
391 HRESULT STDMETHODCALLTYPE SetAncillaryData(IDeckLinkVideoFrameAncillary*) override;
392 HRESULT STDMETHODCALLTYPE SetTimecodeUserBits(BMDTimecodeFormat, BMDTimecodeUserBits) override;
393
394 // IDeckLinkVideoFrameMetadataExtensions interface
395 HRESULT STDMETHODCALLTYPE GetInt(BMDDeckLinkFrameMetadataID metadataID, int64_t* value) override;
396 HRESULT STDMETHODCALLTYPE GetFloat(BMDDeckLinkFrameMetadataID metadataID, double* value) override;
397 HRESULT STDMETHODCALLTYPE GetFlag(BMDDeckLinkFrameMetadataID metadataID, BMD_BOOL* value) override;
398 HRESULT STDMETHODCALLTYPE GetString(BMDDeckLinkFrameMetadataID metadataID, BMD_STR * value) override;
399 HRESULT STDMETHODCALLTYPE GetBytes(BMDDeckLinkFrameMetadataID metadataID, void* buffer, uint32_t* bufferSize) override;
400
401 int64_t timestamp = INT64_MIN;
402};
403
404class DeckLink3DFrame : public DeckLinkFrame, public IDeckLinkVideoFrame3DExtensions
405{
406 private:
407 using DeckLinkFrame::DeckLinkFrame;
408 DeckLink3DFrame(long w, long h, long rb, BMDPixelFormat pf, buffer_pool_t & buffer_pool, HDRMetadata const & hdr_metadata);
409 unique_ptr<DeckLinkFrame> rightEye; // rightEye ref count is always >= 1 therefore deleted by owner (this class)
410
411 public:
412 ~DeckLink3DFrame();
413 static DeckLink3DFrame *Create(long width, long height, long rawBytes, BMDPixelFormat pixelFormat, buffer_pool_t & buffer_pool, HDRMetadata const & hdr_metadata);
414
415 /* IUnknown */
416 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**) override;
417 ULONG STDMETHODCALLTYPE AddRef() override;
418 ULONG STDMETHODCALLTYPE Release() override;
419
420 /* IDeckLinkVideoFrame3DExtensions */
421 BMDVideo3DPackingFormat STDMETHODCALLTYPE Get3DPackingFormat() override;
422 HRESULT STDMETHODCALLTYPE GetFrameForRightEye(IDeckLinkVideoFrame**) override;
423};
424
425void PlaybackDelegate::Reset()
426{
427 RELEASE_IF_NOT_NULL(lastSchedFrame);
428 while (!schedFrames.empty()) {
429 schedFrames.front()->Release();
430 schedFrames.pop();
431 }
432 schedSeq = 0;
433}
434
435bool PlaybackDelegate::EnqueueFrame(DeckLinkFrame *deckLinkFrame)
436{
437 const unique_lock<mutex> lk(schedLock);
438 const unsigned buffered = schedFrames.size();
439 if (buffered < MAX_UNPROC_QUEUE_SIZE) {
440 schedFrames.push(deckLinkFrame);
441 return true;
442 }
443
444 deckLinkFrame->Release();
446 << MOD_NAME "Queue overflow, buffered: " << buffered
447 << ". This should not happen!\n";
448 m_audio_sync_ts = audio_sync_val::resync;
449 return false;
450}
451
452void PlaybackDelegate::ScheduleNextFrame()
453{
454 uint32_t i = 0;
455 m_deckLinkOutput->GetBufferedVideoFrameCount(&i);
456 LOG(LOG_LEVEL_DEBUG) << MOD_NAME << __func__ << " - " << i << " frames buffered\n";
457
458 const unique_lock<mutex> lk(schedLock);
459 while (!schedFrames.empty()) {
460 DeckLinkFrame *f = schedFrames.front();
461 schedFrames.pop();
462 if (++i > m_max_sched_frames) {
463 i -= 1;
465 << MOD_NAME "Dismissed frame, buffered: " << i
466 << "\n";
467 f->Release();
468 continue;
469 }
470 RELEASE_IF_NOT_NULL(lastSchedFrame);
471 lastSchedFrame = f;
472 lastSchedFrame->AddRef();
473 if (m_audio_sync_ts <= audio_sync_val::resync &&
474 f->timestamp != INT64_MIN) {
475 m_audio_sync_ts =
476 (uint32_t) (f->timestamp - frameRateDuration * schedSeq *
477 90000 / frameRateScale);
478 }
480 << MOD_NAME
481 << "video streamTime: " << schedSeq * frameRateDuration
482 << "; scale: " << frameRateScale << "\n";
483 m_deckLinkOutput->ScheduleVideoFrame(
486 schedSeq += 1;
487 }
488
489 if (i >= m_min_sched_frames || lastSchedFrame == nullptr) {
490 return;
491 }
492 for (; i < (m_min_sched_frames + m_max_sched_frames + 1) / 2; ++i) {
493 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Missing frame\n";
494 m_audio_sync_ts = audio_sync_val::resync;
495 m_deckLinkOutput->ScheduleVideoFrame(
496 lastSchedFrame, schedSeq * frameRateDuration,
498 schedSeq += 1;
499 }
500}
501} // end of unnamed namespace
502
503struct state_decklink {
504 uint32_t magic = DECKLINK_MAGIC;
505 bool com_initialized = false;
506 PlaybackDelegate delegate;
507 IDeckLink *deckLink;
508 IDeckLinkOutput *deckLinkOutput;
509 IDeckLinkConfiguration *deckLinkConfiguration;
510 IDeckLinkProfileAttributes *deckLinkAttributes;
511
512 DeckLinkTimecode *timecode{};
514
517 2, 48000, 2, AC_PCM
518 };
519
520 bool stereo = false;
521 bool initialized = false;
522 bool emit_timecode = false;
523 bool play_audio = false;
525
526 BMDPixelFormat pixelFormat{};
527
530 map<BMDDeckLinkConfigurationID, bmd_option> device_options = {
531 { bmdDeckLinkConfigOutput1080pAsPsF, bmd_option{false, false}},
532 { bmdDeckLinkConfigFieldFlickerRemoval, bmd_option{false, false}},
533 { bmdDeckLinkConfigLowLatencyVideoOutput, bmd_option{true, false}}
534 };
535 HDRMetadata requested_hdr_mode{};
536
537 buffer_pool_t buffer_pool;
538
539 bool low_latency = true;
540
542 atomic_bool audio_reconfigure = false;
544
546 };
547
549static void
550show_help(bool full, const char *query_prop_fcc = nullptr)
551 {
552 int numDevices = 0;
553
554 col() << "DeckLink display options:\n";
555 col() << SBOLD(SRED("\t-d decklink")
556 << "[:d[evice]=<device>][:Level{A|B}][:3D][:half-"
557 "duplex][:HDR[=<t>][:drift_fix]]\n");
558 col() << SBOLD(SRED("\t-d decklink") << ":[full]help") << " | "
559 << SBOLD(SRED("-d decklink") << ":query=<FourCC>"
560 "\n");
561 col() << "\nOptions:\n";
562 if (!full) {
563 col() << SBOLD("\tfullhelp") << "\tdisplay additional options and more details\n";
564 }
565 col() << SBOLD("\tdevice") << "\t\tindex or name of output device\n";
566 col() << SBOLD("\tLevelA/LevelB") << "\tspecifies 3G-SDI output level\n";
567 col() << SBOLD("\t3D") << "\t\t3D stream will be received (see also HDMI3DPacking option)\n";
568 col() << SBOLD("\thalf-duplex | full-duplex | simplex")
569 << "\tset a profile that allows maximal number of simultaneous "
570 "IOs / set device to better compatibility (3D, dual-link) / "
571 "use all connectors as single input\n";
572 col() << SBOLD("\tHDR[=HDR|PQ|HLG|<int>|help]") << " - enable HDR metadata (optionally specifying EOTF, int 0-7 as per CEA 861.), help for extended help\n";
573 col() << SBOLD("\tdrift_fix") << " activates a time drift fix for the DeckLink cards with resampler (experimental)\n";
574
575 col() << SBOLD("\t<option_FourCC>=<value>")
576 << "\tarbitrary BMD option (given a FourCC) and corresponding "
577 "value, i.a.:\n";
578 col()
579 << SBOLD("\t\taacl[=no]")
580 << "\tset maximum analog audio attenuation (consumer line level)\n";
581 col() << SBOLD("\t\tvoio=blac|lafa")
582 << "\tdisplay either black or last frame when idle\n";
583
584 if (!full) {
585 col() << SBOLD("\tconversion") << "\toutput size conversion, use '-d decklink:fullhelp' for list of conversions\n";
586 col() << "\n\t(other options available, use \"" << SBOLD("fullhelp") << "\" to see complete list of options)\n";
587 } else {
588 col() << SBOLD("\tsingle-link/dual-link/quad-link") << "\tspecifies if the video output will be in a single-link (HD/3G/6G/12G), dual-link HD-SDI mode or quad-link HD/3G/6G/12G\n";
589 col() << SBOLD("\ttimecode") << "\temit timecode\n";
590 col() << SBOLD("\t[no-]quad-square") << " set Quad-link SDI is output in Square Division Quad Split mode\n";
591 col() << SBOLD("\tsynchronized[=m[,M]]")
592 << " use regular scheduled mode for synchrized output"
593 "\n\t\t(m - minimum "
594 "scheduled frames /default "
595 << PlaybackDelegate::DEFAULT_MIN_SCHED_FRAMES
596 << "/, M - max sched\n\t\tframes /default "
597 << PlaybackDelegate::DEFAULT_MAX_SCHED_FRAMES << "/), shortcut sync\n";
598 col() << SBOLD("\tconversion") << "\toutput size conversion, can be:\n" <<
599 SBOLD("\t\tnone") << " - no conversion\n" <<
600 SBOLD("\t\tltbx") << " - down-converted letterbox SD\n" <<
601 SBOLD("\t\tamph") << " - down-converted anamorphic SD\n" <<
602 SBOLD("\t\t720c") << " - HD720 to HD1080 conversion\n" <<
603 SBOLD("\t\tHWlb") << " - simultaneous output of HD and down-converted letterbox SD\n" <<
604 SBOLD("\t\tHWam") << " - simultaneous output of HD and down-converted anamorphic SD\n" <<
605 SBOLD("\t\tHWcc") << " - simultaneous output of HD and center cut SD\n" <<
606 SBOLD("\t\txcap") << " - simultaneous output of 720p and 1080p cross-conversion\n" <<
607 SBOLD("\t\tua7p") << " - simultaneous output of SD and up-converted anamorphic 720p\n" <<
608 SBOLD("\t\tua1i") << " - simultaneous output of SD and up-converted anamorphic 1080i\n" <<
609 SBOLD("\t\tu47p") << " - simultaneous output of SD and up-converted anamorphic widescreen aspect ratio 14:9 to 720p\n" <<
610 SBOLD("\t\tu41i") << " - simultaneous output of SD and up-converted anamorphic widescreen aspect ratio 14:9 to 1080i\n" <<
611 SBOLD("\t\tup7p") << " - simultaneous output of SD and up-converted pollarbox 720p\n" <<
612 SBOLD("\t\tup1i") << " - simultaneous output of SD and up-converted pollarbox 1080i\n";
613 col() << SBOLD("\tHDMI3DPacking") << " can be (used in conjunction with \"3D\" option):\n" <<
614 SBOLD("\t\tSideBySideHalf, LineByLine, TopAndBottom, FramePacking, LeftOnly, RightOnly\n");
615 col() << SBOLD("\tUse1080PsF[=true|false|keep]") << " flag sets use of PsF on output instead of progressive (default is false)\n";
616 col() << SBOLD("\tprofile=<P>") << "\tuse desired device profile:\n";
618 col() << SBOLD("\tmaxresample=<N>") << " maximum amount the resample delta can be when scaling is applied. Measured in Hz\n";
619 col() << SBOLD("\tminresample=<N>") << " minimum amount the resample delta can be when scaling is applied. Measured in Hz\n";
620 col() << SBOLD("\ttargetbuffer=<N>") << " target amount of samples to have in the buffer (per channel)\n";
621 col() << SBOLD("\tkeep-settings") << "\tdo not apply any DeckLink settings by UG than required (keep user-selected defaults)\n";
622 col() << SBOLD("\tquery=<FourCC>") << "\tquery specified device argument in help listing\n";
623 }
624
625 col() << "\nRecognized pixel formats:";
626 for_each(uv_to_bmd_codec_map.cbegin(), uv_to_bmd_codec_map.cend(), [](auto const &i) { col() << " " << SBOLD(get_codec_name(i.first)); } );
627 cout << "\n";
628
629 col() << "Devices (idx, topological ID, name):\n";
630 // Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system
631 bool com_initialized = false;
632
633 // Enumerate all cards in this system
634 const bool natural_sort =
636 for (auto &d :
637 bmd_get_sorted_devices(&com_initialized, true, natural_sort)) {
638 IDeckLink *deckLink = get<0>(d).get();
639 string deviceName = bmd_get_device_name(deckLink);
640 if (deviceName.empty()) {
641 deviceName = "(unable to get name)";
642 }
643
644 char index[STR_LEN] = "";
645 if (!natural_sort) {
646 snprintf(index, sizeof index,
647 TBOLD("%c") ") ", get<char>(d));
648 }
649 if (full || natural_sort) {
650 snprintf(index + strlen(index),
651 sizeof index - strlen(index), TBOLD("%d") ") ",
652 get<int>(d));
653 }
654 // *** Print the model name of the DeckLink card
655 color_printf("\t%s" TBOLD("%6x") ") " TBOLD(
656 TGREEN("%s")) "\n",
657 index, get<unsigned>(d),
658 deviceName.c_str());
659 if (full) {
660 print_output_modes(deckLink, query_prop_fcc);
661 }
662
663 // Increment the total number of DeckLink cards found
664 numDevices++;
665 }
666
667 if (!full) {
668 col() << "(use \"" << SBOLD("fullhelp") << "\" to see device modes)\n";
669 }
670
671 decklink_uninitialize(&com_initialized);
672
673 // If no DeckLink cards were found in the system, inform the user
674 if (numDevices == 0)
675 {
676 log_msg(LOG_LEVEL_WARNING, "\nNo Blackmagic Design devices were found.\n");
677 return;
678 }
679
680 printf("\n");
681 if (full) {
683 printf("\n");
684 }
685 }
686
687static DeckLinkFrame*
689{
690 const int linesize =
691 vc_get_linesize(s->vid_desc.width, s->vid_desc.color_spec);
692 return s->stereo
693 ? DeckLink3DFrame::Create(
694 s->vid_desc.width, s->vid_desc.height, linesize,
696 : DeckLinkFrame::Create(
697 s->vid_desc.width, s->vid_desc.height, linesize,
699}
700
701static struct video_frame *
703{
704 struct state_decklink *s = (struct state_decklink *)state;
705 assert(s->magic == DECKLINK_MAGIC);
706 assert(s->initialized);
707
708 if (s->audio_reconfigure) {
710 MSG(FATAL, "Cannot reinit after audio reconf!\n");
711 abort();
712 }
713 s->audio_reconfigure = false;
714 }
715
716 struct video_frame *out = vf_alloc_desc(s->vid_desc);
717 static auto dispose = [](struct video_frame *frame) {
718 vf_free(frame);
719 };
720 out->callbacks.dispose = dispose;
721
722 const int linesize = vc_get_linesize(s->vid_desc.width, s->vid_desc.color_spec);
723 DeckLinkFrame *deckLinkFrame = nullptr;
724 lock_guard<mutex> lg(s->buffer_pool.lock);
725
726 while (!s->buffer_pool.frame_queue.empty()) {
727 auto tmp = s->buffer_pool.frame_queue.front();
728 DeckLinkFrame *frame =
729 s->stereo ? dynamic_cast<DeckLink3DFrame *>(tmp)
730 : dynamic_cast<DeckLinkFrame *>(tmp);
731 s->buffer_pool.frame_queue.pop();
732 if (!frame || // wrong type
733 frame->GetWidth() != (long)s->vid_desc.width ||
734 frame->GetHeight() != (long)s->vid_desc.height ||
735 frame->GetRowBytes() != linesize || frame->GetPixelFormat() != s->pixelFormat) {
736 tmp->Release();
737 } else {
738 deckLinkFrame = frame;
739 deckLinkFrame->AddRef();
740 break;
741 }
742 }
743 if (!deckLinkFrame) {
744 deckLinkFrame = allocate_new_decklink_frame(s);
745 }
746 out->callbacks.dispose_udata = (void *) deckLinkFrame;
747
748 deckLinkFrame->GetBytes((void **)&out->tiles[0].data);
749
750 if (s->stereo) {
751 IDeckLinkVideoFrame *deckLinkFrameRight = nullptr;
752 DeckLink3DFrame *frame3D = dynamic_cast<DeckLink3DFrame *>(deckLinkFrame);
753 assert(frame3D != nullptr);
754 frame3D->GetFrameForRightEye(&deckLinkFrameRight);
755 deckLinkFrameRight->GetBytes((void **)&out->tiles[1].data);
756 // release immedieatelly (parent still holds the reference)
757 deckLinkFrameRight->Release();
758 }
759
760 return out;
761}
762
763static void update_timecode(DeckLinkTimecode *tc, double fps)
764{
765 const float epsilon = 0.005;
766 uint8_t hours, minutes, seconds, frames;
767 BMDTimecodeBCD bcd;
768 bool dropFrame = false;
769
770 if(ceil(fps) - fps > epsilon) { /* NTSCi drop framecode */
771 dropFrame = true;
772 }
773
774 tc->GetComponents (&hours, &minutes, &seconds, &frames);
775 frames++;
776
777 if((double) frames > fps - epsilon) {
778 frames = 0;
779 seconds++;
780 if(seconds >= 60) {
781 seconds = 0;
782 minutes++;
783 if(dropFrame) {
784 if(minutes % 10 != 0)
785 seconds = 2;
786 }
787 if(minutes >= 60) {
788 minutes = 0;
789 hours++;
790 if(hours >= 24) {
791 hours = 0;
792 }
793 }
794 }
795 }
796
797 bcd = (frames % 10) | (frames / 10) << 4 | (seconds % 10) << 8 | (seconds / 10) << 12 | (minutes % 10) << 16 | (minutes / 10) << 20 |
798 (hours % 10) << 24 | (hours / 10) << 28;
799
800 tc->SetBCD(bcd);
801}
802
803static bool display_decklink_putf(void *state, struct video_frame *frame,
804 [[maybe_unused]] long long timeout_ns)
805{
806 struct state_decklink *s = (struct state_decklink *)state;
807 bool ret = true;
808
809 if (frame == NULL)
810 return true;
811
812 assert(s->magic == DECKLINK_MAGIC);
813
815 for (unsigned i = 0; i < frame->tile_count; ++i) {
817 }
818 }
819
820 auto *deckLinkFrame = (DeckLinkFrame *) frame->callbacks.dispose_udata;
821 if (s->emit_timecode) {
822 deckLinkFrame->SetTimecode(bmdTimecodeRP188Any, s->timecode);
823 }
824
825 if (s->low_latency) {
826 s->deckLinkOutput->DisplayVideoFrameSync(deckLinkFrame);
827 deckLinkFrame->Release();
828 } else {
829 deckLinkFrame->timestamp = frame->timestamp;
830 ret = s->delegate.EnqueueFrame(deckLinkFrame);
831 }
832 if(s->emit_timecode) {
834 }
835
837
838 s->delegate.PrintStats();
839
840 return ret;
841}
842
843static BMDDisplayMode get_mode(IDeckLinkOutput *deckLinkOutput, struct video_desc desc, BMDTimeValue *frameRateDuration,
844 BMDTimeScale *frameRateScale, bool stereo)
845{ IDeckLinkDisplayModeIterator *displayModeIterator;
846 IDeckLinkDisplayMode* deckLinkDisplayMode;
847 BMDDisplayMode displayMode = bmdModeUnknown;
848
849 // Populate the display mode combo with a list of display modes supported by the installed DeckLink card
850 if (FAILED(deckLinkOutput->GetDisplayModeIterator(&displayModeIterator)))
851 {
852 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Fatal: cannot create display mode iterator.\n");
853 return bmdModeUnknown;
854 }
855
856 while (displayModeIterator->Next(&deckLinkDisplayMode) == S_OK)
857 {
858 BMD_STR modeNameString;
859 if (deckLinkDisplayMode->GetName(&modeNameString) == S_OK)
860 {
861 if (deckLinkDisplayMode->GetWidth() == (long) desc.width &&
862 deckLinkDisplayMode->GetHeight() == (long) desc.height)
863 {
864 double displayFPS;
865 BMDFieldDominance dominance;
866 bool interlaced;
867
868 dominance = deckLinkDisplayMode->GetFieldDominance();
869 if (dominance == bmdLowerFieldFirst ||
870 dominance == bmdUpperFieldFirst) {
871 if (dominance == bmdLowerFieldFirst) {
872 bug_msg(
874 "Lower field first format "
875 "detected, fields can be "
876 "switched! ");
877 }
878 interlaced = true;
879 } else { // progressive, psf, unknown
880 interlaced = false;
881 }
882
883 deckLinkDisplayMode->GetFrameRate(frameRateDuration,
885 displayFPS = (double) *frameRateScale / *frameRateDuration;
886 if (fabs(desc.fps - displayFPS) < 0.01 && (desc.interlacing == INTERLACED_MERGED) == interlaced) {
887 MSG(INFO, "Selected mode: %s%s\n",
889 modeNameString)
890 .c_str(),
891 stereo ? " (3D)" : "");
892 displayMode = deckLinkDisplayMode->GetDisplayMode();
893 release_bmd_api_str(modeNameString);
894 deckLinkDisplayMode->Release();
895 break;
896 }
897 }
898 release_bmd_api_str(modeNameString);
899 }
900 deckLinkDisplayMode->Release();
901 }
902 displayModeIterator->Release();
903
904 return displayMode;
905}
906
907static bool enable_audio(struct state_decklink *s, int bps, int channels)
908{
909 const BMDAudioSampleType sample_type =
910 bps == 2 ? bmdAudioSampleType16bitInteger
911 : bmdAudioSampleType32bitInteger;
912 const BMDAudioOutputStreamType stream_type =
913 s->low_latency ? bmdAudioOutputStreamContinuous
914 : bmdAudioOutputStreamTimestamped;
916 s->deckLinkOutput->EnableAudioOutput(
917 bmdAudioSampleRate48kHz, sample_type, channels, stream_type),
918 "EnableAudioOutput");
919 return true;
920}
921
922static bool
924{
925 auto *s = (struct state_decklink *) state;
926 assert(s->magic == DECKLINK_MAGIC);
927
928 BMDDisplayMode displayMode;
929 BMD_BOOL supported;
930 HRESULT result;
931
932 const unique_lock<mutex> lk(s->audio_reconf_lock);
933 s->delegate.ResetAudio(); // disables audio until full reconf
934
935 s->vid_desc = desc;
936
937 if (s->initialized) {
938 if (!s->low_latency) {
939 CALL_AND_CHECK(s->deckLinkOutput->StopScheduledPlayback(
940 0, nullptr, 0),
941 "StopScheduledPlayback");
942 }
943 s->deckLinkOutput->SetScheduledFrameCompletionCallback(nullptr);
944 CALL_AND_CHECK(s->deckLinkOutput->DisableVideoOutput(),
945 "DisableVideoOutput");
946 if (s->play_audio) {
947 CALL_AND_CHECK(s->deckLinkOutput->DisableAudioOutput(),
948 "DisableAudioOutput");
949 }
950 s->initialized = false;
951 }
952
953 s->delegate.Reset();
954
956 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Using limited range R10k as specified by BMD, use '--param "
957 R10K_FULL_OPT "' to override.\n");
958 }
959
960 auto it = std::find_if(uv_to_bmd_codec_map.begin(),
962 [&desc](const std::pair<codec_t, BMDPixelFormat>& el){ return el.first == desc.color_spec; });
963 if (it == uv_to_bmd_codec_map.end()) {
964 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unsupported pixel format!\n");
965 return false;
966 }
967 s->pixelFormat = it->second;
968
969 if (desc.tile_count <= 2 && desc.tile_count != (s->stereo ? 2 : 1)) {
970 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Stereo %s enabled but receiving %u streams. %sabling "
971 "it. This behavior is experimental so please report any problems. "
972 "You can also specify (or not) `3D` option explicitly.\n"
973 , s->stereo ? "" : "not", desc.tile_count, s->stereo ? "dis" : "en");
974 s->stereo = !s->stereo;
975 }
976
977 if (s->stereo) {
978 bmd_check_stereo_profile(s->deckLink);
979 if ((int) desc.tile_count != 2) {
980 log_msg(LOG_LEVEL_ERROR, MOD_NAME "In stereo mode exactly "
981 "2 streams expected, %d received.\n", desc.tile_count);
982 return false;
983 }
984 } else {
985 if ((int) desc.tile_count == 2) {
986 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Received 2 streams but stereo mode is not enabled! Did you forget a \"3D\" parameter?\n");
987 }
988 }
989
990 BMDVideoOutputFlags outputFlags = bmdVideoOutputFlagDefault;
991 BMDSupportedVideoModeFlags supportedFlags = bmdSupportedVideoModeDefault;
992
993 BMDTimeValue frameRateDuration{};
994 BMDTimeScale frameRateScale{};
995 displayMode = get_mode(s->deckLinkOutput, desc, &frameRateDuration,
996 &frameRateScale, s->stereo);
997 if (displayMode == bmdModeUnknown) {
998 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not find suitable video mode.\n");
999 return false;
1000 }
1001 s->delegate.SetFrameTimes(frameRateDuration, frameRateScale);
1002
1003 if (s->emit_timecode) {
1004 outputFlags = (BMDVideoOutputFlags)(outputFlags | bmdVideoOutputRP188);
1005 }
1006
1007 if (s->stereo) {
1008 outputFlags = (BMDVideoOutputFlags)(outputFlags | bmdVideoOutputDualStream3D);
1009 supportedFlags = (BMDSupportedVideoModeFlags)(supportedFlags |
1010 bmdSupportedVideoModeDualStream3D);
1011 }
1012
1013 const bmd_option subsampling_444(codec_is_a_rgb(desc.color_spec),
1014 false); // we don't have pixfmt for 444 YCbCr
1015 subsampling_444.device_write(s->deckLinkConfiguration, bmdDeckLinkConfig444SDIVideoOutput,
1016 MOD_NAME);
1017
1018 if (!s->keep_device_defaults &&
1019 s->device_options.find(bmdDeckLinkConfigSDIOutputLinkConfiguration) ==
1020 s->device_options.end()) {
1021 const int64_t link = desc.width == 7680 ? bmdLinkConfigurationQuadLink
1022 : bmdLinkConfigurationSingleLink;
1023 bmd_option(link).device_write(s->deckLinkConfiguration,
1024 bmdDeckLinkConfigSDIOutputLinkConfiguration,
1025 MOD_NAME);
1026 }
1027
1028 int64_t link = 0;
1029 s->deckLinkConfiguration->GetInt(bmdDeckLinkConfigSDIOutputLinkConfiguration, &link);
1030 if (!s->keep_device_defaults && s->profile_req.is_default() &&
1031 link == bmdLinkConfigurationQuadLink) {
1033 << MOD_NAME "Quad-link detected - setting 1-subdevice-1/2-duplex "
1034 "profile automatically, use 'profile=keep' to override.\n";
1036 s->deckLink, bmd_option((int64_t)bmdProfileOneSubDeviceHalfDuplex), s->stereo);
1037 } else if (link == bmdLinkConfigurationQuadLink &&
1038 (!s->profile_req.keep() &&
1039 s->profile_req.get_int() != bmdProfileOneSubDeviceHalfDuplex)) {
1040 LOG(LOG_LEVEL_WARNING) << MOD_NAME "Setting quad-link and an incompatible device "
1041 "profile may not be supported!\n";
1042 }
1043
1044 BMD_BOOL quad_link_supp = BMD_FALSE;
1045 if (s->deckLinkAttributes->GetFlag(BMDDeckLinkSupportsQuadLinkSDI, &quad_link_supp) ==
1046 S_OK &&
1047 quad_link_supp == BMD_TRUE) {
1048 s->quad_square_division_split.device_write(
1049 s->deckLinkConfiguration,
1050 bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit, MOD_NAME);
1051 }
1052
1053 const BMDVideoOutputConversionMode conversion_mode =
1054 s->device_options.find(bmdDeckLinkConfigVideoOutputConversionMode) !=
1055 s->device_options.end()
1056 ? (BMDVideoOutputConversionMode)s->device_options
1057 .at(bmdDeckLinkConfigVideoOutputConversionMode)
1058 .get_int()
1059 : (BMDVideoOutputConversionMode)bmdNoVideoOutputConversion;
1060 EXIT_IF_FAILED(s->deckLinkOutput->DoesSupportVideoMode(
1061 bmdVideoConnectionUnspecified, displayMode, s->pixelFormat,
1062 conversion_mode, supportedFlags, nullptr, &supported),
1063 "DoesSupportVideoMode");
1064 if (!supported) {
1066 MOD_NAME "Requested parameters "
1067 "combination not supported - %d * %dx%d@%f, timecode %s.\n",
1069 (outputFlags & bmdVideoOutputRP188 ? "ON" : "OFF"));
1070 return false;
1071 }
1072
1073 result = s->deckLinkOutput->EnableVideoOutput(displayMode, outputFlags);
1074 if (FAILED(result)) {
1075 if (result == E_ACCESSDENIED) {
1077 MOD_NAME "Unable to access the hardware or output "
1078 "stream currently active (another application "
1079 "using it?).\n");
1080 } else {
1082 << MOD_NAME << "EnableVideoOutput: " << bmd_hresult_to_string(result)
1083 << "\n";
1084 }
1085 return false;
1086 }
1087
1088 if (s->play_audio) {
1089 if (!enable_audio(s, s->aud_desc.bps, s->aud_desc.ch_count)) {
1090 return false;
1091 }
1092 }
1093
1094 if (!s->low_latency) {
1095 // Provide this class as a delegate to a video output interface
1096 s->deckLinkOutput->SetScheduledFrameCompletionCallback(
1097 &s->delegate);
1098 if (!s->delegate.Preroll(s)) {
1099 return false;
1100 }
1101 }
1102
1103 s->initialized = true;
1104 s->audio_reconfigure = false;
1105 return true;
1106}
1107
1108bool
1109PlaybackDelegate::Preroll(struct state_decklink *s)
1110{
1111 auto *f = allocate_new_decklink_frame(s);
1112 for (unsigned i = 0;
1113 i < (m_min_sched_frames + m_min_sched_frames + 1) / 2; ++i) {
1114 f->AddRef();
1115 const bool ret = EnqueueFrame(f);
1116 assert(ret);
1117 ScheduleNextFrame();
1118 }
1119 f->Release(); // release initial reference from alloc
1120 HRESULT result =
1121 m_deckLinkOutput->StartScheduledPlayback(0, frameRateScale, 1.0);
1122 if (FAILED(result)) {
1124 << MOD_NAME << "StartScheduledPlayback (video): "
1125 << bmd_hresult_to_string(result) << "\n";
1126 m_deckLinkOutput->DisableVideoOutput();
1127 return false;
1128 }
1129 return true;
1130}
1131
1132static void display_decklink_probe(struct device_info **available_cards, int *count, void (**deleter)(void *))
1133{
1134 UNUSED(deleter);
1135
1136 *count = 0;
1137 *available_cards = nullptr;
1138
1139 bool com_initialized = false;
1140 // Enumerate all cards in this system
1141 for (auto &d : bmd_get_sorted_devices(&com_initialized, false)) {
1142 IDeckLink *deckLink = get<0>(d).get();
1143 string deviceName = bmd_get_device_name(deckLink);
1144 if (deviceName.empty()) {
1145 deviceName = "(unknown)";
1146 }
1147
1148 *count += 1;
1149 *available_cards = (struct device_info *)
1150 realloc(*available_cards, *count * sizeof(struct device_info));
1151 memset(*available_cards + *count - 1, 0, sizeof(struct device_info));
1152 snprintf((*available_cards)[*count - 1].dev,
1153 sizeof(*available_cards)[*count - 1].dev, ":device=%6x",
1154 get<unsigned>(d));
1155 snprintf((*available_cards)[*count - 1].extra, sizeof (*available_cards)[*count - 1].extra, "\"embeddedAudioAvailable\":\"t\"");
1156 (*available_cards)[*count - 1].repeatable = false;
1157
1158 strncpy((*available_cards)[*count - 1].name, deviceName.c_str(),
1159 sizeof (*available_cards)[*count - 1].name - 1);
1160
1161 dev_add_option(&(*available_cards)[*count - 1], "3D", "3D", "3D", ":3D", true);
1162 dev_add_option(&(*available_cards)[*count - 1], "Profile", "Duplex profile can be one of: 1dhd, 2dhd, 2dfd, 4dhd, keep", "profile", ":profile=", false);
1163 dev_add_option(&(*available_cards)[*count - 1], "HDR", "Can be one of: SDR, HDR, PQ, HLG or int 0-7", "hdr", ":HDR=", false);
1164 }
1165
1166 decklink_uninitialize(&com_initialized);
1167}
1168
1169void
1170PlaybackDelegate::SetSynchronized(const char *cfg)
1171{
1172 if (cfg == nullptr) {
1173 return;
1174 }
1175 cfg += 1;
1176 m_min_sched_frames = stoi(cfg);
1177 if (strchr(cfg, ',') != nullptr) {
1178 m_max_sched_frames = stoi(strchr(cfg, ',') + 1);
1179 } else {
1180 m_max_sched_frames =
1181 m_min_sched_frames + SCHED_RANGE;
1182 }
1183 assert(m_max_sched_frames > m_min_sched_frames);
1184}
1185
1186static void
1188{
1189 if (on) {
1190 MSG(WARNING, "Low latency option unneeded (implicit).\n");
1191 assert(s->low_latency); // check if really so
1192 return;
1193 }
1194 MSG(WARNING, "Deprecated, do not use - "
1195 "see option \"synchroninzed\" instead.\n");
1196 s->low_latency = false;
1197}
1198
1199static bool settings_init(struct state_decklink *s, const char *fmt,
1200 string &cardId) {
1201 if (strlen(fmt) == 0) {
1202 return true;
1203 }
1204
1205 auto tmp = static_cast<char *>(alloca(strlen(fmt) + 1));
1206 strcpy(tmp, fmt);
1207 char *ptr;
1208 char *save_ptr = nullptr;
1209
1210 ptr = strtok_r(tmp, ":", &save_ptr);
1211 assert(ptr != nullptr);
1212 int i = 0;
1213 bool first_option_is_device = true;
1214 while (ptr[i] != '\0') {
1215 if (!isdigit(ptr[i]) && ptr[i] != ',') {
1216 first_option_is_device = false;
1217 break;
1218 }
1219 i++;
1220 }
1221 if (first_option_is_device) {
1222 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Unnamed device index "
1223 "deprecated. Use \"device=%s\" instead.\n", ptr);
1224 cardId = ptr;
1225 ptr = strtok_r(nullptr, ":", &save_ptr);
1226 }
1227
1228 while (ptr != nullptr) {
1229 const char *val = strchr(ptr, '=') + 1;
1230 if (IS_KEY_PREFIX(ptr, "device")) {
1231 cardId = strchr(ptr, '=') + 1;
1232 } else if (IS_KEY_PREFIX(ptr, "profile")) {
1233 s->profile_req.parse(strchr(ptr, '=') + 1);
1234 } else if (strcasecmp(ptr, "3D") == 0) {
1235 s->stereo = true;
1236 } else if (strcasecmp(ptr, "timecode") == 0) {
1237 s->emit_timecode = true;
1238 } else if (strcasecmp(ptr, "single-link") == 0) {
1239 s->device_options[bmdDeckLinkConfigSDIOutputLinkConfiguration].set_int(bmdLinkConfigurationSingleLink);
1240 } else if (strcasecmp(ptr, "dual-link") == 0) {
1241 s->device_options[bmdDeckLinkConfigSDIOutputLinkConfiguration].set_int(bmdLinkConfigurationDualLink);
1242 } else if (strcasecmp(ptr, "quad-link") == 0) {
1243 s->device_options[bmdDeckLinkConfigSDIOutputLinkConfiguration].set_int(bmdLinkConfigurationQuadLink);
1244 } else if (strcasecmp(ptr, "full-duplex") == 0) {
1245 s->profile_req.set_int(bmdProfileOneSubDeviceFullDuplex);
1246 } else if (strcasecmp(ptr, "half-duplex") == 0) {
1247 s->profile_req.set_int(bmdDuplexHalf);
1248 } else if (strcasecmp(ptr, "simplex") == 0) {
1249 s->profile_req.set_int(bmdProfileOneSubDeviceHalfDuplex);
1250 } else if (strcasecmp(ptr, "LevelA") == 0) {
1251 s->device_options[bmdDeckLinkConfigSMPTELevelAOutput].set_flag(true);
1252 } else if (strcasecmp(ptr, "LevelB") == 0) {
1253 s->device_options[bmdDeckLinkConfigSMPTELevelAOutput].set_flag(false);
1254 } else if (strncasecmp(ptr, "HDMI3DPacking=", strlen("HDMI3DPacking=")) == 0) {
1255 char *packing = ptr + strlen("HDMI3DPacking=");
1256 if (strcasecmp(packing, "SideBySideHalf") == 0) {
1257 s->device_options[bmdDeckLinkConfigHDMI3DPackingFormat].set_int(bmdVideo3DPackingSidebySideHalf);
1258 } else if (strcasecmp(packing, "LineByLine") == 0) {
1259 s->device_options[bmdDeckLinkConfigHDMI3DPackingFormat].set_int(bmdVideo3DPackingLinebyLine);
1260 } else if (strcasecmp(packing, "TopAndBottom") == 0) {
1261 s->device_options[bmdDeckLinkConfigHDMI3DPackingFormat].set_int(bmdVideo3DPackingTopAndBottom);
1262 } else if (strcasecmp(packing, "FramePacking") == 0) {
1263 s->device_options[bmdDeckLinkConfigHDMI3DPackingFormat].set_int(bmdVideo3DPackingFramePacking);
1264 } else if (strcasecmp(packing, "LeftOnly") == 0) {
1265 s->device_options[bmdDeckLinkConfigHDMI3DPackingFormat].set_int(bmdVideo3DPackingRightOnly);
1266 } else if (strcasecmp(packing, "RightOnly") == 0) {
1267 s->device_options[bmdDeckLinkConfigHDMI3DPackingFormat].set_int(bmdVideo3DPackingLeftOnly);
1268 } else {
1269 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown HDMI 3D packing %s.\n", packing);
1270 return false;
1271 }
1272 } else if (strncasecmp(ptr, "audio_level=", strlen("audio_level=")) == 0) {
1274 [bmdDeckLinkConfigAnalogAudioConsumerLevels] =
1276 } else if (IS_KEY_PREFIX(ptr, "conversion")) {
1277 s->device_options[bmdDeckLinkConfigVideoOutputConversionMode].parse(strchr(ptr, '=') + 1);
1278 } else if (is_prefix_of(ptr, "Use1080pNotPsF") || is_prefix_of(ptr, "Use1080PsF")) {
1279 s->device_options[bmdDeckLinkConfigOutput1080pAsPsF].parse(strchr(ptr, '=') + 1);
1280 if (strncasecmp(ptr, "Use1080pNotPsF", strlen("Use1080pNotPsF")) == 0) { // compat, inverse
1281 s->device_options[bmdDeckLinkConfigOutput1080pAsPsF].set_flag(s->device_options[bmdDeckLinkConfigOutput1080pAsPsF].get_flag());
1282 }
1283 } else if (strcasecmp(ptr, "low-latency") == 0 || strcasecmp(ptr, "no-low-latency") == 0) {
1284 set_low_latency(s, strcasecmp(ptr, "low-latency") == 0);
1285 } else if (IS_PREFIX(ptr, "synchronized")) {
1286 s->low_latency = false;
1287 s->delegate.SetSynchronized(strchr(ptr, '='));
1288 } else if (strcasecmp(ptr, "quad-square") == 0 || strcasecmp(ptr, "no-quad-square") == 0) {
1289 s->quad_square_division_split.set_flag(strcasecmp(ptr, "quad-square") == 0);
1290 } else if (strncasecmp(ptr, "hdr", strlen("hdr")) == 0) {
1291 s->requested_hdr_mode.EOTF = static_cast<int64_t>(HDR_EOTF::HDR); // default
1292 if (strncasecmp(ptr, "hdr=", strlen("hdr=")) == 0) {
1293 try {
1294 s->requested_hdr_mode.Init(ptr + strlen("hdr="));
1295 } catch (ug_no_error const &e) {
1296 return false;
1297 } catch (exception const &e) {
1298 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "HDR mode init: " << e.what() << "\n";
1299 return false;
1300 }
1301 }
1302 } else if (strstr(ptr, "keep-settings") == ptr) {
1303 s->keep_device_defaults = true;
1304 } else if (strstr(ptr, "drift_fix") == ptr) {
1306 } else if (strncasecmp(ptr, "maxresample=", strlen("maxresample=")) == 0) {
1307 s->audio_drift_fixer.set_max_hz(parse_uint32(strchr(ptr, '=') + 1));
1308 } else if (strncasecmp(ptr, "minresample=", strlen("minresample=")) == 0) {
1309 s->audio_drift_fixer.set_min_hz(parse_uint32(strchr(ptr, '=') + 1));
1310 } else if (strncasecmp(ptr, "targetbuffer=", strlen("targetbuffer=")) == 0) {
1311 s->audio_drift_fixer.set_target_buffer(parse_uint32(strchr(ptr, '=') + 1));
1312 } else if ((strchr(ptr, '=') != nullptr && strchr(ptr, '=') - ptr == 4) || strlen(ptr) == 4) {
1313 s->device_options[(BMDDeckLinkConfigurationID) bmd_read_fourcc(ptr)].parse(strchr(ptr, '=') + 1);
1314 } else {
1315 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unknown option in config string: %s\n", ptr);
1316 return false;
1317 }
1318 ptr = strtok_r(nullptr, ":", &save_ptr);
1319 }
1320
1321 return true;
1322}
1323
1325#define CHECK_CH_COUNT(channels) \
1326 assert((channels) >= 2 && (channels) != 4 && (channels) <= 64 && \
1327 is_power_of_two((channels)))
1328
1329static void
1331 IDeckLinkConfiguration *deckLinkConfiguration,
1332 unsigned int audio_output)
1333{
1334 if (audio_output == 0U) {
1335 s->play_audio = false;
1336 return;
1337 }
1338 s->play_audio = true;
1339
1340 int64_t max_aud_chans = 0;
1341 HRESULT result = s->deckLinkAttributes->GetInt(
1342 BMDDeckLinkMaximumAudioChannels, &max_aud_chans);
1343 if (result != S_OK) {
1344 MSG(WARNING, "Cannot get maximum audio channels: %s\n",
1345 bmd_hresult_to_string(result).c_str());
1346 } else {
1347 s->max_aud_chans = (int) max_aud_chans;
1349 }
1350 MSG(VERBOSE, "Using maximum audio channels: %d\n", s->max_aud_chans);
1351
1352 MSG(INFO, "Using audio output: %s\n",
1353 get_audio_conn_flag_name(audio_output));
1354
1355 /* Actually no action is required to set audio connection because BMD
1356 * card plays audio through all its outputs (AES/SDI/analog) .... */
1357 BMDAudioOutputAnalogAESSwitch audioConnection{};
1358 switch (audio_output) {
1360 return;
1362 audioConnection = bmdAudioOutputSwitchAESEBU;
1363 break;
1365 audioConnection = bmdAudioOutputSwitchAnalog;
1366 break;
1367 default:
1368 MSG(ERROR, "Unsupporetd audio connection: %d.\n", audio_output);
1369 abort();
1370 }
1371 /* .... one exception is a card that has switchable cables between
1372 * AES/EBU and analog. (But this applies only for channels 3 and above.)
1373 */
1374 result = deckLinkConfiguration->SetInt(
1375 bmdDeckLinkConfigAudioOutputAESAnalogSwitch, audioConnection);
1376 if (result == S_OK) { // has switchable channels
1377 MSG(INFO, "Card with switchable audio channels detected. "
1378 "Switched to correct format.\n");
1379 } else if (result == E_NOTIMPL) {
1380 // normal case - without switchable channels
1381 MSG(VERBOSE,
1382 "Not setting switchable AESEBU/analog - not supported.\n");
1383 } else {
1384 MSG(WARNING,
1385 "Unable to switch audio output for channels 3 or "
1386 "above although the card shall support it: %s\nCheck if "
1387 "it is ok. Continuing anyway.\n",
1388 bmd_hresult_to_string(result).c_str());
1389 }
1390}
1391
1392static void *display_decklink_init(struct module *parent, const char *fmt, unsigned int flags)
1393{
1394 HRESULT result;
1395 string cardId("0");
1396 IDeckLinkConfiguration* deckLinkConfiguration = NULL;
1397 // for Decklink Studio which has switchable XLR - analog 3 and 4 or AES/EBU 3,4 and 5,6
1398
1399 if (strcmp(fmt, "help") == 0 || strcmp(fmt, "fullhelp") == 0) {
1400 show_help(strcmp(fmt, "fullhelp") == 0);
1401 return INIT_NOERR;
1402 }
1403 if (IS_KEY_PREFIX(fmt, "query")) {
1404 show_help(true, strchr(fmt, '=') + 1);
1405 return INIT_NOERR;
1406 }
1407
1409 return NULL;
1410 }
1411
1412 auto *s = new state_decklink();
1414
1415 bool succeeded = false;
1416 try {
1417 succeeded = settings_init(s, fmt, cardId);
1418 } catch (exception &e) {
1419 if (invalid_arg_is_numeric(e.what())) {
1420 MSG(ERROR, "Invalid number passed where numeric "
1421 "argument expected!\n");
1422 } else {
1423 MSG(ERROR, "%s\n", e.what());
1424 }
1425 }
1426 if (!succeeded) {
1427 delete s;
1428 return NULL;
1429 }
1430
1431 // Connect to the first DeckLink instance
1432 for (auto &d : bmd_get_sorted_devices(&s->com_initialized, true)) {
1433 s->deckLink = get<0>(d).release(); // unmanage
1434 string deviceName = bmd_get_device_name(s->deckLink);
1435
1436 if (!deviceName.empty() && deviceName == cardId) {
1437 break;
1438 }
1439 // topological ID
1440 const unsigned long tid = strtoul(cardId.c_str(), nullptr, 16);
1441 if (get<unsigned>(d) == tid) {
1442 break;
1443 }
1444 // natural (old) index
1445 if (isdigit(cardId.c_str()[0])) {
1446 if (atoi(cardId.c_str()) == get<int>(d)) {
1447 break;
1448 }
1449 }
1450 // new (character) index
1451 if (cardId.length() == 1 && cardId[0] >= 'a') {
1452 if (cardId[0] == get<char>(d)) {
1453 break;
1454 }
1455 }
1456
1457 s->deckLink->Release();
1458 s->deckLink = nullptr;
1459 }
1460 if (s->deckLink == nullptr) {
1461 LOG(LOG_LEVEL_ERROR) << "No DeckLink PCI card " << cardId << " found\n";
1463 return nullptr;
1464 }
1465 // Print the model name of the DeckLink card
1466 string deviceName = bmd_get_device_name(s->deckLink);
1467 if (!deviceName.empty()) {
1468 LOG(LOG_LEVEL_INFO) << MOD_NAME "Using device " << deviceName << "\n";
1469 }
1470
1471 // Get IDeckLinkAttributes object
1472 result = s->deckLink->QueryInterface(
1473 IID_IDeckLinkProfileAttributes,
1474 reinterpret_cast<void **>(&s->deckLinkAttributes));
1475 if (result != S_OK) {
1477 << MOD_NAME "Could not query device attributes: "
1478 << bmd_hresult_to_string(result) << "\n";
1480 return nullptr;
1481 }
1482
1483 if(s->emit_timecode) {
1484 s->timecode = new DeckLinkTimecode;
1485 } else {
1486 s->timecode = NULL;
1487 }
1488
1489 if (!s->keep_device_defaults && !s->profile_req.keep()) {
1491 s->stereo)) {
1493 return nullptr;
1494 }
1495 }
1496
1497 // Obtain the audio/video output interface (IDeckLinkOutput)
1498 if ((result = s->deckLink->QueryInterface(
1499 IID_IDeckLinkOutput, reinterpret_cast<void **>(&s->deckLinkOutput))) != S_OK) {
1501 MOD_NAME "Could not obtain the IDeckLinkOutput interface: %08x\n",
1502 (int)result);
1504 return nullptr;
1505 }
1506
1507 // Query the DeckLink for its configuration interface
1508 result = s->deckLink->QueryInterface(IID_IDeckLinkConfiguration,
1509 reinterpret_cast<void **>(&deckLinkConfiguration));
1510 s->deckLinkConfiguration = deckLinkConfiguration;
1511 if (result != S_OK) {
1513 "Could not obtain the IDeckLinkConfiguration interface: %08x\n",
1514 (int)result);
1516 return nullptr;
1517 }
1518
1519 for (const auto &o : s->device_options) {
1520 if (s->keep_device_defaults && !o.second.is_user_set()) {
1521 continue;
1522 }
1523 if (!o.second.device_write(deckLinkConfiguration, o.first, MOD_NAME)) {
1525 return nullptr;
1526 }
1527 }
1528
1529 if (s->requested_hdr_mode.EOTF != static_cast<int64_t>(HDR_EOTF::NONE)) {
1530 BMD_BOOL hdr_supp = BMD_FALSE;
1531 if (s->deckLinkAttributes->GetFlag(BMDDeckLinkSupportsHDRMetadata, &hdr_supp) !=
1532 S_OK) {
1534 << MOD_NAME
1535 << "HDR requested, but unable to validate HDR support. Will "
1536 "try to pass it anyway which may result in blank image if "
1537 "not supported - remove the option if so.\n";
1538 } else {
1539 if (hdr_supp != BMD_TRUE) {
1541 << MOD_NAME
1542 << "HDR requested, but card doesn't support that.\n";
1544 return nullptr;
1545 }
1546 }
1547
1548 BMD_BOOL rec2020_supp = BMD_FALSE;
1549 if (s->deckLinkAttributes->GetFlag(BMDDeckLinkSupportsColorspaceMetadata,
1550 &rec2020_supp) != S_OK) {
1552 << MOD_NAME << "Cannot check Rec. 2020 color space metadata support.\n";
1553 } else {
1554 if (rec2020_supp != BMD_TRUE) {
1556 << MOD_NAME
1557 << "Rec. 2020 color space metadata not supported.\n";
1558 }
1559 }
1560 }
1561
1562 set_audio_props(s, deckLinkConfiguration,
1564
1565 if (!s->low_latency) {
1566 s->delegate.SetDecklinkOutput(s->deckLinkOutput);
1567 }
1568 // s->state.at(i).deckLinkOutput->DisableAudioOutput();
1569
1570 return (void *)s;
1571}
1572
1574{
1575 debug_msg("display_decklink_done\n"); /* TOREMOVE */
1576 struct state_decklink *s = (struct state_decklink *)state;
1577
1578 assert (s != NULL);
1579
1580 if (s->initialized) {
1581 if (!s->low_latency) {
1583 s->deckLinkOutput->StopScheduledPlayback(0, nullptr, 0),
1584 "StopScheduledPlayback");
1585 }
1586 s->deckLinkOutput->SetScheduledFrameCompletionCallback(nullptr);
1587 if (s->play_audio) {
1589 s->deckLinkOutput->DisableAudioOutput(),
1590 "DisableAudiioOutput");
1591 }
1592 CALL_AND_CHECK(s->deckLinkOutput->DisableVideoOutput(),
1593 "DisableVideoOutput");
1594 }
1595
1600
1601 while (!s->buffer_pool.frame_queue.empty()) {
1602 auto tmp = s->buffer_pool.frame_queue.front();
1603 s->buffer_pool.frame_queue.pop();
1604 tmp->Release();
1605 }
1606
1607 delete s->timecode;
1608
1610 delete s;
1611}
1612
1613static bool display_decklink_get_property(void *state, int property, void *val, size_t *len)
1614{
1615 struct state_decklink *s = (struct state_decklink *)state;
1616 vector<codec_t> codecs(uv_to_bmd_codec_map.size());
1617 int rgb_shift[] = {16, 8, 0};
1619 int count = 0;
1620 for (auto & c : uv_to_bmd_codec_map) {
1621 if (decklink_supports_codec(s->deckLinkOutput, c.second)) {
1622 codecs[count++] = c.first;
1623 }
1624 }
1625
1626 switch (property) {
1628 if(sizeof(codec_t) * count <= *len) {
1629 memcpy(val, codecs.data(), sizeof(codec_t) * count);
1630 *len = sizeof(codec_t) * count;
1631 } else {
1632 return false;
1633 }
1634 break;
1636 if(sizeof(rgb_shift) > *len) {
1637 return false;
1638 }
1639 memcpy(val, rgb_shift, sizeof(rgb_shift));
1640 *len = sizeof(rgb_shift);
1641 break;
1643 *(int *) val = PITCH_DEFAULT;
1644 *len = sizeof(int);
1645 break;
1648 *len = sizeof(int);
1649 break;
1651 if(sizeof(supported_il_modes) <= *len) {
1652 memcpy(val, supported_il_modes, sizeof(supported_il_modes));
1653 } else {
1654 return false;
1655 }
1656 *len = sizeof(supported_il_modes);
1657 break;
1659 {
1660 assert(*len == sizeof(struct audio_desc));
1661 struct audio_desc *desc = (struct audio_desc *) val;
1662 desc->sample_rate = 48000;
1663 if (desc->ch_count >= s->max_aud_chans) {
1664 desc->ch_count = (int) s->max_aud_chans;
1665 } else if (desc->ch_count <= 2) {
1666 desc->ch_count = 2;
1667 } else if (desc->ch_count <= 8) {
1668 desc->ch_count = 8;
1669 } else {
1670 desc->ch_count =
1671 next_power_of_two(desc->ch_count);
1672 }
1673 desc->codec = AC_PCM;
1674 desc->bps = desc->bps < 3 ? 2 : 4;
1675 }
1676 break;
1677 default:
1678 return false;
1679 }
1680 return true;
1681}
1682
1683/*
1684 * AUDIO
1685 */
1686void PlaybackDelegate::ScheduleAudio(const struct audio_frame *frame,
1687 uint32_t *const samples, bool underflow) {
1688 if (m_adata.saved_sync_ts == INT64_MIN &&
1689 m_audio_sync_ts == audio_sync_val::deinit) {
1690 return;
1691 }
1692 if (m_adata.saved_sync_ts != m_audio_sync_ts &&
1693 m_audio_sync_ts > audio_sync_val::resync) {
1694 m_adata = audio_vals{};
1695 m_adata.last_sync_ts =
1696 m_adata.saved_sync_ts = m_audio_sync_ts;
1697 }
1698
1699 if (frame->timestamp < m_adata.last_sync_ts) { // wrap-around
1700 m_adata.last_sync_ts -= (1LLU << 32);
1701 }
1702 BMDTimeValue streamTime =
1703 ((int64_t) frame->timestamp - m_adata.last_sync_ts) *
1704 bmdAudioSampleRate48kHz / 90000;
1705
1706 LOG(LOG_LEVEL_DEBUG) << MOD_NAME << "audio streamTime: " << streamTime
1707 << "; samples: " << *samples
1708 << "; RTP TS: " << frame->timestamp
1709 << "; sync TS: " << m_audio_sync_ts << "\n";
1710 const HRESULT res = m_deckLinkOutput->ScheduleAudioSamples(
1711 frame->data, *samples, streamTime, bmdAudioSampleRate48kHz,
1712 samples);
1713 if (FAILED(res)) {
1714 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "ScheduleAudioSamples: "
1715 << bmd_hresult_to_string(res) << "\n";
1716 }
1717
1718 if (underflow) {
1719 static unsigned count;
1720 if (count++ == 50) {
1721 MSG(WARNING, "Excessive underflow in sync mode! Try to "
1722 "increase sync frames (:sync=%d or so).\n",
1723 m_min_sched_frames + 2);
1724 }
1725 }
1726}
1727
1729{
1730 struct state_decklink *s = (struct state_decklink *)state;
1731 assert(s->play_audio);
1732 if (s->audio_reconfigure) {
1733 return;
1734 }
1735 const unsigned int sampleFrameCount =
1736 frame->data_len / (frame->bps * frame->ch_count);
1737 uint32_t sampleFramesWritten = sampleFrameCount;
1738
1739 uint32_t buffered = 0;
1740 s->deckLinkOutput->GetBufferedAudioSampleFrameCount(&buffered);
1741 if (buffered == 0) {
1742 LOG(LOG_LEVEL_WARNING) << MOD_NAME << "audio buffer underflow!\n";
1743 }
1744
1745 if (s->low_latency) {
1746 HRESULT res = s->deckLinkOutput->WriteAudioSamplesSync(frame->data, sampleFrameCount,
1747 &sampleFramesWritten);
1748 if (FAILED(res)) {
1749 log_msg(LOG_LEVEL_WARNING, MOD_NAME "WriteAudioSamplesSync failed.\n");
1750 return;
1751 }
1752 } else {
1753 s->delegate.ScheduleAudio(frame, &sampleFramesWritten,
1754 buffered == 0);
1755 }
1756 const bool overflow = sampleFramesWritten != sampleFrameCount;
1757 if (overflow || log_level >= LOG_LEVEL_DEBUG) {
1758 ostringstream details_oss;
1760 details_oss
1761 << " (" << sampleFramesWritten << " written, "
1762 << sampleFrameCount - sampleFramesWritten
1763 << " dropped, " << buffered << " buffer size)";
1764 }
1765 int level = overflow ? LOG_LEVEL_WARNING : LOG_LEVEL_DEBUG;
1766 LOG(level) << MOD_NAME << "audio buffer"
1767 << (overflow ? " overflow!" : "")
1768 << details_oss.str() << "\n";
1769 }
1770 s->audio_drift_fixer.update(buffered, sampleFrameCount, sampleFramesWritten);
1771}
1772
1774 int sample_rate) {
1775 struct state_decklink *s = (struct state_decklink *)state;
1776
1777 assert(s->play_audio);
1779 assert(quant_samples == 16 || quant_samples == 32);
1780
1781 const int bps = quant_samples / 8;
1782 const unique_lock<mutex> lk(s->audio_reconf_lock);
1783 if (bps != s->aud_desc.bps || sample_rate != s->aud_desc.sample_rate ||
1784 channels != s->aud_desc.ch_count) {
1786 AC_PCM};
1787 s->audio_reconfigure = true;
1789 << MOD_NAME << "Audio reconfigured to: " << s->aud_desc
1790 << "\n";
1791 }
1792
1793 return true;
1794}
1795
1796#ifndef _WIN32
1797static bool operator==(const REFIID & first, const REFIID & second){
1798 return (first.byte0 == second.byte0) &&
1799 (first.byte1 == second.byte1) &&
1800 (first.byte2 == second.byte2) &&
1801 (first.byte3 == second.byte3) &&
1802 (first.byte4 == second.byte4) &&
1803 (first.byte5 == second.byte5) &&
1804 (first.byte6 == second.byte6) &&
1805 (first.byte7 == second.byte7) &&
1806 (first.byte8 == second.byte8) &&
1807 (first.byte9 == second.byte9) &&
1808 (first.byte10 == second.byte10) &&
1809 (first.byte11 == second.byte11) &&
1810 (first.byte12 == second.byte12) &&
1811 (first.byte13 == second.byte13) &&
1812 (first.byte14 == second.byte14) &&
1813 (first.byte15 == second.byte15);
1814}
1815#endif
1816
1817HRESULT DeckLinkFrame::QueryInterface(REFIID iid, LPVOID *ppv)
1818{
1819#ifdef _WIN32
1820 IID iunknown = IID_IUnknown;
1821#else
1822 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
1823#endif
1824 HRESULT result = S_OK;
1825
1826 if (ppv == nullptr) {
1827 return E_INVALIDARG;
1828 }
1829
1830 // Initialise the return result
1831 *ppv = nullptr;
1832
1833 LOG(LOG_LEVEL_DEBUG) << MOD_NAME << "DeckLinkFrame QueryInterface " << iid << "\n";
1834 if (iid == iunknown) {
1835 *ppv = this;
1836 AddRef();
1837 } else if (iid == IID_IDeckLinkVideoFrame) {
1838 *ppv = static_cast<IDeckLinkVideoFrame*>(this);
1839 AddRef();
1840 } else if (iid == IID_IDeckLinkVideoFrameMetadataExtensions) {
1841 if (m_metadata.EOTF == static_cast<int64_t>(HDR_EOTF::NONE)) {
1842 result = E_NOINTERFACE;
1843 } else {
1844 *ppv = static_cast<IDeckLinkVideoFrameMetadataExtensions*>(this);
1845 AddRef();
1846 }
1847 } else {
1848 result = E_NOINTERFACE;
1849 }
1850
1851 return result;
1852
1853 return E_NOINTERFACE;
1854}
1855
1856ULONG DeckLinkFrame::AddRef()
1857{
1858 return ++ref;
1859}
1860
1861ULONG DeckLinkFrame::Release()
1862{
1863 ULONG ret = --ref;
1864 if (ret == 0) {
1865 lock_guard<mutex> lg(buffer_pool.lock);
1866 buffer_pool.frame_queue.push(this);
1867 }
1868 return ret;
1869}
1870
1871DeckLinkFrame::DeckLinkFrame(long w, long h, long rb, BMDPixelFormat pf, buffer_pool_t & bp, HDRMetadata const & hdr_metadata)
1872 : width(w), height(h), rawBytes(rb), pixelFormat(pf), data(new char[rb * h]), timecode(NULL),
1873 buffer_pool(bp)
1874{
1875 clear_video_buffer(reinterpret_cast<unsigned char *>(data.get()), rawBytes, rawBytes, height,
1876 pf == bmdFormat8BitYUV ? UYVY : (pf == bmdFormat10BitYUV ? v210 : RGBA));
1877 m_metadata = hdr_metadata;
1878}
1879
1880DeckLinkFrame *DeckLinkFrame::Create(long width, long height, long rawBytes, BMDPixelFormat pixelFormat, buffer_pool_t & buffer_pool, const HDRMetadata & hdr_metadata)
1881{
1882 return new DeckLinkFrame(width, height, rawBytes, pixelFormat, buffer_pool, hdr_metadata);
1883}
1884
1885DeckLinkFrame::~DeckLinkFrame()
1886{
1887}
1888
1889long DeckLinkFrame::GetWidth ()
1890{
1891 return width;
1892}
1893
1894long DeckLinkFrame::GetHeight ()
1895{
1896 return height;
1897}
1898
1899long DeckLinkFrame::GetRowBytes ()
1900{
1901 return rawBytes;
1902}
1903
1904BMDPixelFormat DeckLinkFrame::GetPixelFormat ()
1905{
1906 return pixelFormat;
1907}
1908
1909BMDFrameFlags DeckLinkFrame::GetFlags ()
1910{
1911 return m_metadata.EOTF == static_cast<int64_t>(HDR_EOTF::NONE) ? bmdFrameFlagDefault : bmdFrameContainsHDRMetadata;
1912}
1913
1914HRESULT DeckLinkFrame::GetBytes (/* out */ void **buffer)
1915{
1916 *buffer = static_cast<void *>(data.get());
1917 return S_OK;
1918}
1919
1920HRESULT DeckLinkFrame::GetTimecode (/* in */ BMDTimecodeFormat, /* out */ IDeckLinkTimecode **timecode)
1921{
1922 *timecode = dynamic_cast<IDeckLinkTimecode *>(this->timecode);
1923 return S_OK;
1924}
1925
1926HRESULT DeckLinkFrame::GetAncillaryData (/* out */ IDeckLinkVideoFrameAncillary **)
1927{
1928 return S_FALSE;
1929}
1930
1931/* IDeckLinkMutableVideoFrame */
1932HRESULT DeckLinkFrame::SetFlags (/* in */ BMDFrameFlags)
1933{
1934 return E_FAIL;
1935}
1936
1937HRESULT DeckLinkFrame::SetTimecode (/* in */ BMDTimecodeFormat, /* in */ IDeckLinkTimecode *timecode)
1938{
1939 if(this->timecode)
1940 this->timecode->Release();
1941 this->timecode = timecode;
1942 return S_OK;
1943}
1944
1945HRESULT DeckLinkFrame::SetTimecodeFromComponents (/* in */ BMDTimecodeFormat, /* in */ uint8_t, /* in */ uint8_t, /* in */ uint8_t, /* in */ uint8_t, /* in */ BMDTimecodeFlags)
1946{
1947 return E_FAIL;
1948}
1949
1950HRESULT DeckLinkFrame::SetAncillaryData (/* in */ IDeckLinkVideoFrameAncillary *)
1951{
1952 return E_FAIL;
1953}
1954
1955HRESULT DeckLinkFrame::SetTimecodeUserBits (/* in */ BMDTimecodeFormat, /* in */ BMDTimecodeUserBits)
1956{
1957 return E_FAIL;
1958}
1959
1960void HDRMetadata::Init(const string &fmt) {
1961 auto opts = unique_ptr<char []>(new char [fmt.size() + 1]);
1962 strcpy(opts.get(), fmt.c_str());
1963 char *save_ptr = nullptr;
1964 char *mode_c = strtok_r(opts.get(), ",", &save_ptr);
1965 assert(mode_c != nullptr);
1966 string mode = mode_c;
1967 std::for_each(std::begin(mode), std::end(mode), [](char& c) {
1968 c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
1969 });
1970 if (mode == "SDR"s) {
1971 EOTF = static_cast<int64_t>(HDR_EOTF::SDR);
1972 } else if (mode == "HDR"s) {
1973 EOTF = static_cast<int64_t>(HDR_EOTF::HDR);
1974 } else if (mode == "PQ"s) {
1975 EOTF = static_cast<int64_t>(HDR_EOTF::PQ);
1976 } else if (mode == "HLG"s) {
1977 EOTF = static_cast<int64_t>(HDR_EOTF::HLG);
1978 } else if (mode == "HELP"s) {
1979 cout << MOD_NAME << "HDR syntax:\n";
1980 cout << "\tHDR[=<eotf>|int[,{<k>=<v>}*]\n";
1981 cout << "\t\t<eotf> may be one of SDR, HDR, PQ, HLG or int 0-7\n";
1982 cout << "\t\tFurther options may be specification of HDR values, accepted keys are (values are floats):\n";
1983 cout << "\t\t\t- maxDisplayMasteringLuminance\n";
1984 cout << "\t\t\t- minDisplayMasteringLuminance\n";
1985 cout << "\t\t\t- maxCLL\n";
1986 cout << "\t\t\t- maxFALL\n";
1987 throw ug_no_error{};
1988 } else {
1989 EOTF = stoi(mode);
1990 if (EOTF < 0 || EOTF > 7) {
1991 throw out_of_range("Value outside [0..7]");
1992 }
1993 }
1994
1995 char *other_opt = nullptr;
1996 while ((other_opt = strtok_r(nullptr, ",", &save_ptr)) != nullptr) {
1997 if (strstr(other_opt, "maxDisplayMasteringLuminance=") != nullptr) {
1998 maxDisplayMasteringLuminance = stod(other_opt + strlen("maxDisplayMasteringLuminance="));
1999 } else if (strstr(other_opt, "minDisplayMasteringLuminance=") != nullptr) {
2000 minDisplayMasteringLuminance = stod(other_opt + strlen("minDisplayMasteringLuminance="));
2001 } else if (strstr(other_opt, "maxCLL=") != nullptr) {
2002 maxCLL = stod(other_opt + strlen("maxCLL="));
2003 } else if (strstr(other_opt, "maxFALL=") != nullptr) {
2004 maxFALL = stod(other_opt + strlen("maxFALL="));
2005 } else {
2006 throw invalid_argument("Unrecognized HDR attribute "s + other_opt);
2007 }
2008 }
2009}
2010
2011static inline void debug_print_metadata_id(const char *fn_name, BMDDeckLinkFrameMetadataID metadataID) {
2013 return;
2014 }
2015 array<char, sizeof metadataID + 1> fourcc{};
2016 copy(reinterpret_cast<char *>(&metadataID), reinterpret_cast<char *>(&metadataID) + sizeof metadataID, fourcc.data());
2017 LOG(LOG_LEVEL_DEBUG2) << MOD_NAME << "DecklLinkFrame " << fn_name << ": " << fourcc.data() << "\n";
2018}
2019
2020// IDeckLinkVideoFrameMetadataExtensions interface
2021HRESULT DeckLinkFrame::GetInt(BMDDeckLinkFrameMetadataID metadataID, int64_t* value)
2022{
2023 debug_print_metadata_id(static_cast<const char *>(__func__), metadataID);
2024 switch (metadataID)
2025 {
2026 case bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc:
2027 *value = m_metadata.EOTF;
2028 return S_OK;
2029 case bmdDeckLinkFrameMetadataColorspace:
2030 // Colorspace is fixed for this sample
2031 *value = bmdColorspaceRec2020;
2032 return S_OK;
2033 default:
2034 value = nullptr;
2035 return E_INVALIDARG;
2036 }
2037}
2038
2039HRESULT DeckLinkFrame::GetFloat(BMDDeckLinkFrameMetadataID metadataID, double* value)
2040{
2041 debug_print_metadata_id(static_cast<const char *>(__func__), metadataID);
2042 switch (metadataID)
2043 {
2044 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX:
2045 *value = m_metadata.referencePrimaries.RedX;
2046 return S_OK;
2047 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY:
2048 *value = m_metadata.referencePrimaries.RedY;
2049 return S_OK;
2050 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX:
2051 *value = m_metadata.referencePrimaries.GreenX;
2052 return S_OK;
2053 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY:
2054 *value = m_metadata.referencePrimaries.GreenY;
2055 return S_OK;
2056 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX:
2057 *value = m_metadata.referencePrimaries.BlueX;
2058 return S_OK;
2059 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY:
2060 *value = m_metadata.referencePrimaries.BlueY;
2061 return S_OK;
2062 case bmdDeckLinkFrameMetadataHDRWhitePointX:
2063 *value = m_metadata.referencePrimaries.WhiteX;
2064 return S_OK;
2065 case bmdDeckLinkFrameMetadataHDRWhitePointY:
2066 *value = m_metadata.referencePrimaries.WhiteY;
2067 return S_OK;
2068 case bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance:
2069 *value = m_metadata.maxDisplayMasteringLuminance;
2070 return S_OK;
2071 case bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance:
2072 *value = m_metadata.minDisplayMasteringLuminance;
2073 return S_OK;
2074 case bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel:
2075 *value = m_metadata.maxCLL;
2076 return S_OK;
2077 case bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel:
2078 *value = m_metadata.maxFALL;
2079 return S_OK;
2080 default:
2081 value = nullptr;
2082 return E_INVALIDARG;
2083 }
2084}
2085
2086HRESULT DeckLinkFrame::GetFlag(BMDDeckLinkFrameMetadataID metadataID, BMD_BOOL* value)
2087{
2088 debug_print_metadata_id(static_cast<const char *>(__func__), metadataID);
2089 // Not expecting GetFlag
2090 *value = BMD_TRUE;
2091 return E_INVALIDARG;
2092}
2093
2094HRESULT DeckLinkFrame::GetString(BMDDeckLinkFrameMetadataID metadataID, BMD_STR* value)
2095{
2096 debug_print_metadata_id(static_cast<const char *>(__func__), metadataID);
2097 // Not expecting GetString
2098 *value = nullptr;
2099 return E_INVALIDARG;
2100}
2101
2102HRESULT DeckLinkFrame::GetBytes(BMDDeckLinkFrameMetadataID metadataID, void* /* buffer */, uint32_t* bufferSize)
2103{
2104 debug_print_metadata_id(static_cast<const char *>(__func__), metadataID);
2105 *bufferSize = 0;
2106 return E_INVALIDARG;
2107}
2108
2109// 3D frame
2110DeckLink3DFrame::DeckLink3DFrame(long w, long h, long rb, BMDPixelFormat pf, buffer_pool_t & buffer_pool, HDRMetadata const & hdr_metadata)
2111 : DeckLinkFrame(w, h, rb, pf, buffer_pool, hdr_metadata), rightEye(DeckLinkFrame::Create(w, h, rb, pf, buffer_pool, hdr_metadata))
2112{
2113}
2114
2115DeckLink3DFrame *DeckLink3DFrame::Create(long width, long height, long rawBytes, BMDPixelFormat pixelFormat, buffer_pool_t & buffer_pool, HDRMetadata const & hdr_metadata)
2116{
2117 DeckLink3DFrame *frame = new DeckLink3DFrame(width, height, rawBytes, pixelFormat, buffer_pool, hdr_metadata);
2118 return frame;
2119}
2120
2121DeckLink3DFrame::~DeckLink3DFrame()
2122{
2123}
2124
2125ULONG DeckLink3DFrame::AddRef()
2126{
2127 return DeckLinkFrame::AddRef();
2128}
2129
2130ULONG DeckLink3DFrame::Release()
2131{
2132 return DeckLinkFrame::Release();
2133}
2134
2135HRESULT DeckLink3DFrame::QueryInterface(REFIID id, void **data)
2136{
2137 LOG(LOG_LEVEL_DEBUG) << MOD_NAME << "DecklLink3DFrame QueryInterface " << id << "\n";
2138 if(id == IID_IDeckLinkVideoFrame3DExtensions)
2139 {
2140 this->AddRef();
2141 *data = dynamic_cast<IDeckLinkVideoFrame3DExtensions *>(this);
2142 return S_OK;
2143 }
2144 return DeckLinkFrame::QueryInterface(id, data);
2145}
2146
2147BMDVideo3DPackingFormat DeckLink3DFrame::Get3DPackingFormat()
2148{
2149 return bmdVideo3DPackingLeftOnly;
2150}
2151
2152HRESULT DeckLink3DFrame::GetFrameForRightEye(IDeckLinkVideoFrame ** frame)
2153{
2154 *frame = rightEye.get();
2155 rightEye->AddRef();
2156 return S_OK;
2157}
2158
2159/* function from DeckLink SDK sample DeviceList */
2160static void
2161print_output_modes(IDeckLink *deckLink, const char *query_prop_fcc)
2162{
2163 IDeckLinkOutput* deckLinkOutput = NULL;
2164 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
2165 IDeckLinkDisplayMode* displayMode = NULL;
2166 HRESULT result;
2167 int displayModeNumber = 0;
2168
2169 // Query the DeckLink for its configuration interface
2170 result = deckLink->QueryInterface(IID_IDeckLinkOutput, (void**)&deckLinkOutput);
2171 if (result != S_OK)
2172 {
2173 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not obtain the IDeckLinkOutput interface - result = %08x\n", (int) result);
2174 if (result == E_NOINTERFACE) {
2175 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Device doesn't support video playback.\n");
2176 }
2177 goto bail;
2178 }
2179
2180 // Obtain an IDeckLinkDisplayModeIterator to enumerate the display modes supported on output
2181 result = deckLinkOutput->GetDisplayModeIterator(&displayModeIterator);
2182 if (result != S_OK)
2183 {
2184 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Could not obtain the video output display mode iterator - result = %08x\n", (int) result);
2185 goto bail;
2186 }
2187
2188 // List all supported output display modes
2189 printf("\tdisplay modes:\n");
2190 while (displayModeIterator->Next(&displayMode) == S_OK)
2191 {
2192 BMD_STR displayModeString = NULL;
2193
2194 result = displayMode->GetName(&displayModeString);
2195
2196 if (result == S_OK)
2197 {
2198 BMDTimeValue frameRateDuration;
2199 BMDTimeScale frameRateScale;
2200
2201 // Obtain the display mode's properties
2202 string flags_str = bmd_get_flags_str(displayMode->GetFlags());
2203 int modeWidth = displayMode->GetWidth();
2204 int modeHeight = displayMode->GetHeight();
2205 uint32_t field_dominance_n = ntohl(displayMode->GetFieldDominance());
2206 displayMode->GetFrameRate(&frameRateDuration, &frameRateScale);
2207 printf(
2208 "\t\t%2d) %-20s %d x %d \t %2.2f FPS %.4s, "
2209 "flags: %s\n",
2210 displayModeNumber,
2211 get_str_from_bmd_api_str(displayModeString).c_str(),
2212 modeWidth, modeHeight,
2213 (float) ((double) frameRateScale /
2214 (double) frameRateDuration),
2215 (char *) &field_dominance_n, flags_str.c_str());
2216 release_bmd_api_str(displayModeString);
2217 }
2218
2219 // Release the IDeckLinkDisplayMode object to prevent a leak
2220 displayMode->Release();
2221
2222 displayModeNumber++;
2223 }
2224 color_printf("\n\tsupported pixel formats:" TERM_BOLD);
2225 for (auto & c : uv_to_bmd_codec_map) {
2226 if (decklink_supports_codec(deckLinkOutput, c.second)) {
2227 printf(" %s", get_codec_name(c.first));
2228 }
2229 }
2231
2232 if (query_prop_fcc != nullptr) {
2233 IDeckLinkProfileAttributes *deckLinkAttributes = nullptr;
2234 if (deckLink->QueryInterface(IID_IDeckLinkProfileAttributes,
2235 (void **) &deckLinkAttributes) ==
2236 S_OK) {
2237 cout << "\n";
2238 print_bmd_attribute(deckLinkAttributes, query_prop_fcc);
2239 deckLinkAttributes->Release();
2240 } else {
2241 MSG(ERROR, "Could not query device attributes.\n\n");
2242 }
2243 }
2244
2245 color_printf("\n");
2246
2247bail:
2248 // Ensure that the interfaces we obtained are released to prevent a memory leak
2249 if (displayModeIterator != NULL)
2250 displayModeIterator->Release();
2251
2252 if (deckLinkOutput != NULL)
2253 deckLinkOutput->Release();
2254}
2255
2269
2271
2272/* vim: set expandtab sw=8: */
#define UNUSED(x)
void ** state
int void * val
struct video_desc desc
int int int sample_rate
int int channels
struct video_frame * frame
const char * fmt
int void size_t * len
const char unsigned int flags
int void(** deleter)(void *))
int property
int quant_samples
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
const char * get_codec_name(codec_t codec)
This file contains definition of some common audio types.
@ AC_PCM
Definition types.h:53
#define MOD_NAME
Definition audio.cpp:110
bool bmd_parse_audio_levels(const char *opt) noexcept(false)
TOREMOVE (implicit "aacl" option is preferred)
BMD_STR get_bmd_api_str_from_cstr(const char *cstr)
bool blackmagic_api_version_check()
string bmd_get_flags_str(BMDDisplayModeFlags flags)
uint32_t bmd_read_fourcc(const char *str)
void print_bmd_device_profiles(const char *line_prefix)
void release_bmd_api_str(BMD_STR string)
void print_bmd_attribute(IDeckLinkProfileAttributes *deckLinkAttributes, const char *query_prop_fcc)
string bmd_hresult_to_string(HRESULT res)
string bmd_get_device_name(IDeckLink *decklink)
void decklink_uninitialize(bool *com_initialized)
bool decklink_set_profile(IDeckLink *deckLink, bmd_option const &req_profile, bool stereo)
std::string get_str_from_bmd_api_str(BMD_STR string)
void r10k_full_to_limited(const char *in, char *out, size_t len)
bool bmd_check_stereo_profile(IDeckLink *deckLink)
#define RELEASE_IF_NOT_NULL(x)
std::vector< bmd_dev > bmd_get_sorted_devices(bool *com_initialized, bool verbose, bool natural_sort)
bool decklink_supports_codec(T *deckLink, BMDPixelFormat pf)
void print_decklink_version()
#define R10K_FULL_OPT
@ BMD_MAX_AUD_CH
#define BMD_BOOL
#define BMD_STR
#define BMD_NAT_SORT
static std::vector< std::pair< codec_t, BMDPixelFormat > > uv_to_bmd_codec_map
#define BMD_FALSE
#define BMD_TRUE
static void show_help()
Definition wasapi.cpp:193
void set_target_buffer(uint32_t target_buffer)
Set the target buffer object.
void set_max_hz(uint32_t max_hz)
Set the max hz object.
void update(uint32_t buffered_count, uint32_t sample_frame_count, uint32_t sampleFramesWritten)
void set_min_hz(uint32_t min_hz)
Set the min hz object.
void set_root(module *root)
Set the root object.
int color_printf(const char *format,...)
#define TERM_BOLD
Definition color_out.h:48
#define SRED(x)
Definition color_out.h:85
#define TERM_RESET
Definition color_out.h:47
#define TGREEN(x)
Definition color_out.h:72
#define SBOLD(x)
Definition color_out.h:80
#define TBOLD(x)
Definition color_out.h:70
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
Definition debug.cpp:379
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_DEBUG2
Definition debug.h:67
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG(level)
Definition debug.h:313
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
Definition debug.h:92
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define PITCH_DEFAULT
default pitch, i. e. respective linesize
#define DISPLAY_FLAG_AUDIO_ANALOG
#define DISPLAY_FLAG_AUDIO_AESEBU
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_FLAG_AUDIO_ANY
const char * get_audio_conn_flag_name(int audio_init_flag)
#define DISPLAY_FLAG_AUDIO_EMBEDDED
@ DISPLAY_PROPERTY_BUF_PITCH
requested framebuffer pitch - int (bytes), may be PITCH_DEFAULT
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_SUPPORTED_IL_MODES
display supported interlacing modes - enum interlacing_t[]
@ DISPLAY_PROPERTY_VIDEO_MODE
requested video mode - int (one of display_prop_vid_mode)
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
@ DISPLAY_PROPERTY_AUDIO_FORMAT
@ DISPLAY_PROPERTY_VIDEO_SEPARATE_3D
same as MERGED but 2 streams should be handled as separate 3D tiles
struct module * get_root_module(struct module *node)
Definition module.c:203
int count
Definition hd-rum.c:44
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define INIT_NOERR
Definition host.h:73
static int settings_init(struct state_jack *s, char *cfg)
Definition jack.c:186
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
volatile int log_level
Definition debug.cpp:65
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
Definition macros.h:138
#define IS_PREFIX(tok, key)
similar as above, but also key without a value is accepted (value optional)
Definition macros.h:142
@ STR_LEN
"standard" string length placeholder
Definition macros.h:134
unsigned long long next_power_of_two(unsigned long long x)
Definition math.c:79
bool invalid_arg_is_numeric(const char *what)
Definition misc.cpp:340
uint32_t parse_uint32(const char *value_str) noexcept(false)
Definition misc.cpp:296
enum openssl_mode mode
codec_t first
Definition sdl2.c:423
uint32_t second
Definition sdl2.c:424
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
bool is_prefix_of(const char *haystack, const char *needle)
Definition string.c:91
struct used to comunicate with audio capture/playback device
Definition types.h:99
void set_int(int64_t val_)
void parse(const char *)
bool keep() const
bool device_write(IDeckLinkConfiguration *deckLinkConfiguration, BMDDeckLinkConfigurationID opt, std::string const &log_prefix="[DeckLink] ") const
void set_flag(bool val_)
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
void(* dispose)(struct video_frame *)
Definition types.h:245
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
int64_t timestamp
Definition types.h:331
double fps
Definition types.h:308
unsigned int tile_count
Definition types.h:344
struct video_frame_callbacks callbacks
Definition types.h:324
codec_t color_spec
Definition types.h:306
#define NS_IN_SEC_DBL
Definition tv.h:93
static time_ns_t get_time_in_ns()
Definition tv.h:101
void dev_add_option(struct device_info *dev, const char *name, const char *desc, const char *key, const char *opt_str, bool is_boolean)
interlacing_t
Definition types.h:163
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ SEGMENTED_FRAME
Segmented frame. Contains the same data as progressive frame.
Definition types.h:168
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
Definition types.h:167
codec_t
Definition types.h:65
@ R10k
RGB 10-bit packed - RGBX, big-endian (2 bit padding)
Definition types.h:73
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
Definition types.h:75
This is an umbrella header for video functions.
bool codec_is_a_rgb(codec_t codec)
Returns TRUE if specified pixelformat is some form of RGB (not YUV).
bool clear_video_buffer(unsigned char *data, size_t linesize, size_t pitch, size_t height, codec_t color_spec)
static struct @76 codecs[]
@ FAILED