54#include <condition_variable>
69#include <unordered_map>
91#define MOD_NAME "[DeckLink capture] "
94#define STDMETHODCALLTYPE
97#define RELEASE_IF_NOT_NULL(x) if (x != nullptr) { x->Release(); x = nullptr; }
99using namespace std::string_literals;
100using std::chrono::duration_cast;
101using std::chrono::microseconds;
102using std::chrono::milliseconds;
103using std::chrono::steady_clock;
104using std::condition_variable;
112using std::make_unique;
122using std::unique_lock;
123using std::unique_ptr;
124using std::unordered_map;
133#define EXIT_IF_FAILED(cmd, name) \
135 HRESULT result = cmd;\
136 if (FAILED(result)) {;\
137 LOG(LOG_LEVEL_ERROR) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
143#define CALL_AND_CHECK_2(cmd, name) \
145 HRESULT result = cmd;\
146 if (FAILED(result)) {;\
147 LOG(LOG_LEVEL_WARNING) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
151#define CALL_AND_CHECK_3(cmd, name, msg) \
153 HRESULT result = cmd;\
154 if (FAILED(result)) {;\
155 LOG(LOG_LEVEL_WARNING) << MOD_NAME << name << ": " << bmd_hresult_to_string(result) << "\n";\
157 LOG(LOG_LEVEL_INFO) << MOD_NAME << name << ": " << msg << "\n";\
161#define GET_3TH_ARG(arg1, arg2, arg3, ...) arg3
162#define CALL_AND_CHECK_CHOOSER(...) \
163 GET_3TH_ARG(__VA_ARGS__, CALL_AND_CHECK_3, CALL_AND_CHECK_2, )
165#define CALL_AND_CHECK(cmd, ...) CALL_AND_CHECK_CHOOSER(__VA_ARGS__)(cmd, __VA_ARGS__)
184 vector <struct device_state>
state{vector <struct device_state>(1)};
202 { bmdDeckLinkConfigCapturePassThroughMode,
bmd_option((int64_t) bmdDeckLinkCapturePassthroughModeDisabled,
false) },
228static list<tuple<int, string, string, string>>
get_input_modes (IDeckLink* deckLink);
234 static constexpr BMDDetectedVideoInputFormatFlags
csMask{bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInputRGB444};
235 static constexpr BMDDetectedVideoInputFormatFlags
bitDepthMask{bmdDetectedVideoInput8BitDepth | bmdDetectedVideoInput10BitDepth | bmdDetectedVideoInput12BitDepth};
263 if (newRefValue == 0)
272 map<BMDDetectedVideoInputFormatFlags, string> change_map {
273 { bmdVideoInputDisplayModeChanged,
"display mode"s },
274 { bmdVideoInputFieldDominanceChanged,
"field dominance"s },
275 { bmdVideoInputColorspaceChanged,
"color space"s },
277 for (
auto &i : change_map) {
278 if ((notificationEvents & i.first) != 0U) {
289 map<BMDDetectedVideoInputFormatFlags, string> flag_map {
290 { bmdDetectedVideoInputYCbCr422,
"YCbCr422"s },
291 { bmdDetectedVideoInputRGB444,
"RGB444"s },
292 { bmdDetectedVideoInputDualStream3D,
"DualStream3D"s },
293 { bmdDetectedVideoInput12BitDepth,
"12bit"s },
294 { bmdDetectedVideoInput10BitDepth,
"10bit"s },
295 { bmdDetectedVideoInput8BitDepth,
"8bit"s },
298 for (
auto &i : flag_map) {
299 if ((
flags & i.first) != 0U) {
311 BMDVideoInputFormatChangedEvents notificationEvents,
312 IDeckLinkDisplayMode*
mode,
313 BMDDetectedVideoInputFormatFlags
flags)
noexcept override {
315 <<
MOD_NAME <<
"Format change detected ("
319 bool detected_3d = (
flags & bmdDetectedVideoInputDualStream3D) != 0U;
320 if (detected_3d !=
s->
stereo) {
323 << (detected_3d ?
"" :
"not ") <<
"detected but "
326 "Switching automatically. This behavior is "
327 "experimental so please report any problems. "
328 "You can also specify `3D` option explicitly.\n";
333 unique_lock<mutex> lk(
s->
lock);
334 if (notificationEvents & bmdVideoInputColorspaceChanged && csBitDepth !=
configuredCsBitDepth) {
336 csBitDepth |= bmdDetectedVideoInput8BitDepth;
341 unordered_map<BMDDetectedVideoInputFormatFlags, codec_t> m = {
342 {bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInput8BitDepth,
UYVY},
343 {bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInput10BitDepth,
v210},
344 {bmdDetectedVideoInputYCbCr422 | bmdDetectedVideoInput12BitDepth,
v210},
345 {bmdDetectedVideoInputRGB444 | bmdDetectedVideoInput8BitDepth,
RGBA},
346 {bmdDetectedVideoInputRGB444 | bmdDetectedVideoInput10BitDepth,
R10k},
347 {bmdDetectedVideoInputRGB444 | bmdDetectedVideoInput12BitDepth,
R12L},
350 const string &
depth = (
flags & bmdDetectedVideoInput10BitDepth) != 0U ?
"10"s :
"12"s;
353 <<
"-bit signal, use \":codec=UYVY\" to "
354 "enforce 8-bit capture (old "
362 if (notificationEvents & bmdVideoInputDisplayModeChanged) {
364 deckLinkInput->PauseStreams();
371 deckLinkInput->FlushStreams();
372 deckLinkInput->StartStreams();
385 unique_lock<mutex> lk(
s->
lock);
391 if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) {
400 BMDTimeValue unused_duration = 0;
401 videoFrame->GetStreamTime(&
frameTime, &unused_duration,
405 if (audioPacket && !nosig) {
407 audioPacket->AddRef();
422 IDeckLinkTimecode *tc = NULL;
423 if (videoFrame->GetTimecode(bmdTimecodeRP188Any, &tc) == S_OK) {
438 IDeckLinkVideoFrame3DExtensions *rightEye;
440 result = videoFrame->QueryInterface(IID_IDeckLinkVideoFrame3DExtensions, (
void **)&rightEye);
442 if (result == S_OK) {
479 if (result != S_OK) {
480 cout <<
"Could not query device attributes.\n\n";
486 if ((
deckLink->QueryInterface(IID_IDeckLinkInput,
499 int64_t connections = 0;
500 if (
deckLinkAttributes->GetInt(BMDDeckLinkVideoInputConnections, &connections) != S_OK) {
503 cout <<
"\n\tConnection can be one of following:\n";
505 if (connections & it.first) {
506 col() <<
"\t\t" <<
SBOLD(it.second) <<
"\n";
512 if (query_prop_fcc !=
nullptr) {
525 col() <<
"\nDeckLink options:\n";
526 col() <<
SBOLD(
SRED(
"\t-t decklink") <<
":[full]help") <<
" | "
527 <<
SBOLD(
SRED(
"-t decklink") <<
":query=<FourCC>") <<
"\n";
529 <<
"{:m[ode]=<mode>|:d[evice]=<idx|ID|name>|:c[odec]=<colorspace>...<key>=<"
533 <<
"[:<device_index(indices)>[:<mode>:<colorspace>[:3D]["
534 ":sync_timecode][:connection=<input>][:detect-"
535 "format][:conversion=<conv_mode>]]")
537 col() <<
"\t(mode specification is mandatory if your card does not support format autodetection; syntax on the first line is recommended, the second is obsolescent)\n";
541 printf(
"\tUse this to capture 3D from supported card (eg. DeckLink HD 3D Extreme).\n");
542 printf(
"\tDo not use it for eg. Quad or Duo. Availability of the mode is indicated\n");
543 printf(
"\tin video format listing above by flag \"3D\".\n");
547 col() <<
"\tPrint description of all available options.\n";
550 col() <<
SBOLD(
"half-duplex | full-duplex | simplex") <<
"\n";
551 col() <<
"\tSet a profile that allows maximal number of simultaneous "
552 "IOs / set device to better compatibility (3D, dual-link) / "
553 "use all connectors as single input.\n";
556 col() <<
SBOLD(
"[no]passthrough[=keep]") <<
"\n";
557 col() <<
"\tDisables/enables/keeps capture passthrough (default is "
562 col() <<
SBOLD(
"conversion") <<
"\n";
563 col() <<
SBOLD(
"\tnone") <<
" - No video input conversion\n";
564 col() <<
SBOLD(
"\t10lb") <<
" - HD1080 to SD video input down conversion\n";
565 col() <<
SBOLD(
"\t10am") <<
" - Anamorphic from HD1080 to SD video input down conversion\n";
566 col() <<
SBOLD(
"\t72lb") <<
" - Letter box from HD720 to SD video input down conversion\n";
567 col() <<
SBOLD(
"\t72ab") <<
" - Letterbox video input up conversion\n";
568 col() <<
SBOLD(
"\tamup") <<
" - Anamorphic video input up conversion\n";
569 col() <<
"\tThen use the set the resulting mode (!) for capture, eg. for 1080p to PAL conversion:\n"
570 "\t\t-t decklink:mode=pal:conversion=10lb\n";
573 col() <<
SBOLD(
"query=<FourCC>") <<
"\n";
574 col() <<
"\tQueries device attribute, eg. `decklink:q=mach` to "
575 "see max embed. channels).\n";
579 col() <<
"\tIncoming signal should be treated as progressive even if detected as interlaced (PsF).\n";
582 col() <<
SBOLD(
"Use1080PsF[=true|false]") <<
"\n";
583 col() <<
"\tIncoming signal should be treated as PsF instead of progressive.\n";
586 col() <<
SBOLD(
"nosig-send") <<
"\n";
587 col() <<
"\tSend video even if no signal was detected (useful when video interrupts\n"
588 "\tbut the video stream needs to be preserved, eg. to keep sync with audio).\n";
591 col() <<
SBOLD(
"detect-format") <<
"\n";
592 col() <<
"\tTry to detect input video format even if the "
593 "device doesn't support\n"
594 "\tautodetect, eg. \"-t "
595 "decklink:connection=HDMI:detect-format\".\n";
598 col() <<
SBOLD(
"profile=<FourCC>") <<
" - use desired device profile:\n";
601 col() <<
SBOLD(
"sync_timecode") <<
"\n";
602 col() <<
"\tTry to synchronize inputs based on timecode (for multiple inputs, eg. tiled 4K)\n";
604 col() <<
SBOLD(
"keep-settings") <<
"\n\tdo not apply any DeckLink settings by UG than required (keep user-selected defaults)\n";
606 col() <<
SBOLD(
"<option_FourCC>=<value>") <<
" - arbitrary BMD option (given a FourCC) and corresponding value, i.a.:\n";
608 <<
"\t\tset analog audio levels to maximum gain "
609 "(consumer audio level)\n";
612 col() <<
"(other options available, use \"" <<
SBOLD(
"fullhelp") <<
"\" to see complete list of options)\n\n";
615 col() <<
"Available color spaces:";
625 col() <<
"Possible connections: " <<
TBOLD(
"SDI") <<
", " <<
TBOLD(
"HDMI") <<
", " <<
TBOLD(
"OpticalSDI") <<
", " <<
TBOLD(
"Component") <<
", " <<
TBOLD(
"Composite") <<
", " <<
TBOLD(
"SVideo") <<
"\n";
630 bool com_initialized =
false;
632 cout <<
"Devices (idx, topological ID, name):\n";
635 const bool natural_sort =
639 IDeckLink *
deckLink = get<0>(d).get();
641 if (deviceName.empty()) {
642 deviceName =
"(unable to get name)";
647 snprintf(index,
sizeof index,
648 TBOLD(
"%c")
") ", get<char>(d));
650 if (full || natural_sort) {
651 snprintf(index + strlen(index),
652 sizeof index - strlen(index),
TBOLD(
"%d")
") ",
658 index, get<unsigned>(d),
669 col() <<
"\n(use \"-t decklink:"
671 "fullhelp")
"\" to see full list of device modes "
672 "and available connections)\n\n";
683 printf(
"Examples:\n");
685 <<
" # captures autodetected video from first DeckLink (index 0) "
688 <<
" # specify mode for 2nd device which doesn't have "
692 <<
" -t decklink:d=\"DeckLink 8K Pro (1)\":profile=1dfd")
693 <<
" # capture from 8K Pro and set profile to 1-subdevice "
694 "full-duplex (useful for 3D capture)\n";
708 assert(devs != NULL && strlen(devs) > 0);
709 char *devices = strdup(devs);
715 while ((ptr = strtok_r(tmp,
",", &save_ptr_dev))) {
727 const char *
val = strchr(opt,
'=') + 1;
728 if(strcasecmp(opt,
"3D") == 0) {
730 }
else if(strcasecmp(opt,
"timecode") == 0) {
733 const char *
codec = strchr(opt,
'=') + 1;
740 const char *connection = strchr(opt,
'=') + 1;
742 if (strcasecmp(connection, it.second.c_str()) == 0) {
750 }
else if(strncasecmp(opt,
"audio_level=",
751 strlen(
"audio_level=")) == 0) {
755 s->
device_options[bmdDeckLinkConfigVideoInputConversionMode].parse(strchr(opt,
'='));
759 s->
mode = strchr(opt,
'=') + 1;
762 }
else if (strcasecmp(opt,
"detect-format") == 0) {
764 }
else if (strcasecmp(opt,
"p_not_i") == 0) {
766 }
else if (strstr(opt,
"Use1080PsF") !=
nullptr) {
767 s->
device_options[bmdDeckLinkConfigCapture1080pAsPsF].set_flag(strchr(opt,
'=') ==
nullptr || strcasecmp(strchr(opt,
'='),
"false") != 0);
768 }
else if (strncasecmp(opt,
"passthrough", 4) == 0 ||
769 strncasecmp(opt,
"nopassthrough", 6) == 0) {
770 if (strstr(opt,
"keep")) {
773 s->
device_options[bmdDeckLinkConfigCapturePassThroughMode] =
bmd_option((int64_t) (opt[0] ==
'n' ? bmdDeckLinkCapturePassthroughModeDisabled
774 : bmdDeckLinkCapturePassthroughModeCleanSwitch));
776 }
else if (strstr(opt,
"full-duplex") == opt) {
778 }
else if (strstr(opt,
"half-duplex") == opt) {
780 }
else if (strstr(opt,
"simplex") == opt) {
782 }
else if (strcasecmp(opt,
"nosig-send") == 0) {
784 }
else if (strstr(opt,
"keep-settings") == opt) {
786 }
else if ((strchr(opt,
'=') !=
nullptr && strchr(opt,
'=') - opt == 4) || strlen(opt) == 4) {
800 while((tmp = strtok_r(NULL,
":", save_ptr))) {
813 char *save_ptr = NULL;
815 if (!
fmt || (tmp = strtok_r(
fmt,
":", &save_ptr)) == NULL) {
816 MSG(INFO,
"Auto-choosen device 0.\n");
821 if (isdigit(tmp[0]) && strcasecmp(tmp,
"3D") != 0) {
823 <<
MOD_NAME "Deprecated syntax used, please use options in "
824 "format \"key=value\"\n";
829 tmp = strtok_r(NULL,
":", &save_ptr);
833 tmp = strtok_r(NULL,
":", &save_ptr);
866 bool com_initialized =
false;
870 IDeckLink *
deckLink = get<0>(d).get();
875 if (result != S_OK) {
878 int64_t connections_bitmap;
879 if(
deckLinkAttributes->GetInt(BMDDeckLinkVideoInputConnections, &connections_bitmap) != S_OK) {
885 if (deviceName.empty()) {
886 deviceName =
"(unknown)";
891 realloc(cards, *card_count *
sizeof(
struct device_info));
892 memset(&cards[*card_count - 1], 0,
sizeof(
struct device_info));
893 snprintf(cards[*card_count - 1].
dev,
sizeof cards[*card_count - 1].
dev,
894 ":device=%6x", get<unsigned>(d));
895 snprintf(cards[*card_count - 1].
name,
sizeof cards[*card_count - 1].
name,
896 "%s", deviceName.c_str());
897 snprintf(cards[*card_count - 1].
extra,
sizeof cards[*card_count - 1].
extra,
898 "\"embeddedAudioAvailable\":\"t\"");
901 list<string> connections;
903 if (connections_bitmap & it.first) {
904 connections.push_back(it.second);
910 const int mode_count =
sizeof cards[*card_count - 1].
modes /
911 sizeof cards[*card_count - 1].
modes[0];
913 for (
auto &c : connections) {
914 if (i >= mode_count) {
917 snprintf(cards[*card_count - 1].
modes[i].
id,
918 sizeof cards[*card_count - 1].
modes[i].
id,
919 R
"({"modeOpt":"connection=%s:codec=UYVY"})",
921 snprintf(cards[*card_count - 1].modes[i].name,
922 sizeof cards[*card_count - 1].
modes[i].
name,
923 "DeckLink Auto (%s)", c.c_str());
924 if (++i >= mode_count) {
927 snprintf(cards[*card_count - 1].
modes[i].
id,
928 sizeof cards[*card_count - 1].
modes[i].
id,
929 R
"({"modeOpt":"detect-format:connection=%s:codec=UYVY"})",
931 snprintf(cards[*card_count - 1].modes[i].name,
932 sizeof cards[*card_count - 1].
modes[i].
name,
933 "UltraGrid auto-detect (%s) (SLOW)", c.c_str());
937 for (
auto &m :
modes) {
938 for (
auto &c : connections) {
939 if (i >= mode_count) {
943 snprintf(cards[*card_count - 1].
modes[i].
id,
944 sizeof cards[*card_count - 1].
modes[i].
id,
945 R
"({"modeOpt":"connection=%s:mode=%s:codec=UYVY"})",
946 c.c_str(), get<1>(m).c_str());
947 snprintf(cards[*card_count - 1].modes[i].name,
948 sizeof cards[*card_count - 1].
modes[i].
name,
949 "%s (%s)", get<2>(m).c_str(), c.c_str());
954 dev_add_option(&cards[*card_count - 1],
"3D",
"3D",
"3D",
":3D",
true);
955 dev_add_option(&cards[*card_count - 1],
"Profile",
"Duplex profile can be one of: 1dhd, 2dhd, 2dfd, 4dhd, keep",
"profile",
":profile=",
false);
961 *available_cards = cards;
966 BMD_STR displayModeBMDString =
nullptr;
968 if (HRESULT result = displayMode->GetName(&displayModeBMDString);
FAILED(result)) {
976 [&s](
const std::pair<codec_t, BMDPixelFormat>& el){ return el.first == s->codec; });
984 BMDTimeValue frameRateDuration = 0;
990 displayMode->GetFrameRate(&frameRateDuration, &s->
frameRateScale);
993 switch(displayMode->GetFieldDominance()) {
994 case bmdLowerFieldFirst:
996 MOD_NAME "Lower field first format detected, "
997 "fields can be switched! ");
999 case bmdUpperFieldFirst:
1002 case bmdProgressiveFrame:
1003 case bmdProgressiveSegmentedFrame:
1006 case bmdUnknownFieldDominance:
1041 IDeckLinkDisplayMode *displayMode;
1043 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
1044 result = device->
deckLinkInput->GetDisplayModeIterator(&displayModeIterator);
1045 if (result != S_OK) {
1049 vector<BMDPixelFormat> pfs = {bmdFormat8BitYUV, bmdFormat8BitBGRA};
1051 while (displayModeIterator->Next(&displayMode) == S_OK) {
1052 for (BMDPixelFormat pf : pfs) {
1053 uint32_t
mode = ntohl(displayMode->GetDisplayMode());
1055 result = device->
deckLinkInput->EnableVideoInput(displayMode->GetDisplayMode(), pf, 0);
1056 if (result == S_OK) {
1058 unique_lock<mutex> lk(s->
lock);
1060 lk, milliseconds(1200), [device] {
1062 ->newFrameReady == 1;
1068 if (device->
delegate->newFrameReady == 1) {
1069 *outDisplayMode = displayMode->GetDisplayMode();
1072 displayMode->Release();
1073 displayModeIterator->Release();
1079 displayMode->Release();
1082 displayModeIterator->Release();
1087 if (audio_src_flag == 0U) {
1091 s->
state[0].audio =
true;
1092 switch (audio_src_flag) {
1094 *audioConnection = bmdAudioConnectionEmbedded;
1097 *audioConnection = bmdAudioConnectionAESEBU;
1100 *audioConnection = bmdAudioConnectionAnalog;
1107 if (s->
audio.bps != 2 && s->
audio.bps != 4) {
1109 <<
MOD_NAME <<
"Unsupported audio Bps "
1111 <<
"! Supported is 2 or 4 bytes only!\n";
1118 s->
audio.sample_rate = bmdAudioSampleRate48kHz;
1121 s->
audio.data = (
char *) malloc(s->
audio.max_size);
1134#define INIT_ERR() do { RELEASE_IF_NOT_NULL(displayMode); RELEASE_IF_NOT_NULL(displayModeIterator); return false; } while (0)
1137 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
1138 IDeckLinkDisplayMode* displayMode = NULL;
1142 bool com_initialized =
false;
1146 if (!deviceName.empty() && deviceName ==
device_id.c_str()) {
1151 const unsigned long tid =
1152 strtoul(
device_id.c_str(),
nullptr, 16);
1153 if (get<unsigned>(d) == tid) {
1158 if (atoi(
device_id.c_str()) == get<int>(d)) {
1181 if (!deviceName.empty()) {
1208 const BMDVideoInputConversionMode supported_conversion_mode = s->
device_options.find(bmdDeckLinkConfigVideoInputConversionMode) != s->
device_options.end() ? (BMDVideoInputConversionMode) s->
device_options.at(bmdDeckLinkConfigVideoInputConversionMode).get_int() : (BMDVideoInputConversionMode) bmdNoVideoInputConversion;
1214 delegate = make_unique<VideoDelegate>(s, *
this);
1217 BMDDisplayMode detectedDisplayMode = bmdModeUnknown;
1227 "Could not obtain the video input display mode iterator:",
INIT_ERR());
1232 enum { MODE_SPEC_AUTODETECT = -1, MODE_SPEC_FOURCC = -2, MODE_SPEC_DETECTED = -3 };
1233 int mode_idx = MODE_SPEC_AUTODETECT;
1236 if (s->
mode.length() > 0) {
1237 if (s->
mode.length() <= 2) {
1238 mode_idx = atoi(s->
mode.c_str());
1240 mode_idx = MODE_SPEC_FOURCC;
1244 mode_idx = MODE_SPEC_DETECTED;
1247 while (displayModeIterator->Next(&displayMode) == S_OK) {
1248 if (mode_idx == MODE_SPEC_DETECTED) {
1249 if (detectedDisplayMode == displayMode->GetDisplayMode()) {
1252 displayMode->Release();
1254 }
else if (mode_idx == MODE_SPEC_AUTODETECT) {
1255 if ((s->
stereo && (displayMode->GetFlags() & bmdDisplayModeSupports3D) == 0u) || displayMode->GetFieldDominance() == bmdLowerFieldFirst) {
1256 displayMode->Release();
1261 [&s](
const std::pair<codec_t, BMDPixelFormat>& el){ return el.first == s->codec; });
1266 BMDPixelFormat pf = it->second;
1269 ? (BMDVideoConnection) s->
device_options.at(bmdDeckLinkConfigVideoInputConnection).get_int() : (BMDVideoConnection) bmdVideoConnectionUnspecified;
1274 }
else if (mode_idx != MODE_SPEC_FOURCC) {
1275 if (mode_idx != mnum) {
1278 displayMode->Release();
1288 displayMode->Release();
1293 BMD_STR displayModeString = NULL;
1294 if (HRESULT result = displayMode->GetName(&displayModeString); result == S_OK) {
1296 <<
"The desired display mode is supported: "
1302 if (mode_idx == MODE_SPEC_FOURCC) {
1304 "Desired mode \"%s\" is invalid or not "
1307 }
else if (mode_idx >= 0) {
1308 MSG(ERROR,
"Desired mode index %s is out of bounds.\n", s->
mode.c_str());
1309 }
else if (mode_idx == MODE_SPEC_AUTODETECT) {
1310 MSG(ERROR,
"Cannot set initial format for "
1311 "autodetection - perhaps imposible "
1312 "combinations of parameters were set.\n");
1314 assert(
"Invalid mode spec." && 0);
1321 "Could not set display mode properties",
INIT_ERR());
1325 if (mode_idx == MODE_SPEC_AUTODETECT) {
1326 MSG(INFO,
"Trying to autodetect format.\n");
1331 "Device doesn't support format autodetection, you "
1332 "must set it manually or try \"-t "
1333 "decklink:detect-format[:connection=<in>]\"\n");
1341 ? (BMDVideoConnection) s->
device_options.at(bmdDeckLinkConfigVideoInputConnection).get_int() : (BMDVideoConnection) bmdVideoConnectionUnspecified;
1349 if (HRESULT result =
deckLinkInput->EnableVideoInput(displayMode->GetDisplayMode(), pf, s->
enable_flags); result != S_OK) {
1354 case E_ACCESSDENIED:
1356 "stream currently active (another application using it?).\n");
1368 audioConnection) == S_OK) {
1369 const map<BMDAudioConnection, string>
mapping = {
1370 { bmdAudioConnectionEmbedded,
"embedded" },
1371 { bmdAudioConnectionAESEBU,
"AES/EBU" },
1372 { bmdAudioConnectionAnalog,
"analog" },
1373 { bmdAudioConnectionAnalogXLR,
"analogXLR" },
1374 { bmdAudioConnectionAnalogRCA,
"analogRCA" },
1375 { bmdAudioConnectionMicrophone,
"microphone" },
1376 { bmdAudioConnectionHeadphones,
"headphones" },
1383 if (s->
audio.ch_count == 4 ||
1388 "DeckLink may not be able to grab %d audio "
1390 "1, 2, 8, 16, 32 or 64 may work.\n",
1394 bmdAudioSampleRate48kHz,
1396 ? bmdAudioSampleType16bitInteger
1397 : bmdAudioSampleType32bitInteger,
1398 max(s->
audio.ch_count, 2)),
1405 displayMode->Release();
1408 displayModeIterator->Release();
1409 displayModeIterator = NULL;
1418 if (strcmp(
fmt,
"help") == 0 || strcmp(
fmt,
"fullhelp") == 0) {
1442 char *tmp_fmt = strdup(
fmt);
1446 }
catch (exception &e) {
1447 MSG(ERROR,
"%s\n", e.what());
1466 BMDAudioConnection audioConnection = bmdAudioConnectionEmbedded;
1477 "In this mode, only one device needs to be passed.");
1502 audioPacket->Release();
1512 free(s->
audio.data);
1531 result = s->
state[i].deckLinkInput->StopStreams();
1532 if (result != S_OK) {
1534 <<
MOD_NAME "Could not stop stream: "
1538 if (s->
state[i].audio) {
1539 result = s->
state[i].deckLinkInput->DisableAudioInput();
1540 if (result != S_OK) {
1542 <<
MOD_NAME "Could disable audio input: "
1546 result = s->
state[i].deckLinkInput->DisableVideoInput();
1547 if (result != S_OK) {
1549 <<
MOD_NAME "Could disable video input: "
1567 BMDTimecodeBCD max_timecode = 0u;
1568 int tiles_total = 0;
1574 if(s->
state[i].delegate->newFrameReady == 1) {
1575 if (s->
state[i].delegate->timecode > max_timecode) {
1576 max_timecode = s->
state[i].delegate->timecode;
1584 if(s->
state[i].delegate->newFrameReady == 1) {
1588 if(s->
state[i].delegate->timecode && s->
state[i].delegate->timecode != max_timecode) {
1589 s->
state[i].delegate->newFrameReady = 0;
1599 if (s->
state[i].delegate->newFrameReady == -1) {
1610 s->
audio.data_len = 0;
1612 BMDTimeValue audio_time = 0;
1613 s->
audioPackets.front()->GetPacketTime(&audio_time, bmdAudioSampleRate48kHz);
1614 s->
audio.timestamp =
1615 ((int64_t)audio_time * 90000 + bmdAudioSampleRate48kHz - 1) / bmdAudioSampleRate48kHz;
1621 void *audioFrame =
nullptr;
1622 audioPacket->GetBytes(&audioFrame);
1624 if (s->
audio.ch_count == 1) {
1625 if (s->
audio.data_len + audioPacket->GetSampleFrameCount() * 1U * s->
audio.bps <=
static_cast<unsigned>(s->
audio.max_size)) {
1626 demux_channel(s->
audio.data + s->
audio.data_len,
static_cast<char *
>(audioFrame), s->
audio.bps, min<int64_t>(audioPacket->GetSampleFrameCount() * 2 * s->
audio.bps, INT_MAX), 2 , 0 );
1629 MSG(WARNING,
"Audio frame too small!\n");
1632 if (s->
audio.data_len + audioPacket->GetSampleFrameCount() * s->
audio.ch_count * s->
audio.bps <= s->
audio.max_size) {
1633 memcpy(s->
audio.data + s->
audio.data_len, audioFrame, audioPacket->GetSampleFrameCount() * s->
audio.ch_count * s->
audio.bps);
1636 MSG(WARNING,
"Audio frame too small!\n");
1639 audioPacket->Release();
1664 int tiles_total = 0;
1666 bool frame_ready =
true;
1670 unique_lock<mutex> lk(s->
lock);
1677 cv_status rc = cv_status::no_timeout;
1678 steady_clock::time_point t0(steady_clock::now());
1680 while(rc == cv_status::no_timeout
1682 && tiles_total != -1
1691 steady_clock::time_point t(steady_clock::now());
1697 if (rc != cv_status::no_timeout || timeout || tiles_total == -1) {
1717 frame_ready =
false;
1724 if(s->
state[i].delegate->newFrameReady == 0) {
1725 frame_ready =
false;
1727 s->
state[i].delegate->newFrameReady = 0;
1741 if (s->
state[0].delegate->pixelFrame != NULL &&
1742 s->
state[0].delegate->pixelFrameRight != NULL) {
1749 if (s->
state[i].delegate->pixelFrame == NULL) {
1764 ((int64_t)s->
state[0].delegate->frameTime * 90000 +
1773 list<tuple<int, string, string, string>> ret;
1774 IDeckLinkInput* deckLinkInput = NULL;
1775 IDeckLinkDisplayModeIterator* displayModeIterator = NULL;
1776 IDeckLinkDisplayMode* displayMode = NULL;
1778 int displayModeNumber = 0;
1781 if ((result = deckLink->QueryInterface(IID_IDeckLinkInput, (
void**)&deckLinkInput)) != S_OK) {
1783 if (result == E_NOINTERFACE) {
1790 if ((result = deckLinkInput->GetDisplayModeIterator(&displayModeIterator)) != S_OK) {
1796 while (displayModeIterator->Next(&displayMode) == S_OK)
1798 BMD_STR displayModeString = NULL;
1800 result = displayMode->GetName((
BMD_STR *) &displayModeString);
1804 BMDTimeValue frameRateDuration;
1809 int modeWidth = displayMode->GetWidth();
1810 int modeHeight = displayMode->GetHeight();
1812 uint32_t
mode = ntohl(displayMode->GetDisplayMode());
1813 uint32_t field_dominance_n = ntohl(displayMode->GetFieldDominance());
1814 string fcc{(
char *) &
mode, 4};
1817 snprintf(buf,
sizeof buf,
"%d x %d \t %6.2f FPS \t flags: %.4s, %s", modeWidth, modeHeight,
1819 (
char *) &field_dominance_n, flags_str.c_str());
1820 string details{buf};
1821 ret.push_back(tuple<int, string, string, string> {displayModeNumber, fcc,
name, details});
1827 displayMode->Release();
1829 displayModeNumber++;
1842 list<tuple<int, string, string, string>> ret =
get_input_modes (deckLink);
1843 printf(
"\tcapture modes:\n");
1844 for (
auto &i : ret) {
1845 col() <<
"\t\t" << right << SBOLD(setw(2) << get<0>(i) <<
" (" << get<1>(i) <<
")") <<
") " <<
1846 left << setw(20) << get<2>(i) << internal <<
" " <<
const char unsigned int flags
struct audio_frame ** audio
int void(** deleter)(void *))
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
const char * get_codec_name(codec_t codec)
codec_t get_codec_from_name(const char *name)
static const struct codec_param mapping[AC_COUNT]
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)
bool blackmagic_api_version_check()
string bmd_get_flags_str(BMDDisplayModeFlags flags)
const map< BMDVideoConnection, string > & get_connection_string_map()
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)
bool bmd_check_stereo_profile(IDeckLink *deckLink)
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()
void r10k_limited_to_full(const char *in, char *out, size_t len)
#define BMD_CHECK(cmd, name, action)
static std::vector< std::pair< codec_t, BMDPixelFormat > > uv_to_bmd_codec_map
struct vidcap_decklink_state * s
IDeckLinkVideoInputFrame * lastFrame
VideoDelegate(struct vidcap_decklink_state *state, struct device_state &device_)
static constexpr BMDDetectedVideoInputFormatFlags csMask
virtual ULONG STDMETHODCALLTYPE AddRef(void) override
struct device_state & device
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame *, IDeckLinkAudioInputPacket *) override
IDeckLinkVideoFrame * rightEyeFrame
virtual ULONG STDMETHODCALLTYPE Release(void) override
virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents notificationEvents, IDeckLinkDisplayMode *mode, BMDDetectedVideoInputFormatFlags flags) noexcept override
static auto getNotificationEventsStr(BMDVideoInputFormatChangedEvents notificationEvents, BMDDetectedVideoInputFormatFlags flags) noexcept
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID *) override
static constexpr BMDDetectedVideoInputFormatFlags bitDepthMask
BMDDetectedVideoInputFormatFlags configuredCsBitDepth
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_NOTICE
information that may be interesting
#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_FATAL
errors that prevent UG run
#define LOG_LEVEL_WARNING
less severe errors
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
unsigned int audio_capture_bps
const char * get_commandline_param(const char *key)
unsigned int audio_capture_sample_rate
unsigned int audio_capture_channels
#define DEFAULT_AUDIO_CAPTURE_CHANNELS
@ LIBRARY_CLASS_VIDEO_CAPTURE
#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
@ STR_LEN
"standard" string length placeholder
bool is_power_of_two(unsigned long long x)
decoder_func_t vc_copylineToRGBA_inplace
struct used to comunicate with audio capture/playback device
void set_int(int64_t val_)
struct device_info::mode modes[512]
bool init(struct vidcap_decklink_state *s, struct tile *tile, BMDAudioConnection audioConnection)
IDeckLinkInput * deckLinkInput
void check_attributes(struct vidcap_decklink_state *s)
unique_ptr< VideoDelegate > delegate
IDeckLinkConfiguration * deckLinkConfiguration
IDeckLinkProfileAttributes * deckLinkAttributes
Struct tile is an area of video_frame.
char * data
Raw tile data. Pointer must be at least 4B aligned.
unsigned int data_len
length of the data
vector< struct device_state > state
unsigned int next_frame_time
struct video_frame * frame
bool nosig_send
send video even when no signal detected
queue< IDeckLinkAudioInputPacket * > audioPackets
unsigned int requested_bit_depth
BMDTimeScale frameRateScale
condition_variable boss_cv
void set_codec(codec_t c)
map< BMDDeckLinkConfigurationID, bmd_option > device_options
BMDVideoInputFlags enable_flags
BMDSupportedVideoModeFlags supported_flags
bool keep_device_defaults
Struct video_frame represents a video frame and contains video description.
enum interlacing_t interlacing
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
uint32_t timecode
BCD timecode (hrs, min, sec, frm num)
This file contains some common data types.
@ TIMESTAMP_VALID
timestamp set by source (in 90 kHz clock)
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
@ 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.
@ VIDEO_CODEC_NONE
dummy color spec
@ R12L
RGB 12-bit packed, little-endian.
@ RGBA
RGBA 8-bit, big-endian.
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
void demux_channel(char *out, char *in, int bps, int in_len, int in_stream_channels, int pos_in_stream)
This is an umbrella header for video functions.
static bool settings_init(struct vidcap_decklink_state *s, char *fmt)
static bool decklink_cap_configure_audio(struct vidcap_decklink_state *s, unsigned int audio_src_flag, BMDAudioConnection *audioConnection)
static audio_frame * process_new_audio_packets(struct vidcap_decklink_state *s)
static void vidcap_decklink_print_card_info(IDeckLink *deckLink, const char *query_prop_fcc)
static bool detect_format(struct vidcap_decklink_state *s, BMDDisplayMode *outDisplayMode, struct device_state *device)
static int decklink_help(bool full, const char *query_prop_fcc=nullptr)
static list< tuple< int, string, string, string > > get_input_modes(IDeckLink *deckLink)
static HRESULT set_display_mode_properties(struct vidcap_decklink_state *s, struct tile *tile, IDeckLinkDisplayMode *displayMode, BMDPixelFormat *pf)
constexpr const int DEFAULT_AUDIO_BPS
static bool settings_init_key_val(struct vidcap_decklink_state *s, char **save_ptr)
static void cleanup_common(struct vidcap_decklink_state *s)
static void parse_devices(struct vidcap_decklink_state *s, const char *devs)
static struct video_frame * vidcap_decklink_grab(void *state, struct audio_frame **audio)
#define STDMETHODCALLTYPE
static void vidcap_decklink_probe(device_info **available_cards, int *card_count, void(**deleter)(void *))
#define CALL_AND_CHECK(cmd,...)
static int nr_frames(struct vidcap_decklink_state *s)
static void postprocess_frame(struct vidcap_decklink_state *s)
static void print_input_modes(IDeckLink *deckLink)
static void vidcap_decklink_done(void *state)
static bool parse_option(struct vidcap_decklink_state *s, const char *opt)
static int vidcap_decklink_init(struct vidcap_params *params, void **state)
constexpr const size_t MAX_AUDIO_PACKETS
#define RELEASE_IF_NOT_NULL(x)
static const struct video_capture_info vidcap_decklink_info
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_AESEBU
AES/EBU audio.
#define VIDCAP_FLAG_AUDIO_ANALOG
(balanced) analog audio
#define VIDCAP_FLAG_AUDIO_ANY
#define VIDCAP_FLAG_AUDIO_EMBEDDED
HD-SDI embedded audio.
int get_bits_per_component(codec_t codec)
struct tile * vf_get_tile(struct video_frame *buf, int pos)
Returns pointer to n-th tile from video frame Equivalent to &video_frame::tiles[pos].
bool com_initialize(bool *com_initialized, const char *err_prefix)
void com_uninitialize(bool *com_initialized)