From bf4d64357adb4563aa2a39de6c4ce6c348e0ddeb Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Fri, 7 Jun 2024 11:02:50 +0200 Subject: [PATCH] vcap/decklink: sort devices with topological ID Allow device specification by topological ID and also sort according to it. This is important mainly for 8K Pro subdevices, which are now sorted in this order: ```` 0) 580200) DeckLink 8K Pro (1) 1) 580201) DeckLink 8K Pro (3) 2) 580202) DeckLink 8K Pro (2) 3) 580203) DeckLink 8K Pro (4) ``` The new order actually represent the physical SDI connector order of the card - the SDIs on the cards from the PCIe slots are Ref In, DeckLink 8K Pro (1), D8K (3), D8K (2), D8K (3). --- src/blackmagic_common.cpp | 36 +++++++++++++++++++++++ src/blackmagic_common.hpp | 4 +++ src/video_capture/decklink.cpp | 54 +++++++++++++++++++--------------- 3 files changed, 70 insertions(+), 24 deletions(-) diff --git a/src/blackmagic_common.cpp b/src/blackmagic_common.cpp index 85d5e7f6a..8e75ad4f5 100644 --- a/src/blackmagic_common.cpp +++ b/src/blackmagic_common.cpp @@ -39,8 +39,10 @@ #include #include // for isxdigit #include // for seconds +#include // for UINT_MAX #include #include // for fprintf, stderr +#include // for int64_t, uint32_t #include // for strlen, NULL, strdup, memcpy, size_t #include #include @@ -1087,6 +1089,40 @@ print_bmd_attribute(IDeckLinkProfileAttributes *deckLinkAttributes, << " attribute for this device is " << SBOLD(oss.str()) << "\n"; } +/** + * @returns list of DeckLink devices sorted (indexed) by topological ID + * If no topological ID is reported, use UINT_MAX (and lower vals) + */ +std::map> +bmd_get_sorted_devices(IDeckLinkIterator *deckLinkIterator) +{ + IDeckLink *deckLink = nullptr; + std::map> out; + unsigned tid_substitute = UINT_MAX; + while (deckLinkIterator->Next(&deckLink) == S_OK) { + IDeckLinkProfileAttributes *deckLinkAttributes = nullptr; + HRESULT result = E_FAIL; + result = + deckLink->QueryInterface(IID_IDeckLinkProfileAttributes, + (void **) &deckLinkAttributes); + assert(result == S_OK); + int64_t id = 0; + result = + deckLinkAttributes->GetInt(BMDDeckLinkTopologicalID, &id); + if (result != S_OK) { + id = tid_substitute--; + } + assert(id >= 0 && id <= UINT_MAX); + deckLinkAttributes->Release(); + + auto release = [](IDeckLink *d) { d->Release(); }; + out.emplace(id, + std::unique_ptr{ + deckLink, release }); + } + return out; +} + ADD_TO_PARAM(R10K_FULL_OPT, "* " R10K_FULL_OPT "\n" " Do not do conversion from/to limited range on in/out for R10k on BMD devs.\n"); diff --git a/src/blackmagic_common.hpp b/src/blackmagic_common.hpp index 54271af7f..f22cdd699 100644 --- a/src/blackmagic_common.hpp +++ b/src/blackmagic_common.hpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -174,5 +175,8 @@ bool bmd_parse_audio_levels(const char *opt) noexcept(false); void print_bmd_attribute(IDeckLinkProfileAttributes *deckLinkAttributes, const char *query_prop_fcc); +std::map> +bmd_get_sorted_devices(IDeckLinkIterator *deckLinkIterator); + #endif // defined BLACKMAGIC_COMMON_HPP diff --git a/src/video_capture/decklink.cpp b/src/video_capture/decklink.cpp index 7a2fa5c10..443628acc 100644 --- a/src/video_capture/decklink.cpp +++ b/src/video_capture/decklink.cpp @@ -503,7 +503,7 @@ decklink_help(bool full, const char *query_prop_fcc = nullptr) col() << SBOLD(SRED("\t-t decklink") << ":[full]help") << " | " << SBOLD(SRED("-t decklink") << ":query=") << "\n"; col() << SBOLD(SRED("\t-t decklink") - << "{:m[ode]=|:d[evice]=|:c[odec]=...=<" + << "{:m[ode]=|:d[evice]=|:c[odec]=...=<" "val>}*") << "\n"; col() << SBOLD(SRED("\t-t decklink") @@ -610,27 +610,28 @@ decklink_help(bool full, const char *query_prop_fcc = nullptr) return 0; } - cout << "Devices:\n"; + cout << "Devices (idx, topological ID, name):\n"; // Enumerate all cards in this system - IDeckLink *deckLink = nullptr; + auto deckLinkDevices = bmd_get_sorted_devices(deckLinkIterator); int numDevices = 0; - while (deckLinkIterator->Next(&deckLink) == S_OK) - { + for (auto & d : deckLinkDevices) { + IDeckLink *deckLink = d.second.get(); string deviceName = bmd_get_device_name(deckLink); if (deviceName.empty()) { deviceName = "(unable to get name)"; } // *** Print the model name of the DeckLink card - col() << "\t" << TBOLD(<< numDevices <<) << ") " << TBOLD(TGREEN(<< deviceName <<)) << "\n"; - + color_printf("\t" TBOLD("%d") ") " TBOLD("%6x") ") " TBOLD( + TGREEN("%s")) "\n", + numDevices, d.first, deviceName.c_str()); + // Increment the total number of DeckLink cards found numDevices++; if (full) { vidcap_decklink_print_card_info(deckLink, query_prop_fcc); } - deckLink->Release(); } if (!full) { col() << "\n(use \"-t decklink:" @@ -832,7 +833,6 @@ static void vidcap_decklink_probe(device_info **available_cards, int *card_count *deleter = free; IDeckLinkIterator* deckLinkIterator; - IDeckLink* deckLink; int numDevices = 0; // Create an IDeckLinkIterator object to enumerate all DeckLink cards in the system @@ -843,7 +843,9 @@ static void vidcap_decklink_probe(device_info **available_cards, int *card_count } // Enumerate all cards in this system - while (deckLinkIterator->Next(&deckLink) == S_OK) { + auto deckLinkDevices = bmd_get_sorted_devices(deckLinkIterator); + for (auto & d: deckLinkDevices) { + IDeckLink *deckLink = d.second.get(); HRESULT result; IDeckLinkProfileAttributes *deckLinkAttributes; @@ -869,7 +871,7 @@ static void vidcap_decklink_probe(device_info **available_cards, int *card_count snprintf(cards[*card_count - 1].dev, sizeof cards[*card_count - 1].dev, ":device=%d", numDevices); snprintf(cards[*card_count - 1].name, sizeof cards[*card_count - 1].name, - "%s #%d", deviceName.c_str(), numDevices); + "%s #%x", deviceName.c_str(), numDevices); snprintf(cards[*card_count - 1].extra, sizeof cards[*card_count - 1].extra, "\"embeddedAudioAvailable\":\"t\""); @@ -881,6 +883,7 @@ static void vidcap_decklink_probe(device_info **available_cards, int *card_count } } list> modes = get_input_modes (deckLink); + int i = 0; const int mode_count = sizeof cards[*card_count - 1].modes / sizeof cards[*card_count - 1].modes[0]; @@ -933,8 +936,6 @@ static void vidcap_decklink_probe(device_info **available_cards, int *card_count numDevices++; RELEASE_IF_NOT_NULL(deckLinkAttributes); - // Release the IDeckLink instance when we've finished with it to prevent leaks - deckLink->Release(); } deckLinkIterator->Release(); @@ -1121,20 +1122,25 @@ bool device_state::init(struct vidcap_decklink_state *s, struct tile *t, BMDAudi if (deckLinkIterator == NULL) { return false; } - bool found = false; - while (deckLinkIterator->Next(&deckLink) == S_OK) { + auto deckLinkDevices = bmd_get_sorted_devices(deckLinkIterator); + for (auto & d: deckLinkDevices) { + deckLink = d.second.release(); // we must release manually! string deviceName = bmd_get_device_name(deckLink); if (!deviceName.empty() && deviceName == device_id.c_str()) { - found = true; - } - - if (isdigit(device_id.c_str()[0]) && atoi(device_id.c_str()) == dnum) { - found = true; - } - - if (found) { break; } + + // topological ID + const unsigned long tid = + strtoul(device_id.c_str(), nullptr, 16); + if (d.first == tid) { + break; + } + // idx + if (isdigit(device_id.c_str()[0]) && atoi(device_id.c_str()) == dnum) { + break; + } + dnum++; // Release the IDeckLink instance when we've finished with it to prevent leaks deckLink->Release(); @@ -1143,7 +1149,7 @@ bool device_state::init(struct vidcap_decklink_state *s, struct tile *t, BMDAudi deckLinkIterator->Release(); deckLinkIterator = NULL; - if (!found) { + if (deckLink == nullptr) { LOG(LOG_LEVEL_ERROR) << MOD_NAME "Device " << device_id << " was not found.\n"; INIT_ERR(); }