47#include <DeckLinkAPIVersion.h>
72#define DECKLINK_MAGIC to_fourcc('v', 'd', 'D', 'L')
73#define MOD_NAME "[DeckLink display] "
94#define STDMETHODCALLTYPE
97#define RELEASE_IF_NOT_NULL(x) if ((x) != nullptr) { (x)->Release(); (x) = nullptr; }
104#define EXIT_IF_FAILED(cmd, name) \
106 const HRESULT result = cmd;\
107 if (FAILED(result)) {;\
108 LOG(LOG_LEVEL_ERROR) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
114#define CALL_AND_CHECK(cmd, name) \
116 const HRESULT result = cmd;\
117 if (FAILED(result)) {;\
118 LOG(LOG_LEVEL_WARNING) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
122using hrc = std::chrono::high_resolution_clock;
123using namespace std::string_literals;
124using std::chrono::duration_cast;
125using std::chrono::seconds;
128using std::atomic_bool;
133using std::invalid_argument;
134using std::lock_guard;
137using std::ostringstream;
138using std::out_of_range;
143using std::unique_lock;
144using std::unique_ptr;
153class PlaybackDelegate :
public IDeckLinkVideoOutputCallback
157 int64_t saved_sync_ts = INT64_MIN;
158 int64_t last_sync_ts = INT64_MIN;
161 enum audio_sync_val : int64_t {
163 resync = INT64_MIN + 1,
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;
171 IDeckLinkOutput *m_deckLinkOutput{};
173 queue<DeckLinkFrame *> schedFrames{};
174 DeckLinkFrame *lastSchedFrame{};
176 atomic<int64_t> m_audio_sync_ts = audio_sync_val::deinit;
177 struct audio_vals m_adata;
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{};
187 DEFAULT_MIN_SCHED_FRAMES = 4,
188 DEFAULT_MAX_SCHED_FRAMES =
189 DEFAULT_MIN_SCHED_FRAMES + SCHED_RANGE,
190 MAX_UNPROC_QUEUE_SIZE = 10,
193 void SetDecklinkOutput(IDeckLinkOutput *ido) { m_deckLinkOutput = ido; }
194 void SetFrameTimes(BMDTimeValue fd, BMDTimeScale fs)
196 frameRateDuration = fd;
199 void SetSynchronized(
const char *cfg);
202 void ResetAudio() { m_audio_sync_ts = audio_sync_val::deinit; }
204 virtual ~PlaybackDelegate() {
213 return E_NOINTERFACE;
218 bool EnqueueFrame(DeckLinkFrame *deckLinkFrame);
219 void ScheduleNextFrame();
224 ScheduledFrameCompleted(IDeckLinkVideoFrame *completedFrame,
225 BMDOutputFrameCompletionResult result)
override
227 if (result == bmdOutputFrameDisplayedLate){
230 }
else if (result == bmdOutputFrameDropped){
233 }
else if (result == bmdOutputFrameFlushed){
239 IDeckLinkTimecode *timecode = NULL;
240 if (completedFrame->GetTimecode ((BMDTimecodeFormat) 0, &timecode) == S_OK) {
242 if (timecode && timecode->GetString(&timecode_str) == S_OK) {
258 completedFrame->Release();
269void PlaybackDelegate::PrintStats()
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";
281struct buffer_pool_t {
282 queue<DeckLinkFrame *> frame_queue;
286class DeckLinkTimecode :
public IDeckLinkTimecode{
287 BMDTimecodeBCD timecode;
289 DeckLinkTimecode() : timecode(0) {}
290 virtual ~DeckLinkTimecode() =
default;
293 virtual HRESULT
STDMETHODCALLTYPE GetComponents ( uint8_t *hours, uint8_t *minutes, uint8_t *seconds, 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;
301 uint8_t hours, minutes, seconds, frames;
302 GetComponents(&hours, &minutes, &seconds, &frames);
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);
307 return *timecode ? S_OK : E_FAIL;
309 virtual BMDTimecodeFlags
STDMETHODCALLTYPE GetFlags (
void) {
return bmdTimecodeFlagDefault; }
310 virtual HRESULT
STDMETHODCALLTYPE GetTimecodeUserBits ( BMDTimecodeUserBits *userBits) {
if (!userBits)
return E_POINTER;
else return S_OK; }
313 virtual HRESULT
STDMETHODCALLTYPE QueryInterface (REFIID , LPVOID *) {
return E_NOINTERFACE;}
317 void STDMETHODCALLTYPE SetBCD(BMDTimecodeBCD timecode) { this->timecode = timecode; }
320struct ChromaticityCoordinates
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 };
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};
348 void Init(
const string &
fmt);
351class DeckLinkFrame :
public IDeckLinkMutableVideoFrame,
public IDeckLinkVideoFrameMetadataExtensions
356 BMDPixelFormat pixelFormat;
357 unique_ptr<char []> data;
359 IDeckLinkTimecode *timecode;
361 atomic<ULONG> ref = 1;
363 buffer_pool_t &buffer_pool;
364 struct HDRMetadata m_metadata;
366 DeckLinkFrame(
long w,
long h,
long rb, BMDPixelFormat pf, buffer_pool_t & bp, HDRMetadata
const & hdr_metadata);
369 virtual ~DeckLinkFrame();
370 static DeckLinkFrame *Create(
long width,
long height,
long rawBytes, BMDPixelFormat pixelFormat, buffer_pool_t & buffer_pool, HDRMetadata
const & hdr_metadata);
384 HRESULT
STDMETHODCALLTYPE GetTimecode ( BMDTimecodeFormat format, IDeckLinkTimecode **timecode)
override;
385 HRESULT
STDMETHODCALLTYPE GetAncillaryData ( IDeckLinkVideoFrameAncillary **ancillary)
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;
395 HRESULT
STDMETHODCALLTYPE GetInt(BMDDeckLinkFrameMetadataID metadataID, int64_t* value)
override;
396 HRESULT
STDMETHODCALLTYPE GetFloat(BMDDeckLinkFrameMetadataID metadataID,
double* value)
override;
399 HRESULT
STDMETHODCALLTYPE GetBytes(BMDDeckLinkFrameMetadataID metadataID,
void* buffer, uint32_t* bufferSize)
override;
401 int64_t timestamp = INT64_MIN;
404class DeckLink3DFrame :
public DeckLinkFrame,
public IDeckLinkVideoFrame3DExtensions
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;
413 static DeckLink3DFrame *Create(
long width,
long height,
long rawBytes, BMDPixelFormat pixelFormat, buffer_pool_t & buffer_pool, HDRMetadata
const & hdr_metadata);
425void PlaybackDelegate::Reset()
428 while (!schedFrames.empty()) {
429 schedFrames.front()->Release();
435bool PlaybackDelegate::EnqueueFrame(DeckLinkFrame *
deckLinkFrame)
437 const unique_lock<mutex> lk(schedLock);
438 const unsigned buffered = schedFrames.size();
439 if (buffered < MAX_UNPROC_QUEUE_SIZE) {
446 <<
MOD_NAME "Queue overflow, buffered: " << buffered
447 <<
". This should not happen!\n";
448 m_audio_sync_ts = audio_sync_val::resync;
452void PlaybackDelegate::ScheduleNextFrame()
455 m_deckLinkOutput->GetBufferedVideoFrameCount(&i);
458 const unique_lock<mutex> lk(schedLock);
459 while (!schedFrames.empty()) {
460 DeckLinkFrame *f = schedFrames.front();
462 if (++i > m_max_sched_frames) {
465 <<
MOD_NAME "Dismissed frame, buffered: " << i
472 lastSchedFrame->AddRef();
473 if (m_audio_sync_ts <= audio_sync_val::resync &&
474 f->timestamp != INT64_MIN) {
483 m_deckLinkOutput->ScheduleVideoFrame(
489 if (i >= m_min_sched_frames || lastSchedFrame ==
nullptr) {
492 for (; i < (m_min_sched_frames + m_max_sched_frames + 1) / 2; ++i) {
494 m_audio_sync_ts = audio_sync_val::resync;
495 m_deckLinkOutput->ScheduleVideoFrame(
531 { bmdDeckLinkConfigOutput1080pAsPsF,
bmd_option{
false,
false}},
532 { bmdDeckLinkConfigFieldFlickerRemoval,
bmd_option{
false,
false}},
533 { bmdDeckLinkConfigLowLatencyVideoOutput,
bmd_option{
true,
false}}
550show_help(
bool full,
const char *query_prop_fcc =
nullptr)
554 col() <<
"DeckLink display options:\n";
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>"
561 col() <<
"\nOptions:\n";
563 col() <<
SBOLD(
"\tfullhelp") <<
"\tdisplay additional options and more details\n";
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";
575 col() <<
SBOLD(
"\t<option_FourCC>=<value>")
576 <<
"\tarbitrary BMD option (given a FourCC) and corresponding "
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";
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";
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";
625 col() <<
"\nRecognized pixel formats:";
629 col() <<
"Devices (idx, topological ID, name):\n";
631 bool com_initialized =
false;
634 const bool natural_sort =
638 IDeckLink *deckLink = get<0>(d).get();
640 if (deviceName.empty()) {
641 deviceName =
"(unable to get name)";
646 snprintf(index,
sizeof index,
647 TBOLD(
"%c")
") ", get<char>(d));
649 if (full || natural_sort) {
650 snprintf(index + strlen(index),
651 sizeof index - strlen(index),
TBOLD(
"%d")
") ",
657 index, get<unsigned>(d),
668 col() <<
"(use \"" <<
SBOLD(
"fullhelp") <<
"\" to see device modes)\n";
693 ? DeckLink3DFrame::Create(
696 : DeckLinkFrame::Create(
710 MSG(FATAL,
"Cannot reinit after audio reconf!\n");
723 DeckLinkFrame *deckLinkFrame =
nullptr;
728 DeckLinkFrame *
frame =
729 s->
stereo ?
dynamic_cast<DeckLink3DFrame *
>(tmp)
730 :
dynamic_cast<DeckLinkFrame *
>(tmp);
738 deckLinkFrame =
frame;
739 deckLinkFrame->AddRef();
743 if (!deckLinkFrame) {
748 deckLinkFrame->GetBytes((
void **)&out->
tiles[0].
data);
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);
757 deckLinkFrameRight->Release();
765 const float epsilon = 0.005;
766 uint8_t hours, minutes, seconds, frames;
768 bool dropFrame =
false;
770 if(ceil(
fps) -
fps > epsilon) {
774 tc->GetComponents (&hours, &minutes, &seconds, &frames);
777 if((
double) frames >
fps - epsilon) {
784 if(minutes % 10 != 0)
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;
804 [[maybe_unused]]
long long timeout_ns)
845{ IDeckLinkDisplayModeIterator *displayModeIterator;
846 IDeckLinkDisplayMode* deckLinkDisplayMode;
847 BMDDisplayMode displayMode = bmdModeUnknown;
853 return bmdModeUnknown;
856 while (displayModeIterator->Next(&deckLinkDisplayMode) == S_OK)
859 if (deckLinkDisplayMode->GetName(&modeNameString) == S_OK)
861 if (deckLinkDisplayMode->GetWidth() == (
long)
desc.
width &&
862 deckLinkDisplayMode->GetHeight() == (
long)
desc.
height)
865 BMDFieldDominance dominance;
868 dominance = deckLinkDisplayMode->GetFieldDominance();
869 if (dominance == bmdLowerFieldFirst ||
870 dominance == bmdUpperFieldFirst) {
871 if (dominance == bmdLowerFieldFirst) {
874 "Lower field first format "
875 "detected, fields can be "
887 MSG(INFO,
"Selected mode: %s%s\n",
892 displayMode = deckLinkDisplayMode->GetDisplayMode();
894 deckLinkDisplayMode->Release();
900 deckLinkDisplayMode->Release();
902 displayModeIterator->Release();
909 const BMDAudioSampleType sample_type =
910 bps == 2 ? bmdAudioSampleType16bitInteger
911 : bmdAudioSampleType32bitInteger;
912 const BMDAudioOutputStreamType stream_type =
914 : bmdAudioOutputStreamTimestamped;
917 bmdAudioSampleRate48kHz, sample_type,
channels, stream_type),
918 "EnableAudioOutput");
928 BMDDisplayMode displayMode;
932 const unique_lock<mutex> lk(s->audio_reconf_lock);
933 s->delegate.ResetAudio();
937 if (s->initialized) {
938 if (!s->low_latency) {
941 "StopScheduledPlayback");
943 s->deckLinkOutput->SetScheduledFrameCompletionCallback(
nullptr);
945 "DisableVideoOutput");
948 "DisableAudioOutput");
950 s->initialized =
false;
962 [&
desc](
const std::pair<codec_t, BMDPixelFormat>& el){ return el.first == desc.color_spec; });
967 s->pixelFormat = it->second;
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;
990 BMDVideoOutputFlags outputFlags = bmdVideoOutputFlagDefault;
991 BMDSupportedVideoModeFlags supportedFlags = bmdSupportedVideoModeDefault;
997 if (displayMode == bmdModeUnknown) {
1003 if (s->emit_timecode) {
1004 outputFlags = (BMDVideoOutputFlags)(outputFlags | bmdVideoOutputRP188);
1008 outputFlags = (BMDVideoOutputFlags)(outputFlags | bmdVideoOutputDualStream3D);
1009 supportedFlags = (BMDSupportedVideoModeFlags)(supportedFlags |
1010 bmdSupportedVideoModeDualStream3D);
1015 subsampling_444.
device_write(s->deckLinkConfiguration, bmdDeckLinkConfig444SDIVideoOutput,
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;
1024 bmdDeckLinkConfigSDIOutputLinkConfiguration,
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)) {
1041 "profile may not be supported!\n";
1045 if (s->deckLinkAttributes->GetFlag(BMDDeckLinkSupportsQuadLinkSDI, &quad_link_supp) ==
1048 s->quad_square_division_split.device_write(
1049 s->deckLinkConfiguration,
1050 bmdDeckLinkConfigQuadLinkSDIVideoOutputSquareDivisionSplit,
MOD_NAME);
1053 const BMDVideoOutputConversionMode conversion_mode =
1054 s->device_options.find(bmdDeckLinkConfigVideoOutputConversionMode) !=
1055 s->device_options.end()
1056 ? (BMDVideoOutputConversionMode)s->device_options
1057 .at(bmdDeckLinkConfigVideoOutputConversionMode)
1059 : (BMDVideoOutputConversionMode)bmdNoVideoOutputConversion;
1061 bmdVideoConnectionUnspecified, displayMode, s->pixelFormat,
1062 conversion_mode, supportedFlags,
nullptr, &supported),
1063 "DoesSupportVideoMode");
1067 "combination not supported - %d * %dx%d@%f, timecode %s.\n",
1069 (outputFlags & bmdVideoOutputRP188 ?
"ON" :
"OFF"));
1073 result = s->deckLinkOutput->EnableVideoOutput(displayMode, outputFlags);
1075 if (result == E_ACCESSDENIED) {
1077 MOD_NAME "Unable to access the hardware or output "
1078 "stream currently active (another application "
1088 if (s->play_audio) {
1089 if (!
enable_audio(s, s->aud_desc.bps, s->aud_desc.ch_count)) {
1094 if (!s->low_latency) {
1096 s->deckLinkOutput->SetScheduledFrameCompletionCallback(
1098 if (!s->delegate.Preroll(s)) {
1103 s->initialized =
true;
1104 s->audio_reconfigure =
false;
1112 for (
unsigned i = 0;
1113 i < (m_min_sched_frames + m_min_sched_frames + 1) / 2; ++i) {
1115 const bool ret = EnqueueFrame(f);
1117 ScheduleNextFrame();
1121 m_deckLinkOutput->StartScheduledPlayback(0, frameRateScale, 1.0);
1124 <<
MOD_NAME <<
"StartScheduledPlayback (video): "
1126 m_deckLinkOutput->DisableVideoOutput();
1137 *available_cards =
nullptr;
1139 bool com_initialized =
false;
1142 IDeckLink *deckLink = get<0>(d).get();
1144 if (deviceName.empty()) {
1145 deviceName =
"(unknown)";
1152 snprintf((*available_cards)[*
count - 1].dev,
1153 sizeof(*available_cards)[*
count - 1].dev,
":device=%6x",
1155 snprintf((*available_cards)[*
count - 1].extra,
sizeof (*available_cards)[*
count - 1].extra,
"\"embeddedAudioAvailable\":\"t\"");
1156 (*available_cards)[*
count - 1].repeatable =
false;
1158 strncpy((*available_cards)[*
count - 1].
name, deviceName.c_str(),
1159 sizeof (*available_cards)[*
count - 1].
name - 1);
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);
1170PlaybackDelegate::SetSynchronized(
const char *cfg)
1172 if (cfg ==
nullptr) {
1176 m_min_sched_frames = stoi(cfg);
1177 if (strchr(cfg,
',') !=
nullptr) {
1178 m_max_sched_frames = stoi(strchr(cfg,
',') + 1);
1180 m_max_sched_frames =
1181 m_min_sched_frames + SCHED_RANGE;
1183 assert(m_max_sched_frames > m_min_sched_frames);
1190 MSG(WARNING,
"Low latency option unneeded (implicit).\n");
1194 MSG(WARNING,
"Deprecated, do not use - "
1195 "see option \"synchroninzed\" instead.\n");
1201 if (strlen(
fmt) == 0) {
1205 auto tmp =
static_cast<char *
>(alloca(strlen(
fmt) + 1));
1208 char *save_ptr =
nullptr;
1210 ptr = strtok_r(tmp,
":", &save_ptr);
1211 assert(ptr !=
nullptr);
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;
1221 if (first_option_is_device) {
1223 "deprecated. Use \"device=%s\" instead.\n", ptr);
1225 ptr = strtok_r(
nullptr,
":", &save_ptr);
1228 while (ptr !=
nullptr) {
1229 const char *
val = strchr(ptr,
'=') + 1;
1231 cardId = strchr(ptr,
'=') + 1;
1234 }
else if (strcasecmp(ptr,
"3D") == 0) {
1236 }
else if (strcasecmp(ptr,
"timecode") == 0) {
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) {
1246 }
else if (strcasecmp(ptr,
"half-duplex") == 0) {
1248 }
else if (strcasecmp(ptr,
"simplex") == 0) {
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);
1272 }
else if (strncasecmp(ptr,
"audio_level=", strlen(
"audio_level=")) == 0) {
1274 [bmdDeckLinkConfigAnalogAudioConsumerLevels] =
1277 s->
device_options[bmdDeckLinkConfigVideoOutputConversionMode].parse(strchr(ptr,
'=') + 1);
1279 s->
device_options[bmdDeckLinkConfigOutput1080pAsPsF].parse(strchr(ptr,
'=') + 1);
1280 if (strncasecmp(ptr,
"Use1080pNotPsF", strlen(
"Use1080pNotPsF")) == 0) {
1283 }
else if (strcasecmp(ptr,
"low-latency") == 0 || strcasecmp(ptr,
"no-low-latency") == 0) {
1285 }
else if (
IS_PREFIX(ptr,
"synchronized")) {
1287 s->
delegate.SetSynchronized(strchr(ptr,
'='));
1288 }
else if (strcasecmp(ptr,
"quad-square") == 0 || strcasecmp(ptr,
"no-quad-square") == 0) {
1290 }
else if (strncasecmp(ptr,
"hdr", strlen(
"hdr")) == 0) {
1292 if (strncasecmp(ptr,
"hdr=", strlen(
"hdr=")) == 0) {
1297 }
catch (exception
const &e) {
1302 }
else if (strstr(ptr,
"keep-settings") == ptr) {
1304 }
else if (strstr(ptr,
"drift_fix") == ptr) {
1306 }
else if (strncasecmp(ptr,
"maxresample=", strlen(
"maxresample=")) == 0) {
1308 }
else if (strncasecmp(ptr,
"minresample=", strlen(
"minresample=")) == 0) {
1310 }
else if (strncasecmp(ptr,
"targetbuffer=", strlen(
"targetbuffer=")) == 0) {
1312 }
else if ((strchr(ptr,
'=') !=
nullptr && strchr(ptr,
'=') - ptr == 4) || strlen(ptr) == 4) {
1318 ptr = strtok_r(
nullptr,
":", &save_ptr);
1325#define CHECK_CH_COUNT(channels) \
1326 assert((channels) >= 2 && (channels) != 4 && (channels) <= 64 && \
1327 is_power_of_two((channels)))
1331 IDeckLinkConfiguration *deckLinkConfiguration,
1332 unsigned int audio_output)
1334 if (audio_output == 0U) {
1340 int64_t max_aud_chans = 0;
1342 BMDDeckLinkMaximumAudioChannels, &max_aud_chans);
1343 if (result != S_OK) {
1344 MSG(WARNING,
"Cannot get maximum audio channels: %s\n",
1352 MSG(INFO,
"Using audio output: %s\n",
1357 BMDAudioOutputAnalogAESSwitch audioConnection{};
1358 switch (audio_output) {
1362 audioConnection = bmdAudioOutputSwitchAESEBU;
1365 audioConnection = bmdAudioOutputSwitchAnalog;
1368 MSG(ERROR,
"Unsupporetd audio connection: %d.\n", audio_output);
1374 result = deckLinkConfiguration->SetInt(
1375 bmdDeckLinkConfigAudioOutputAESAnalogSwitch, audioConnection);
1376 if (result == S_OK) {
1377 MSG(INFO,
"Card with switchable audio channels detected. "
1378 "Switched to correct format.\n");
1379 }
else if (result == E_NOTIMPL) {
1382 "Not setting switchable AESEBU/analog - not supported.\n");
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",
1396 IDeckLinkConfiguration* deckLinkConfiguration = NULL;
1399 if (strcmp(
fmt,
"help") == 0 || strcmp(
fmt,
"fullhelp") == 0) {
1415 bool succeeded =
false;
1418 }
catch (exception &e) {
1420 MSG(ERROR,
"Invalid number passed where numeric "
1421 "argument expected!\n");
1423 MSG(ERROR,
"%s\n", e.what());
1436 if (!deviceName.empty() && deviceName == cardId) {
1440 const unsigned long tid = strtoul(cardId.c_str(),
nullptr, 16);
1441 if (get<unsigned>(d) == tid) {
1445 if (isdigit(cardId.c_str()[0])) {
1446 if (atoi(cardId.c_str()) == get<int>(d)) {
1451 if (cardId.length() == 1 && cardId[0] >=
'a') {
1452 if (cardId[0] == get<char>(d)) {
1467 if (!deviceName.empty()) {
1472 result = s->
deckLink->QueryInterface(
1473 IID_IDeckLinkProfileAttributes,
1475 if (result != S_OK) {
1477 <<
MOD_NAME "Could not query device attributes: "
1484 s->
timecode =
new DeckLinkTimecode;
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",
1508 result = s->
deckLink->QueryInterface(IID_IDeckLinkConfiguration,
1509 reinterpret_cast<void **
>(&deckLinkConfiguration));
1511 if (result != S_OK) {
1513 "Could not obtain the IDeckLinkConfiguration interface: %08x\n",
1523 if (!o.second.device_write(deckLinkConfiguration, o.first,
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";
1542 <<
"HDR requested, but card doesn't support that.\n";
1550 &rec2020_supp) != S_OK) {
1552 <<
MOD_NAME <<
"Cannot check Rec. 2020 color space metadata support.\n";
1557 <<
"Rec. 2020 color space metadata not supported.\n";
1584 "StopScheduledPlayback");
1590 "DisableAudiioOutput");
1593 "DisableVideoOutput");
1617 int rgb_shift[] = {16, 8, 0};
1636 if(
sizeof(rgb_shift) > *
len) {
1639 memcpy(
val, rgb_shift,
sizeof(rgb_shift));
1640 *
len =
sizeof(rgb_shift);
1651 if(
sizeof(supported_il_modes) <= *
len) {
1652 memcpy(
val, supported_il_modes,
sizeof(supported_il_modes));
1656 *
len =
sizeof(supported_il_modes);
1662 desc->sample_rate = 48000;
1665 }
else if (
desc->ch_count <= 2) {
1667 }
else if (
desc->ch_count <= 8) {
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) {
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;
1700 m_adata.last_sync_ts -= (1LLU << 32);
1702 BMDTimeValue streamTime =
1704 bmdAudioSampleRate48kHz / 90000;
1707 <<
"; samples: " << *samples
1709 <<
"; sync TS: " << m_audio_sync_ts <<
"\n";
1710 const HRESULT res = m_deckLinkOutput->ScheduleAudioSamples(
1711 frame->data, *samples, streamTime, bmdAudioSampleRate48kHz,
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);
1735 const unsigned int sampleFrameCount =
1737 uint32_t sampleFramesWritten = sampleFrameCount;
1739 uint32_t buffered = 0;
1741 if (buffered == 0) {
1747 &sampleFramesWritten);
1756 const bool overflow = sampleFramesWritten != sampleFrameCount;
1758 ostringstream details_oss;
1761 <<
" (" << sampleFramesWritten <<
" written, "
1762 << sampleFrameCount - sampleFramesWritten
1763 <<
" dropped, " << buffered <<
" buffer size)";
1767 << (overflow ?
" overflow!" :
"")
1768 << details_oss.str() <<
"\n";
1817HRESULT DeckLinkFrame::QueryInterface(REFIID iid, LPVOID *ppv)
1820 IID iunknown = IID_IUnknown;
1822 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
1824 HRESULT result = S_OK;
1826 if (ppv ==
nullptr) {
1827 return E_INVALIDARG;
1834 if (iid == iunknown) {
1837 }
else if (iid == IID_IDeckLinkVideoFrame) {
1838 *ppv =
static_cast<IDeckLinkVideoFrame*
>(
this);
1840 }
else if (iid == IID_IDeckLinkVideoFrameMetadataExtensions) {
1841 if (m_metadata.EOTF ==
static_cast<int64_t
>(HDR_EOTF::NONE)) {
1842 result = E_NOINTERFACE;
1844 *ppv =
static_cast<IDeckLinkVideoFrameMetadataExtensions*
>(
this);
1848 result = E_NOINTERFACE;
1853 return E_NOINTERFACE;
1856ULONG DeckLinkFrame::AddRef()
1861ULONG DeckLinkFrame::Release()
1865 lock_guard<mutex> lg(buffer_pool.lock);
1866 buffer_pool.frame_queue.push(
this);
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),
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;
1880DeckLinkFrame *DeckLinkFrame::Create(
long width,
long height,
long rawBytes, BMDPixelFormat
pixelFormat, buffer_pool_t &
buffer_pool,
const HDRMetadata & hdr_metadata)
1882 return new DeckLinkFrame(width, height, rawBytes, pixelFormat, buffer_pool, hdr_metadata);
1885DeckLinkFrame::~DeckLinkFrame()
1889long DeckLinkFrame::GetWidth ()
1894long DeckLinkFrame::GetHeight ()
1899long DeckLinkFrame::GetRowBytes ()
1904BMDPixelFormat DeckLinkFrame::GetPixelFormat ()
1909BMDFrameFlags DeckLinkFrame::GetFlags ()
1911 return m_metadata.EOTF ==
static_cast<int64_t
>(HDR_EOTF::NONE) ? bmdFrameFlagDefault : bmdFrameContainsHDRMetadata;
1914HRESULT DeckLinkFrame::GetBytes (
void **buffer)
1916 *buffer =
static_cast<void *
>(data.get());
1920HRESULT DeckLinkFrame::GetTimecode ( BMDTimecodeFormat, IDeckLinkTimecode **timecode)
1922 *timecode =
dynamic_cast<IDeckLinkTimecode *
>(this->timecode);
1926HRESULT DeckLinkFrame::GetAncillaryData ( IDeckLinkVideoFrameAncillary **)
1932HRESULT DeckLinkFrame::SetFlags ( BMDFrameFlags)
1937HRESULT DeckLinkFrame::SetTimecode ( BMDTimecodeFormat, IDeckLinkTimecode *timecode)
1940 this->timecode->Release();
1941 this->timecode = timecode;
1945HRESULT DeckLinkFrame::SetTimecodeFromComponents ( BMDTimecodeFormat, uint8_t, uint8_t, uint8_t, uint8_t, BMDTimecodeFlags)
1950HRESULT DeckLinkFrame::SetAncillaryData ( IDeckLinkVideoFrameAncillary *)
1955HRESULT DeckLinkFrame::SetTimecodeUserBits ( BMDTimecodeFormat, BMDTimecodeUserBits)
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)));
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";
1990 if (EOTF < 0 || EOTF > 7) {
1991 throw out_of_range(
"Value outside [0..7]");
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="));
2006 throw invalid_argument(
"Unrecognized HDR attribute "s + other_opt);
2015 array<char, sizeof metadataID + 1> fourcc{};
2016 copy(
reinterpret_cast<char *
>(&metadataID),
reinterpret_cast<char *
>(&metadataID) +
sizeof metadataID, fourcc.data());
2021HRESULT DeckLinkFrame::GetInt(BMDDeckLinkFrameMetadataID metadataID, int64_t* value)
2026 case bmdDeckLinkFrameMetadataHDRElectroOpticalTransferFunc:
2027 *value = m_metadata.EOTF;
2029 case bmdDeckLinkFrameMetadataColorspace:
2031 *value = bmdColorspaceRec2020;
2035 return E_INVALIDARG;
2039HRESULT DeckLinkFrame::GetFloat(BMDDeckLinkFrameMetadataID metadataID,
double* value)
2044 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedX:
2045 *value = m_metadata.referencePrimaries.RedX;
2047 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesRedY:
2048 *value = m_metadata.referencePrimaries.RedY;
2050 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenX:
2051 *value = m_metadata.referencePrimaries.GreenX;
2053 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesGreenY:
2054 *value = m_metadata.referencePrimaries.GreenY;
2056 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueX:
2057 *value = m_metadata.referencePrimaries.BlueX;
2059 case bmdDeckLinkFrameMetadataHDRDisplayPrimariesBlueY:
2060 *value = m_metadata.referencePrimaries.BlueY;
2062 case bmdDeckLinkFrameMetadataHDRWhitePointX:
2063 *value = m_metadata.referencePrimaries.WhiteX;
2065 case bmdDeckLinkFrameMetadataHDRWhitePointY:
2066 *value = m_metadata.referencePrimaries.WhiteY;
2068 case bmdDeckLinkFrameMetadataHDRMaxDisplayMasteringLuminance:
2069 *value = m_metadata.maxDisplayMasteringLuminance;
2071 case bmdDeckLinkFrameMetadataHDRMinDisplayMasteringLuminance:
2072 *value = m_metadata.minDisplayMasteringLuminance;
2074 case bmdDeckLinkFrameMetadataHDRMaximumContentLightLevel:
2075 *value = m_metadata.maxCLL;
2077 case bmdDeckLinkFrameMetadataHDRMaximumFrameAverageLightLevel:
2078 *value = m_metadata.maxFALL;
2082 return E_INVALIDARG;
2086HRESULT DeckLinkFrame::GetFlag(BMDDeckLinkFrameMetadataID metadataID,
BMD_BOOL* value)
2091 return E_INVALIDARG;
2094HRESULT DeckLinkFrame::GetString(BMDDeckLinkFrameMetadataID metadataID,
BMD_STR* value)
2099 return E_INVALIDARG;
2102HRESULT DeckLinkFrame::GetBytes(BMDDeckLinkFrameMetadataID metadataID,
void* , uint32_t* bufferSize)
2106 return E_INVALIDARG;
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))
2115DeckLink3DFrame *DeckLink3DFrame::Create(
long width,
long height,
long rawBytes, BMDPixelFormat
pixelFormat, buffer_pool_t &
buffer_pool, HDRMetadata
const & hdr_metadata)
2117 DeckLink3DFrame *
frame =
new DeckLink3DFrame(width, height, rawBytes, pixelFormat, buffer_pool, hdr_metadata);
2121DeckLink3DFrame::~DeckLink3DFrame()
2125ULONG DeckLink3DFrame::AddRef()
2127 return DeckLinkFrame::AddRef();
2130ULONG DeckLink3DFrame::Release()
2132 return DeckLinkFrame::Release();
2135HRESULT DeckLink3DFrame::QueryInterface(REFIID
id,
void **data)
2138 if(
id == IID_IDeckLinkVideoFrame3DExtensions)
2141 *data =
dynamic_cast<IDeckLinkVideoFrame3DExtensions *
>(
this);
2144 return DeckLinkFrame::QueryInterface(
id, data);
2147BMDVideo3DPackingFormat DeckLink3DFrame::Get3DPackingFormat()
2149 return bmdVideo3DPackingLeftOnly;
2152HRESULT DeckLink3DFrame::GetFrameForRightEye(IDeckLinkVideoFrame **
frame)
2154 *
frame = rightEye.get();
2163 IDeckLinkOutput* deckLinkOutput = NULL;
2164 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
2165 IDeckLinkDisplayMode* displayMode = NULL;
2167 int displayModeNumber = 0;
2170 result = deckLink->QueryInterface(IID_IDeckLinkOutput, (
void**)&deckLinkOutput);
2174 if (result == E_NOINTERFACE) {
2181 result = deckLinkOutput->GetDisplayModeIterator(&displayModeIterator);
2189 printf(
"\tdisplay modes:\n");
2190 while (displayModeIterator->Next(&displayMode) == S_OK)
2192 BMD_STR displayModeString = NULL;
2194 result = displayMode->GetName(&displayModeString);
2198 BMDTimeValue frameRateDuration;
2199 BMDTimeScale frameRateScale;
2203 int modeWidth = displayMode->GetWidth();
2204 int modeHeight = displayMode->GetHeight();
2205 uint32_t field_dominance_n = ntohl(displayMode->GetFieldDominance());
2206 displayMode->GetFrameRate(&frameRateDuration, &frameRateScale);
2208 "\t\t%2d) %-20s %d x %d \t %2.2f FPS %.4s, "
2212 modeWidth, modeHeight,
2213 (
float) ((
double) frameRateScale /
2214 (
double) frameRateDuration),
2215 (
char *) &field_dominance_n, flags_str.c_str());
2220 displayMode->Release();
2222 displayModeNumber++;
2232 if (query_prop_fcc !=
nullptr) {
2233 IDeckLinkProfileAttributes *deckLinkAttributes =
nullptr;
2234 if (deckLink->QueryInterface(IID_IDeckLinkProfileAttributes,
2235 (
void **) &deckLinkAttributes) ==
2239 deckLinkAttributes->Release();
2241 MSG(ERROR,
"Could not query device attributes.\n\n");
2249 if (displayModeIterator != NULL)
2250 displayModeIterator->Release();
2252 if (deckLinkOutput != NULL)
2253 deckLinkOutput->Release();
struct video_frame * frame
const char unsigned int flags
int void(** deleter)(void *))
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)
#define STDMETHODCALLTYPE
This file contains definition of some common audio types.
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()
static std::vector< std::pair< codec_t, BMDPixelFormat > > uv_to_bmd_codec_map
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,...)
void log_msg(int level, const char *format,...)
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
#define debug_msg(...)
#define debug_msg(...) log_msg(LOG_LEVEL_DEBUG, "[pid/%d +%d %s] ", getpid(), LINE,...
#define LOG_LEVEL_WARNING
less severe errors
#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)
const char * get_commandline_param(const char *key)
static int settings_init(struct state_jack *s, char *cfg)
@ LIBRARY_CLASS_VIDEO_DISPLAY
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
#define IS_KEY_PREFIX(tok, key)
expands to true value if <k> from tok in format <k>=<v> is prefix of key
#define IS_PREFIX(tok, key)
similar as above, but also key without a value is accepted (value optional)
@ STR_LEN
"standard" string length placeholder
unsigned long long next_power_of_two(unsigned long long x)
bool invalid_arg_is_numeric(const char *what)
uint32_t parse_uint32(const char *value_str) noexcept(false)
bool is_prefix_of(const char *haystack, const char *needle)
struct used to comunicate with audio capture/playback device
void set_int(int64_t val_)
bool device_write(IDeckLinkConfiguration *deckLinkConfiguration, BMDDeckLinkConfigurationID opt, std::string const &log_prefix="[DeckLink] ") const
bool keep_device_defaults
BMDTimeValue frameRateDuration
AudioDriftFixer audio_drift_fixer
IDeckLinkConfiguration * deckLinkConfiguration
PlaybackDelegate delegate
DeckLinkTimecode * timecode
its lifespan controlled by AddRef()/Release() methods
atomic_bool audio_reconfigure
map< BMDDeckLinkConfigurationID, bmd_option > device_options
PlaybackDelegate * delegate
bool play_audio
the BMD device will be used also for output audio
HDRMetadata requested_hdr_mode
mutex audio_reconf_lock
for audio and video reconf to be mutually exclusive
IDeckLinkMutableVideoFrame * deckLinkFrame
BMDTimeScale frameRateScale
IDeckLinkProfileAttributes * deckLinkAttributes
buffer_pool_t buffer_pool
IDeckLinkOutput * deckLinkOutput
BMDPixelFormat pixelFormat
bmd_option quad_square_division_split
char * data
Raw tile data. Pointer must be at least 4B aligned.
unsigned int data_len
length of the data
enum interlacing_t interlacing
unsigned int width
width of every tile (!)
unsigned int height
height of every tile (!)
void(* dispose)(struct video_frame *)
Struct video_frame represents a video frame and contains video description.
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
struct video_frame_callbacks callbacks
static time_ns_t get_time_in_ns()
void dev_add_option(struct device_info *dev, const char *name, const char *desc, const char *key, const char *opt_str, bool is_boolean)
@ PROGRESSIVE
progressive frame
@ SEGMENTED_FRAME
Segmented frame. Contains the same data as progressive frame.
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
@ R10k
RGB 10-bit packed - RGBX, big-endian (2 bit padding)
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
@ RGBA
RGBA 8-bit, big-endian.
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
This is an umbrella header for video functions.
#define CALL_AND_CHECK(cmd,...)
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 void display_decklink_done(void *state)
static void debug_print_metadata_id(const char *fn_name, BMDDeckLinkFrameMetadataID metadataID)
static BMDDisplayMode get_mode(IDeckLinkOutput *deckLinkOutput, struct video_desc desc, BMDTimeValue *frameRateDuration, BMDTimeScale *frameRateScale, bool stereo)
static bool display_decklink_reconfigure(void *state, struct video_desc desc)
static void set_low_latency(struct state_decklink *s, bool on)
static bool enable_audio(struct state_decklink *s, int bps, int channels)
static bool display_decklink_reconfigure_audio(void *state, int quant_samples, int channels, int sample_rate)
#define CALL_AND_CHECK(cmd, name)
static DeckLinkFrame * allocate_new_decklink_frame(struct state_decklink *s)
static bool display_decklink_get_property(void *state, int property, void *val, size_t *len)
static void update_timecode(DeckLinkTimecode *tc, double fps)
static void set_audio_props(state_decklink *s, IDeckLinkConfiguration *deckLinkConfiguration, unsigned int audio_output)
static const struct video_display_info display_decklink_info
static void * display_decklink_init(struct module *parent, const char *fmt, unsigned int flags)
static bool operator==(const REFIID &first, const REFIID &second)
static bool display_decklink_putf(void *state, struct video_frame *frame, long long timeout_ns)
#define EXIT_IF_FAILED(cmd, name)
static struct video_frame * display_decklink_getf(void *state)
static void display_decklink_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
#define RELEASE_IF_NOT_NULL(x)
static void display_decklink_put_audio_frame(void *state, const struct audio_frame *frame)
#define CHECK_CH_COUNT(channels)
only 2, 8, 16, 32 and 64 are supported according to BMD SDK doc
std::chrono::high_resolution_clock hrc
static void print_output_modes(IDeckLink *deckLink, const char *query_prop_fcc)