aja-common.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186
  1. #include "aja-card-manager.hpp"
  2. #include "aja-common.hpp"
  3. #include "aja-ui-props.hpp"
  4. #include "aja-props.hpp"
  5. #include <ajantv2/includes/ntv2debug.h>
  6. #include <ajantv2/includes/ntv2devicescanner.h>
  7. #include <ajantv2/includes/ntv2devicefeatures.h>
  8. #include <ajantv2/includes/ntv2signalrouter.h>
  9. #include <ajantv2/includes/ntv2utils.h>
  10. void filter_io_selection_input_list(const std::string &cardID,
  11. const std::string &channelOwner,
  12. obs_property_t *list)
  13. {
  14. auto &cardManager = aja::CardManager::Instance();
  15. auto cardEntry = cardManager.GetCardEntry(cardID);
  16. if (!cardEntry) {
  17. blog(LOG_DEBUG,
  18. "filter_io_selection_input_list: Card Entry not found for %s",
  19. cardID.c_str());
  20. return;
  21. }
  22. NTV2DeviceID deviceID = DEVICE_ID_NOTFOUND;
  23. CNTV2Card *card = cardEntry->GetCard();
  24. if (card)
  25. deviceID = card->GetDeviceID();
  26. // Gray out the IOSelection list items that are in use by other plugin instances
  27. for (size_t idx = 0; idx < obs_property_list_item_count(list); idx++) {
  28. auto io_select = static_cast<IOSelection>(
  29. obs_property_list_item_int(list, idx));
  30. if (io_select == IOSelection::Invalid) {
  31. obs_property_list_item_disable(list, idx, false);
  32. continue;
  33. }
  34. bool enabled = cardEntry->InputSelectionReady(
  35. io_select, deviceID, channelOwner);
  36. obs_property_list_item_disable(list, idx, !enabled);
  37. blog(LOG_DEBUG, "IOSelection %s = %s",
  38. aja::IOSelectionToString(io_select).c_str(),
  39. enabled ? "enabled" : "disabled");
  40. }
  41. }
  42. void filter_io_selection_output_list(const std::string &cardID,
  43. const std::string &channelOwner,
  44. obs_property_t *list)
  45. {
  46. auto &cardManager = aja::CardManager::Instance();
  47. auto cardEntry = cardManager.GetCardEntry(cardID);
  48. if (!cardEntry) {
  49. blog(LOG_DEBUG,
  50. "filter_io_selection_output_list: Card Entry not found for %s",
  51. cardID.c_str());
  52. return;
  53. }
  54. NTV2DeviceID deviceID = DEVICE_ID_NOTFOUND;
  55. CNTV2Card *card = cardEntry->GetCard();
  56. if (card)
  57. deviceID = card->GetDeviceID();
  58. // Gray out the IOSelection list items that are in use by other plugin instances
  59. for (size_t idx = 0; idx < obs_property_list_item_count(list); idx++) {
  60. auto io_select = static_cast<IOSelection>(
  61. obs_property_list_item_int(list, idx));
  62. if (io_select == IOSelection::Invalid) {
  63. obs_property_list_item_disable(list, idx, false);
  64. continue;
  65. }
  66. bool enabled = cardEntry->OutputSelectionReady(
  67. io_select, deviceID, channelOwner);
  68. obs_property_list_item_disable(list, idx, !enabled);
  69. blog(LOG_DEBUG, "IOSelection %s = %s",
  70. aja::IOSelectionToString(io_select).c_str(),
  71. enabled ? "enabled" : "disabled");
  72. }
  73. }
  74. void populate_io_selection_input_list(const std::string &cardID,
  75. const std::string &channelOwner,
  76. NTV2DeviceID deviceID,
  77. obs_property_t *list)
  78. {
  79. obs_property_list_clear(list);
  80. obs_property_list_add_int(list,
  81. obs_module_text(kUIPropIOSelectNone.text),
  82. static_cast<long long>(IOSelection::Invalid));
  83. for (auto i = 0; i < static_cast<int32_t>(IOSelection::NumIOSelections);
  84. i++) {
  85. auto ioSelect = static_cast<IOSelection>(i);
  86. if (ioSelect == IOSelection::AnalogIn)
  87. continue;
  88. if (aja::DeviceCanDoIOSelectionIn(deviceID, ioSelect)) {
  89. obs_property_list_add_int(
  90. list,
  91. aja::IOSelectionToString(ioSelect).c_str(),
  92. static_cast<long long>(ioSelect));
  93. }
  94. }
  95. filter_io_selection_input_list(cardID, channelOwner, list);
  96. }
  97. void populate_io_selection_output_list(const std::string &cardID,
  98. const std::string &channelOwner,
  99. NTV2DeviceID deviceID,
  100. obs_property_t *list)
  101. {
  102. obs_property_list_clear(list);
  103. obs_property_list_add_int(list,
  104. obs_module_text(kUIPropIOSelectNone.text),
  105. static_cast<long long>(IOSelection::Invalid));
  106. if (deviceID == DEVICE_ID_TTAP_PRO) {
  107. obs_property_list_add_int(
  108. list, "SDI & HDMI",
  109. static_cast<long long>(IOSelection::HDMIMonitorOut));
  110. } else {
  111. for (auto i = 0;
  112. i < static_cast<int32_t>(IOSelection::NumIOSelections);
  113. i++) {
  114. auto ioSelect = static_cast<IOSelection>(i);
  115. if (ioSelect == IOSelection::Invalid ||
  116. ioSelect == IOSelection::AnalogOut)
  117. continue;
  118. if (aja::DeviceCanDoIOSelectionOut(deviceID,
  119. ioSelect)) {
  120. obs_property_list_add_int(
  121. list,
  122. aja::IOSelectionToString(ioSelect)
  123. .c_str(),
  124. static_cast<long long>(ioSelect));
  125. }
  126. }
  127. }
  128. filter_io_selection_output_list(cardID, channelOwner, list);
  129. }
  130. void populate_video_format_list(NTV2DeviceID deviceID, obs_property_t *list,
  131. NTV2VideoFormat genlockFormat, bool want4KHFR,
  132. bool matchFPS)
  133. {
  134. VideoFormatList videoFormats = {};
  135. VideoStandardList orderedStandards = {};
  136. orderedStandards.push_back(NTV2_STANDARD_525);
  137. orderedStandards.push_back(NTV2_STANDARD_625);
  138. if (NTV2DeviceCanDoHDVideo(deviceID)) {
  139. orderedStandards.push_back(NTV2_STANDARD_720);
  140. orderedStandards.push_back(NTV2_STANDARD_1080);
  141. orderedStandards.push_back(NTV2_STANDARD_1080p);
  142. orderedStandards.push_back(NTV2_STANDARD_2K);
  143. orderedStandards.push_back(NTV2_STANDARD_2Kx1080p);
  144. orderedStandards.push_back(NTV2_STANDARD_2Kx1080i);
  145. }
  146. if (NTV2DeviceCanDo4KVideo(deviceID)) {
  147. orderedStandards.push_back(NTV2_STANDARD_3840i);
  148. orderedStandards.push_back(NTV2_STANDARD_3840x2160p);
  149. if (want4KHFR)
  150. orderedStandards.push_back(NTV2_STANDARD_3840HFR);
  151. orderedStandards.push_back(NTV2_STANDARD_4096i);
  152. orderedStandards.push_back(NTV2_STANDARD_4096x2160p);
  153. if (want4KHFR)
  154. orderedStandards.push_back(NTV2_STANDARD_4096HFR);
  155. }
  156. aja::GetSortedVideoFormats(deviceID, orderedStandards, videoFormats);
  157. for (const auto &vf : videoFormats) {
  158. bool addFormat = true;
  159. // Filter formats by framerate family if specified
  160. if (genlockFormat != NTV2_FORMAT_UNKNOWN)
  161. addFormat = IsMultiFormatCompatible(genlockFormat, vf);
  162. struct obs_video_info ovi;
  163. if (matchFPS && obs_get_video_info(&ovi)) {
  164. NTV2FrameRate frameRate =
  165. GetNTV2FrameRateFromVideoFormat(vf);
  166. ULWord fpsNum = 0;
  167. ULWord fpsDen = 0;
  168. GetFramesPerSecond(frameRate, fpsNum, fpsDen);
  169. uint32_t obsFrameTime =
  170. 1000000 * ovi.fps_den / ovi.fps_num;
  171. uint32_t ajaFrameTime = 1000000 * fpsDen / fpsNum;
  172. if (obsFrameTime != ajaFrameTime)
  173. addFormat = false;
  174. }
  175. if (addFormat) {
  176. std::string name = NTV2VideoFormatToString(vf, true);
  177. obs_property_list_add_int(list, name.c_str(), (int)vf);
  178. }
  179. }
  180. }
  181. void populate_pixel_format_list(NTV2DeviceID deviceID, obs_property_t *list)
  182. {
  183. const NTV2PixelFormat supported_pix_fmts[] = {kDefaultAJAPixelFormat,
  184. NTV2_FBF_24BIT_BGR};
  185. for (auto &&pf : supported_pix_fmts) {
  186. if (NTV2DeviceCanDoFrameBufferFormat(deviceID, pf)) {
  187. obs_property_list_add_int(
  188. list,
  189. NTV2FrameBufferFormatToString(pf, true).c_str(),
  190. static_cast<long long>(pf));
  191. }
  192. }
  193. }
  194. void populate_sdi_transport_list(obs_property_t *list, NTV2DeviceID deviceID,
  195. bool capture)
  196. {
  197. if (capture) {
  198. obs_property_list_add_int(list, obs_module_text("Auto"),
  199. kAutoDetect);
  200. }
  201. for (int i = 0; i < (int)SDITransport::Unknown; i++) {
  202. SDITransport sdi_trx = static_cast<SDITransport>(i);
  203. if (sdi_trx == SDITransport::SDI6G ||
  204. sdi_trx == SDITransport::SDI12G) {
  205. if (!NTV2DeviceCanDo12GSDI(deviceID))
  206. continue;
  207. }
  208. // Disabling 12G in Output plugin until AJA 4K HFR bug is fixed
  209. if (!capture && sdi_trx == SDITransport::SDI12G)
  210. continue;
  211. obs_property_list_add_int(
  212. list, aja::SDITransportToString(sdi_trx).c_str(),
  213. static_cast<long long>(sdi_trx));
  214. }
  215. }
  216. void populate_sdi_4k_transport_list(obs_property_t *list)
  217. {
  218. obs_property_list_add_int(
  219. list,
  220. aja::SDITransport4KToString(SDITransport4K::Squares).c_str(),
  221. static_cast<long long>(SDITransport4K::Squares));
  222. obs_property_list_add_int(
  223. list,
  224. aja::SDITransport4KToString(SDITransport4K::TwoSampleInterleave)
  225. .c_str(),
  226. static_cast<long long>(SDITransport4K::TwoSampleInterleave));
  227. }
  228. bool aja_video_format_changed(obs_properties_t *props, obs_property_t *list,
  229. obs_data_t *settings)
  230. {
  231. auto vid_fmt = static_cast<NTV2VideoFormat>(
  232. obs_data_get_int(settings, kUIPropVideoFormatSelect.id));
  233. size_t itemCount = obs_property_list_item_count(list);
  234. bool itemFound = false;
  235. for (size_t i = 0; i < itemCount; i++) {
  236. auto itemFormat = static_cast<NTV2VideoFormat>(
  237. obs_property_list_item_int(list, i));
  238. if (itemFormat == vid_fmt) {
  239. itemFound = true;
  240. break;
  241. }
  242. }
  243. if (!itemFound) {
  244. obs_property_list_insert_int(list, 0, "", vid_fmt);
  245. obs_property_list_item_disable(list, 0, true);
  246. return true;
  247. }
  248. obs_property_t *sdi_4k_trx =
  249. obs_properties_get(props, kUIPropSDITransport4K.id);
  250. obs_property_set_visible(sdi_4k_trx, NTV2_IS_4K_VIDEO_FORMAT(vid_fmt));
  251. return true;
  252. }
  253. namespace aja {
  254. video_format AJAPixelFormatToOBSVideoFormat(NTV2PixelFormat pf)
  255. {
  256. video_format obs_video_format = VIDEO_FORMAT_NONE;
  257. switch (pf) {
  258. case NTV2_FBF_8BIT_YCBCR:
  259. obs_video_format = VIDEO_FORMAT_UYVY;
  260. break;
  261. case NTV2_FBF_24BIT_RGB:
  262. case NTV2_FBF_24BIT_BGR:
  263. obs_video_format = VIDEO_FORMAT_BGR3;
  264. break;
  265. case NTV2_FBF_ARGB:
  266. case NTV2_FBF_ABGR:
  267. case NTV2_FBF_RGBA:
  268. obs_video_format = VIDEO_FORMAT_BGRA;
  269. break;
  270. case NTV2_FBF_10BIT_YCBCR:
  271. case NTV2_FBF_10BIT_RGB:
  272. case NTV2_FBF_8BIT_YCBCR_YUY2:
  273. case NTV2_FBF_10BIT_DPX:
  274. case NTV2_FBF_10BIT_YCBCR_DPX:
  275. case NTV2_FBF_8BIT_DVCPRO:
  276. case NTV2_FBF_8BIT_YCBCR_420PL3:
  277. case NTV2_FBF_8BIT_HDV:
  278. case NTV2_FBF_10BIT_YCBCRA:
  279. case NTV2_FBF_10BIT_DPX_LE:
  280. case NTV2_FBF_48BIT_RGB:
  281. case NTV2_FBF_12BIT_RGB_PACKED:
  282. case NTV2_FBF_PRORES_DVCPRO:
  283. case NTV2_FBF_PRORES_HDV:
  284. case NTV2_FBF_10BIT_RGB_PACKED:
  285. case NTV2_FBF_10BIT_ARGB:
  286. case NTV2_FBF_16BIT_ARGB:
  287. case NTV2_FBF_8BIT_YCBCR_422PL3:
  288. case NTV2_FBF_10BIT_RAW_RGB:
  289. case NTV2_FBF_10BIT_RAW_YCBCR:
  290. case NTV2_FBF_10BIT_YCBCR_420PL3_LE:
  291. case NTV2_FBF_10BIT_YCBCR_422PL3_LE:
  292. case NTV2_FBF_10BIT_YCBCR_420PL2:
  293. case NTV2_FBF_10BIT_YCBCR_422PL2:
  294. case NTV2_FBF_8BIT_YCBCR_420PL2:
  295. case NTV2_FBF_8BIT_YCBCR_422PL2:
  296. default:
  297. obs_video_format = VIDEO_FORMAT_NONE;
  298. break;
  299. }
  300. return obs_video_format;
  301. }
  302. void GetSortedVideoFormats(NTV2DeviceID id, const VideoStandardList &standards,
  303. VideoFormatList &videoFormats)
  304. {
  305. if (standards.empty())
  306. return;
  307. VideoFormatMap videoFormatMap;
  308. // Bin all the formats based on video standard
  309. for (size_t i = (size_t)NTV2_FORMAT_UNKNOWN;
  310. i < (size_t)NTV2_MAX_NUM_VIDEO_FORMATS; i++) {
  311. NTV2VideoFormat fmt = (NTV2VideoFormat)i;
  312. NTV2Standard standard = GetNTV2StandardFromVideoFormat(fmt);
  313. if (id != DEVICE_ID_NOTFOUND &&
  314. NTV2DeviceCanDoVideoFormat(id, fmt)) {
  315. if (videoFormatMap.count(standard)) {
  316. videoFormatMap.at(standard).push_back(fmt);
  317. } else {
  318. std::vector<NTV2VideoFormat> v;
  319. v.push_back(fmt);
  320. videoFormatMap.insert(
  321. std::pair<NTV2Standard,
  322. std::vector<NTV2VideoFormat>>(
  323. standard, v));
  324. }
  325. }
  326. }
  327. for (size_t v = (size_t)NTV2_STANDARD_1080;
  328. v < (size_t)NTV2_NUM_STANDARDS; v++) {
  329. NTV2Standard standard = (NTV2Standard)v;
  330. if (videoFormatMap.count(standard)) {
  331. std::sort(videoFormatMap.at(standard).begin(),
  332. videoFormatMap.at(standard).end(),
  333. [&](const NTV2VideoFormat &d1,
  334. const NTV2VideoFormat &d2) {
  335. std::string d1Str, d2Str;
  336. d1Str = NTV2VideoFormatToString(d1);
  337. d2Str = NTV2VideoFormatToString(d2);
  338. return d1Str < d2Str;
  339. });
  340. }
  341. }
  342. for (size_t v = 0; v < standards.size(); v++) {
  343. NTV2Standard standard = standards.at(v);
  344. if (videoFormatMap.count(standard)) {
  345. for (size_t i = 0;
  346. i < videoFormatMap.at(standard).size(); i++) {
  347. NTV2VideoFormat vf =
  348. videoFormatMap.at(standard).at(i);
  349. videoFormats.push_back(vf);
  350. }
  351. }
  352. }
  353. }
  354. NTV2VideoFormat HandleSpecialCaseFormats(IOSelection io, NTV2VideoFormat vf,
  355. NTV2DeviceID id)
  356. {
  357. // 1080p Level-B formats and ST372M
  358. if (NTV2_VIDEO_FORMAT_IS_B(vf) &&
  359. !(IsSDITwoWireIOSelection(io) && NTV2_IS_HD_VIDEO_FORMAT(vf))) {
  360. vf = aja::GetLevelAFormatForLevelBFormat(vf);
  361. }
  362. // UHD/4K Square Division auto-detect
  363. if ((io == IOSelection::SDI1__4 || io == IOSelection::SDI5__8) &&
  364. NTV2_IS_HD_VIDEO_FORMAT(vf)) {
  365. vf = GetQuadSizedVideoFormat(vf, true);
  366. }
  367. // Kona5/io4K+ auto-detection of UHD/4K 6G/12G SDI formats.
  368. if (aja::IsSDIOneWireIOSelection(io) && NTV2_IS_4K_VIDEO_FORMAT(vf) &&
  369. !NTV2_IS_SQUARE_DIVISION_FORMAT(vf) &&
  370. !NTV2DeviceCanDo12gRouting(id)) {
  371. vf = GetQuadSizedVideoFormat(GetQuarterSizedVideoFormat(vf));
  372. }
  373. return vf;
  374. }
  375. NTV2Channel WidgetIDToChannel(NTV2WidgetID id)
  376. {
  377. #if AJA_NTV2_SDK_VERSION_MAJOR <= 16 && AJA_NTV2_SDK_VERSION_MINOR <= 2
  378. // Workaround for bug in NTV2 SDK <= 16.2 where NTV2_WgtSDIMonOut1 maps incorrectly to NTV2_CHANNEL1.
  379. if (id == NTV2_WgtSDIMonOut1)
  380. return NTV2_CHANNEL5;
  381. #endif
  382. return CNTV2SignalRouter::WidgetIDToChannel(id);
  383. }
  384. uint32_t CardNumFramestores(NTV2DeviceID id)
  385. {
  386. auto numFramestores = NTV2DeviceGetNumFrameStores(id);
  387. if (id == DEVICE_ID_CORVIDHBR) {
  388. numFramestores = 1;
  389. }
  390. return numFramestores;
  391. }
  392. uint32_t CardNumAudioSystems(NTV2DeviceID id)
  393. {
  394. if (id == DEVICE_ID_KONALHI || id == DEVICE_ID_KONALHEPLUS)
  395. return 2;
  396. return NTV2DeviceGetNumAudioSystems(id);
  397. }
  398. // IO4K and IO4K+ perform SDI Monitor Output on "SDI5" and "Framestore 4".
  399. bool CardCanDoSDIMonitorOutput(NTV2DeviceID id)
  400. {
  401. return (id == DEVICE_ID_IO4K || id == DEVICE_ID_IO4KPLUS);
  402. }
  403. // Cards with a dedicated HDMI Monitor Input, tied to "Framestore 4".
  404. bool CardCanDoHDMIMonitorInput(NTV2DeviceID id)
  405. {
  406. return (id == DEVICE_ID_IO4K || id == DEVICE_ID_IO4KUFC ||
  407. id == DEVICE_ID_IO4KPLUS || id == DEVICE_ID_IOXT ||
  408. id == DEVICE_ID_IOX3 || id == DEVICE_ID_KONALHI);
  409. }
  410. // Cards with a dedicated HDMI Monitor Output, tied to "Framestore 4".
  411. bool CardCanDoHDMIMonitorOutput(NTV2DeviceID id)
  412. {
  413. return (id == DEVICE_ID_IO4K || id == DEVICE_ID_IO4KPLUS ||
  414. id == DEVICE_ID_IOXT || id == DEVICE_ID_IOX3 ||
  415. id == DEVICE_ID_KONA4 || id == DEVICE_ID_KONA5 ||
  416. id == DEVICE_ID_KONA5_8K || id == DEVICE_ID_KONA5_2X4K ||
  417. id == DEVICE_ID_KONA5_8KMK);
  418. }
  419. // Cards capable of 1x SDI at 6G/12G.
  420. bool CardCanDo1xSDI12G(NTV2DeviceID id)
  421. {
  422. return (id == DEVICE_ID_KONA5_8K || id == DEVICE_ID_KONA5_8KMK ||
  423. id == DEVICE_ID_KONA5 || id == DEVICE_ID_KONA5_2X4K ||
  424. id == DEVICE_ID_IO4KPLUS || id == DEVICE_ID_CORVID44_12G);
  425. }
  426. // Check for 3G level-B SDI on the wire.
  427. bool Is3GLevelB(CNTV2Card *card, NTV2Channel channel)
  428. {
  429. if (!card)
  430. return false;
  431. bool levelB = false;
  432. auto deviceID = card->GetDeviceID();
  433. UWord channelIndex = static_cast<UWord>(channel);
  434. if (NTV2DeviceCanDo3GIn(deviceID, channelIndex) ||
  435. NTV2DeviceCanDo12GIn(deviceID, channelIndex)) {
  436. if (!card->GetSDIInput3GbPresent(levelB, channel))
  437. return false;
  438. }
  439. return levelB;
  440. }
  441. // Get the 3G Level-A enum for a 3G Level-B format enum.
  442. NTV2VideoFormat GetLevelAFormatForLevelBFormat(NTV2VideoFormat vf)
  443. {
  444. NTV2VideoFormat result = vf;
  445. switch (vf) {
  446. default:
  447. break;
  448. case NTV2_FORMAT_1080p_5000_B:
  449. result = NTV2_FORMAT_1080p_5000_A;
  450. break;
  451. case NTV2_FORMAT_1080p_5994_B:
  452. result = NTV2_FORMAT_1080p_5994_A;
  453. break;
  454. case NTV2_FORMAT_1080p_6000_B:
  455. result = NTV2_FORMAT_1080p_6000_A;
  456. break;
  457. case NTV2_FORMAT_1080p_2K_4795_B:
  458. result = NTV2_FORMAT_1080p_2K_4795_A;
  459. break;
  460. case NTV2_FORMAT_1080p_2K_4800_B:
  461. result = NTV2_FORMAT_1080p_2K_4800_A;
  462. break;
  463. case NTV2_FORMAT_1080p_2K_5000_B:
  464. result = NTV2_FORMAT_1080p_2K_5000_A;
  465. break;
  466. case NTV2_FORMAT_1080p_2K_5994_B:
  467. result = NTV2_FORMAT_1080p_2K_5994_A;
  468. break;
  469. case NTV2_FORMAT_1080p_2K_6000_B:
  470. result = NTV2_FORMAT_1080p_2K_6000_A;
  471. break;
  472. }
  473. return result;
  474. }
  475. NTV2VideoFormat InterlacedFormatForPsfFormat(NTV2VideoFormat vf)
  476. {
  477. NTV2VideoFormat result = vf;
  478. switch (vf) {
  479. default:
  480. break;
  481. case NTV2_FORMAT_1080psf_2500_2:
  482. result = NTV2_FORMAT_1080i_5000;
  483. break;
  484. case NTV2_FORMAT_1080psf_2997_2:
  485. result = NTV2_FORMAT_1080i_5994;
  486. break;
  487. }
  488. return result;
  489. }
  490. // Certain cards only have 1 SDI spigot.
  491. bool IsSingleSDIDevice(NTV2DeviceID id)
  492. {
  493. return (id == DEVICE_ID_TTAP_PRO || id == DEVICE_ID_KONA1);
  494. }
  495. bool IsIODevice(NTV2DeviceID id)
  496. {
  497. return (id == DEVICE_ID_IOXT || id == DEVICE_ID_IOX3 ||
  498. id == DEVICE_ID_IO4K || id == DEVICE_ID_IO4KPLUS ||
  499. id == DEVICE_ID_IOIP_2022 || id == DEVICE_ID_IOIP_2110);
  500. }
  501. /* Kona5 retail firmware and io4K+ have limited support for 6G/12G SDI
  502. * where SDI 1 is capable of capture and SDI 3 is capable of output.
  503. */
  504. bool IsRetail12GSDICard(NTV2DeviceID id)
  505. {
  506. return (id == DEVICE_ID_KONA5 || id == DEVICE_ID_IO4KPLUS);
  507. }
  508. bool IsOutputOnlyDevice(NTV2DeviceID id)
  509. {
  510. return id == DEVICE_ID_TTAP_PRO;
  511. }
  512. std::string SDITransportToString(SDITransport mode)
  513. {
  514. std::string str = "";
  515. switch (mode) {
  516. case SDITransport::SingleLink:
  517. str = "SD/HD Single Link";
  518. break;
  519. case SDITransport::HDDualLink:
  520. str = "HD Dual-Link";
  521. break;
  522. case SDITransport::SDI3Ga:
  523. str = "3G Level-A (3Ga)";
  524. break;
  525. case SDITransport::SDI3Gb:
  526. str = "3G Level-B (3Gb)";
  527. break;
  528. case SDITransport::SDI6G:
  529. str = "6G";
  530. break;
  531. case SDITransport::SDI12G:
  532. str = "12G";
  533. break;
  534. case SDITransport::Unknown:
  535. str = "Unknown";
  536. break;
  537. }
  538. return str;
  539. }
  540. std::string SDITransport4KToString(SDITransport4K mode)
  541. {
  542. std::string str = "";
  543. switch (mode) {
  544. case SDITransport4K::Squares:
  545. str = "Squares";
  546. break;
  547. case SDITransport4K::TwoSampleInterleave:
  548. str = "2SI";
  549. break;
  550. default:
  551. case SDITransport4K::Unknown:
  552. str = "Unknown";
  553. break;
  554. }
  555. return str;
  556. }
  557. std::string IOSelectionToString(IOSelection io)
  558. {
  559. std::string str;
  560. switch (io) {
  561. case IOSelection::SDI1:
  562. str = "SDI 1";
  563. break;
  564. case IOSelection::SDI2:
  565. str = "SDI 2";
  566. break;
  567. case IOSelection::SDI3:
  568. str = "SDI 3";
  569. break;
  570. case IOSelection::SDI4:
  571. str = "SDI 4";
  572. break;
  573. case IOSelection::SDI5:
  574. str = "SDI 5";
  575. break;
  576. case IOSelection::SDI6:
  577. str = "SDI 6";
  578. break;
  579. case IOSelection::SDI7:
  580. str = "SDI 7";
  581. break;
  582. case IOSelection::SDI8:
  583. str = "SDI 8";
  584. break;
  585. case IOSelection::SDI1_2:
  586. str = "SDI 1 & 2";
  587. break;
  588. case IOSelection::SDI3_4:
  589. str = "SDI 3 & 4";
  590. break;
  591. case IOSelection::SDI5_6:
  592. str = "SDI 5 & 6";
  593. break;
  594. case IOSelection::SDI7_8:
  595. str = "SDI 7 & 8";
  596. break;
  597. case IOSelection::SDI1__4:
  598. str = "SDI 1-4";
  599. break;
  600. case IOSelection::SDI5__8:
  601. str = "SDI 5-8";
  602. break;
  603. case IOSelection::HDMI1:
  604. str = "HDMI 1";
  605. break;
  606. case IOSelection::HDMI2:
  607. str = "HDMI 2";
  608. break;
  609. case IOSelection::HDMI3:
  610. str = "HDMI 3";
  611. break;
  612. case IOSelection::HDMI4:
  613. str = "HDMI 4";
  614. break;
  615. case IOSelection::HDMIMonitorIn:
  616. str = "HDMI IN";
  617. break;
  618. case IOSelection::HDMIMonitorOut:
  619. str = "HDMI OUT";
  620. break;
  621. case IOSelection::AnalogIn:
  622. str = "ANALOG IN";
  623. break;
  624. case IOSelection::AnalogOut:
  625. str = "ANALOG OUT";
  626. break;
  627. case IOSelection::Invalid:
  628. str = "Invalid";
  629. break;
  630. }
  631. return str;
  632. }
  633. void IOSelectionToInputSources(IOSelection io, NTV2InputSourceSet &inputSources)
  634. {
  635. switch (io) {
  636. case IOSelection::SDI1:
  637. inputSources.insert(NTV2_INPUTSOURCE_SDI1);
  638. break;
  639. case IOSelection::SDI2:
  640. inputSources.insert(NTV2_INPUTSOURCE_SDI2);
  641. break;
  642. case IOSelection::SDI3:
  643. inputSources.insert(NTV2_INPUTSOURCE_SDI3);
  644. break;
  645. case IOSelection::SDI4:
  646. inputSources.insert(NTV2_INPUTSOURCE_SDI4);
  647. break;
  648. case IOSelection::SDI5:
  649. inputSources.insert(NTV2_INPUTSOURCE_SDI5);
  650. break;
  651. case IOSelection::SDI6:
  652. inputSources.insert(NTV2_INPUTSOURCE_SDI6);
  653. break;
  654. case IOSelection::SDI7:
  655. inputSources.insert(NTV2_INPUTSOURCE_SDI7);
  656. break;
  657. case IOSelection::SDI8:
  658. inputSources.insert(NTV2_INPUTSOURCE_SDI8);
  659. break;
  660. case IOSelection::SDI1_2:
  661. inputSources.insert(NTV2_INPUTSOURCE_SDI1);
  662. inputSources.insert(NTV2_INPUTSOURCE_SDI2);
  663. break;
  664. case IOSelection::SDI3_4:
  665. inputSources.insert(NTV2_INPUTSOURCE_SDI3);
  666. inputSources.insert(NTV2_INPUTSOURCE_SDI4);
  667. break;
  668. case IOSelection::SDI5_6:
  669. inputSources.insert(NTV2_INPUTSOURCE_SDI5);
  670. inputSources.insert(NTV2_INPUTSOURCE_SDI6);
  671. break;
  672. case IOSelection::SDI7_8:
  673. inputSources.insert(NTV2_INPUTSOURCE_SDI7);
  674. inputSources.insert(NTV2_INPUTSOURCE_SDI8);
  675. break;
  676. case IOSelection::SDI1__4:
  677. inputSources.insert(NTV2_INPUTSOURCE_SDI1);
  678. inputSources.insert(NTV2_INPUTSOURCE_SDI2);
  679. inputSources.insert(NTV2_INPUTSOURCE_SDI3);
  680. inputSources.insert(NTV2_INPUTSOURCE_SDI4);
  681. break;
  682. case IOSelection::SDI5__8:
  683. inputSources.insert(NTV2_INPUTSOURCE_SDI5);
  684. inputSources.insert(NTV2_INPUTSOURCE_SDI6);
  685. inputSources.insert(NTV2_INPUTSOURCE_SDI7);
  686. inputSources.insert(NTV2_INPUTSOURCE_SDI8);
  687. break;
  688. case IOSelection::HDMI1:
  689. inputSources.insert(NTV2_INPUTSOURCE_HDMI1);
  690. break;
  691. case IOSelection::HDMI2:
  692. inputSources.insert(NTV2_INPUTSOURCE_HDMI2);
  693. break;
  694. case IOSelection::HDMI3:
  695. inputSources.insert(NTV2_INPUTSOURCE_HDMI3);
  696. break;
  697. case IOSelection::HDMI4:
  698. inputSources.insert(NTV2_INPUTSOURCE_HDMI4);
  699. break;
  700. case IOSelection::HDMIMonitorIn:
  701. inputSources.insert(NTV2_INPUTSOURCE_HDMI1);
  702. break;
  703. case IOSelection::AnalogIn:
  704. inputSources.insert(NTV2_INPUTSOURCE_ANALOG1);
  705. break;
  706. default:
  707. case IOSelection::HDMIMonitorOut:
  708. case IOSelection::AnalogOut:
  709. case IOSelection::Invalid:
  710. break;
  711. }
  712. }
  713. void IOSelectionToOutputDests(IOSelection io,
  714. NTV2OutputDestinations &outputDests)
  715. {
  716. switch (io) {
  717. case IOSelection::SDI1:
  718. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
  719. break;
  720. case IOSelection::SDI2:
  721. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
  722. break;
  723. case IOSelection::SDI3:
  724. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
  725. break;
  726. case IOSelection::SDI4:
  727. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
  728. break;
  729. case IOSelection::SDI5:
  730. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI5);
  731. break;
  732. case IOSelection::SDI6:
  733. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI6);
  734. break;
  735. case IOSelection::SDI7:
  736. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI7);
  737. break;
  738. case IOSelection::SDI8:
  739. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI8);
  740. break;
  741. case IOSelection::SDI1_2:
  742. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
  743. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
  744. break;
  745. case IOSelection::SDI3_4:
  746. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
  747. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
  748. break;
  749. case IOSelection::SDI5_6:
  750. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI5);
  751. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI6);
  752. break;
  753. case IOSelection::SDI7_8:
  754. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI7);
  755. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI8);
  756. break;
  757. case IOSelection::SDI1__4:
  758. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI1);
  759. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI2);
  760. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI3);
  761. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI4);
  762. break;
  763. case IOSelection::SDI5__8:
  764. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI5);
  765. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI6);
  766. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI7);
  767. outputDests.insert(NTV2_OUTPUTDESTINATION_SDI8);
  768. break;
  769. case IOSelection::HDMIMonitorOut:
  770. outputDests.insert(NTV2_OUTPUTDESTINATION_HDMI);
  771. break;
  772. case IOSelection::AnalogOut:
  773. outputDests.insert(NTV2_OUTPUTDESTINATION_ANALOG);
  774. break;
  775. default:
  776. case IOSelection::HDMI1:
  777. case IOSelection::HDMI2:
  778. case IOSelection::HDMI3:
  779. case IOSelection::HDMI4:
  780. case IOSelection::HDMIMonitorIn:
  781. case IOSelection::AnalogIn:
  782. case IOSelection::Invalid:
  783. break;
  784. }
  785. }
  786. bool DeviceCanDoIOSelectionIn(NTV2DeviceID id, IOSelection io)
  787. {
  788. // Hide "HDMI1" list selection on devices which have a discrete "HDMI IN" port.
  789. if (io == IOSelection::HDMI1 && CardCanDoHDMIMonitorInput(id)) {
  790. return false;
  791. }
  792. if (io == IOSelection::HDMIMonitorIn && id == DEVICE_ID_KONAHDMI) {
  793. return false;
  794. }
  795. NTV2InputSourceSet inputSources;
  796. if (io != IOSelection::Invalid) {
  797. IOSelectionToInputSources(io, inputSources);
  798. size_t numSrcs = inputSources.size();
  799. size_t canDo = 0;
  800. if (numSrcs > 0) {
  801. for (auto &&inp : inputSources) {
  802. if (NTV2DeviceCanDoInputSource(id, inp))
  803. canDo++;
  804. }
  805. if (canDo == numSrcs)
  806. return true;
  807. }
  808. }
  809. return false;
  810. }
  811. bool DeviceCanDoIOSelectionOut(NTV2DeviceID id, IOSelection io)
  812. {
  813. NTV2OutputDestinations outputDests;
  814. if (io != IOSelection::Invalid) {
  815. IOSelectionToOutputDests(io, outputDests);
  816. size_t numOuts = outputDests.size();
  817. size_t canDo = 0;
  818. if (numOuts > 0) {
  819. for (auto &&out : outputDests) {
  820. if (NTV2DeviceCanDoOutputDestination(id, out))
  821. canDo++;
  822. }
  823. if (canDo == numOuts)
  824. return true;
  825. }
  826. }
  827. return false;
  828. }
  829. bool IsSDIOneWireIOSelection(IOSelection io)
  830. {
  831. bool result = false;
  832. switch (io) {
  833. case IOSelection::SDI1:
  834. case IOSelection::SDI2:
  835. case IOSelection::SDI3:
  836. case IOSelection::SDI4:
  837. case IOSelection::SDI5:
  838. case IOSelection::SDI6:
  839. case IOSelection::SDI7:
  840. case IOSelection::SDI8:
  841. result = true;
  842. break;
  843. default:
  844. result = false;
  845. }
  846. return result;
  847. }
  848. bool IsSDITwoWireIOSelection(IOSelection io)
  849. {
  850. bool result = false;
  851. switch (io) {
  852. case IOSelection::SDI1_2:
  853. case IOSelection::SDI3_4:
  854. case IOSelection::SDI5_6:
  855. case IOSelection::SDI7_8:
  856. result = true;
  857. break;
  858. default:
  859. result = false;
  860. }
  861. return result;
  862. }
  863. bool IsSDIFourWireIOSelection(IOSelection io)
  864. {
  865. bool result = false;
  866. switch (io) {
  867. case IOSelection::SDI1__4:
  868. case IOSelection::SDI5__8:
  869. result = true;
  870. break;
  871. default:
  872. result = false;
  873. }
  874. return result;
  875. }
  876. bool IsMonitorOutputSelection(NTV2DeviceID id, IOSelection io)
  877. {
  878. if (CardCanDoSDIMonitorOutput(id) && io == IOSelection::SDI5)
  879. return true;
  880. if (CardCanDoHDMIMonitorOutput(id) && io == IOSelection::HDMIMonitorOut)
  881. return true;
  882. return false;
  883. }
  884. bool IsIOSelectionSDI(IOSelection io)
  885. {
  886. if (io == IOSelection::SDI1 || io == IOSelection::SDI2 ||
  887. io == IOSelection::SDI3 || io == IOSelection::SDI4 ||
  888. io == IOSelection::SDI5 || io == IOSelection::SDI6 ||
  889. io == IOSelection::SDI7 || io == IOSelection::SDI8 ||
  890. io == IOSelection::SDI1_2 || io == IOSelection::SDI3_4 ||
  891. io == IOSelection::SDI5_6 || io == IOSelection::SDI7_8 ||
  892. io == IOSelection::SDI1__4 || io == IOSelection::SDI5__8) {
  893. return true;
  894. }
  895. return false;
  896. }
  897. bool IsIOSelectionHDMI(IOSelection io)
  898. {
  899. if (io == IOSelection::HDMI1 || io == IOSelection::HDMI2 ||
  900. io == IOSelection::HDMI3 || io == IOSelection::HDMI4 ||
  901. io == IOSelection::HDMIMonitorIn ||
  902. io == IOSelection::HDMIMonitorOut) {
  903. return true;
  904. }
  905. return false;
  906. }
  907. std::string MakeCardID(CNTV2Card &card)
  908. {
  909. std::string cardID;
  910. if (card.GetSerialNumberString(cardID)) {
  911. // Try to construct CardID from device ID and serial number...
  912. cardID = NTV2DeviceIDToString(card.GetDeviceID(), false) + "_" +
  913. cardID;
  914. } else {
  915. // ...otherwise fall back to the CNTV2DeviceScanner method.
  916. cardID = CNTV2DeviceScanner::GetDeviceRefName(card);
  917. }
  918. return cardID;
  919. }
  920. RasterDefinition DetermineRasterDefinition(NTV2VideoFormat vf)
  921. {
  922. RasterDefinition def = RasterDefinition::Unknown;
  923. if (NTV2_IS_SD_VIDEO_FORMAT(vf)) {
  924. def = RasterDefinition::SD;
  925. } else if (NTV2_IS_HD_VIDEO_FORMAT(vf)) {
  926. def = RasterDefinition::HD;
  927. } else if (NTV2_IS_QUAD_FRAME_FORMAT(vf)) {
  928. def = RasterDefinition::UHD_4K;
  929. } else if (NTV2_IS_QUAD_QUAD_FORMAT(vf)) {
  930. def = RasterDefinition::UHD2_8K;
  931. } else {
  932. def = RasterDefinition::Unknown;
  933. }
  934. return def;
  935. }
  936. inline bool IsStandard1080i(NTV2Standard standard)
  937. {
  938. if (standard == NTV2_STANDARD_1080 ||
  939. standard == NTV2_STANDARD_2Kx1080i) {
  940. return true;
  941. }
  942. return false;
  943. }
  944. inline bool IsStandard1080p(NTV2Standard standard)
  945. {
  946. if (standard == NTV2_STANDARD_1080p || standard == NTV2_STANDARD_2K ||
  947. standard == NTV2_STANDARD_2Kx1080p) {
  948. return true;
  949. }
  950. return false;
  951. }
  952. VPIDStandard DetermineVPIDStandard(IOSelection io, NTV2VideoFormat vf,
  953. NTV2PixelFormat pf, SDITransport trx,
  954. SDITransport4K t4k)
  955. {
  956. VPIDStandard vpid = VPIDStandard_Unknown;
  957. auto rd = aja::DetermineRasterDefinition(vf);
  958. auto standard = GetNTV2StandardFromVideoFormat(vf);
  959. bool is_rgb = NTV2_IS_FBF_RGB(pf);
  960. bool is_hfr =
  961. NTV2_IS_HIGH_NTV2FrameRate(GetNTV2FrameRateFromVideoFormat(vf));
  962. if (rd == RasterDefinition::SD) {
  963. vpid = VPIDStandard_483_576;
  964. } else if (rd == RasterDefinition::HD) {
  965. vpid = VPIDStandard_1080;
  966. if (aja::IsSDIOneWireIOSelection(io)) {
  967. if (is_rgb) {
  968. if (standard == NTV2_STANDARD_720) {
  969. if (trx == SDITransport::SingleLink) {
  970. vpid = VPIDStandard_720;
  971. } else if (trx ==
  972. SDITransport::SDI3Ga) {
  973. vpid = VPIDStandard_720_3Ga;
  974. } else if (trx ==
  975. SDITransport::SDI3Gb) {
  976. vpid = VPIDStandard_720_3Gb;
  977. }
  978. } else if (IsStandard1080p(standard)) {
  979. if (trx == SDITransport::SingleLink) {
  980. vpid = VPIDStandard_1080;
  981. } else if (trx ==
  982. SDITransport::SDI3Ga) {
  983. vpid = VPIDStandard_1080_3Ga;
  984. } else if (trx ==
  985. SDITransport::SDI3Gb) {
  986. vpid = VPIDStandard_1080_3Gb;
  987. }
  988. }
  989. } else {
  990. if (standard == NTV2_STANDARD_720) {
  991. vpid = VPIDStandard_720;
  992. } else if (IsStandard1080i(standard)) {
  993. vpid = VPIDStandard_1080;
  994. } else if (IsStandard1080p(standard) &&
  995. trx == SDITransport::SDI3Ga) {
  996. vpid = VPIDStandard_1080_3Ga;
  997. } else if (IsStandard1080p(standard) &&
  998. trx == SDITransport::SDI3Gb) {
  999. vpid = VPIDStandard_1080_3Gb;
  1000. }
  1001. }
  1002. } else if (aja::IsSDITwoWireIOSelection(io)) {
  1003. if (is_rgb) {
  1004. if (standard == NTV2_STANDARD_720) {
  1005. if (trx == SDITransport::SDI3Ga)
  1006. vpid = VPIDStandard_720_3Ga;
  1007. else if (trx == SDITransport::SDI3Gb)
  1008. vpid = VPIDStandard_720_3Gb;
  1009. } else if (IsStandard1080p(standard)) {
  1010. if (trx == SDITransport::HDDualLink) {
  1011. vpid = VPIDStandard_1080_DualLink;
  1012. } else if (trx == SDITransport::SDI3Ga)
  1013. vpid = VPIDStandard_1080_Dual_3Ga;
  1014. else if (trx == SDITransport::SDI3Gb)
  1015. vpid = VPIDStandard_1080_Dual_3Gb;
  1016. }
  1017. } else {
  1018. if (IsStandard1080p(standard) &&
  1019. trx == SDITransport::HDDualLink) {
  1020. vpid = VPIDStandard_1080_DualLink;
  1021. }
  1022. }
  1023. }
  1024. } else if (rd == RasterDefinition::UHD_4K) {
  1025. if (aja::IsSDIOneWireIOSelection(io)) {
  1026. if (is_rgb) {
  1027. if (trx == SDITransport::SDI6G) {
  1028. vpid = VPIDStandard_2160_DualLink;
  1029. } else if (trx == SDITransport::SDI12G) {
  1030. vpid = VPIDStandard_2160_DualLink_12Gb;
  1031. }
  1032. } else {
  1033. // YCbCr
  1034. if (trx == SDITransport::SDI6G) {
  1035. vpid = VPIDStandard_2160_Single_6Gb;
  1036. } else if (trx == SDITransport::SDI12G) {
  1037. vpid = VPIDStandard_2160_Single_12Gb;
  1038. } else {
  1039. vpid = VPIDStandard_2160_Single_6Gb; // fallback
  1040. }
  1041. }
  1042. } else if (aja::IsSDITwoWireIOSelection(io)) {
  1043. if (is_rgb) {
  1044. } else {
  1045. // YCbCr
  1046. if (t4k == SDITransport4K::Squares) {
  1047. vpid = VPIDStandard_1080;
  1048. } else if (t4k ==
  1049. SDITransport4K::TwoSampleInterleave) {
  1050. if (is_hfr &&
  1051. trx == SDITransport::SDI3Ga) {
  1052. vpid = VPIDStandard_2160_QuadLink_3Ga;
  1053. } else if (is_hfr &&
  1054. trx == SDITransport::SDI3Gb) {
  1055. vpid = VPIDStandard_2160_QuadDualLink_3Gb;
  1056. } else {
  1057. vpid = VPIDStandard_2160_DualLink;
  1058. }
  1059. }
  1060. }
  1061. } else if (aja::IsSDIFourWireIOSelection(io)) {
  1062. if (is_rgb) {
  1063. if (t4k == SDITransport4K::Squares) {
  1064. if (trx == SDITransport::SDI3Ga) {
  1065. vpid = VPIDStandard_1080_3Ga;
  1066. } else if (trx ==
  1067. SDITransport::SDI3Gb) {
  1068. vpid = VPIDStandard_1080_DualLink_3Gb;
  1069. }
  1070. }
  1071. } else {
  1072. // YCbCr
  1073. if (t4k == SDITransport4K::Squares) {
  1074. if (trx == SDITransport::SDI3Ga) {
  1075. vpid = VPIDStandard_1080_3Ga;
  1076. } else if (trx ==
  1077. SDITransport::SDI3Gb) {
  1078. vpid = VPIDStandard_1080_DualLink_3Gb;
  1079. } else {
  1080. vpid = VPIDStandard_1080;
  1081. }
  1082. } else if (t4k ==
  1083. SDITransport4K::TwoSampleInterleave) {
  1084. if (trx == SDITransport::SDI3Ga) {
  1085. vpid = VPIDStandard_2160_QuadLink_3Ga;
  1086. } else if (trx ==
  1087. SDITransport::SDI3Gb) {
  1088. vpid = VPIDStandard_2160_QuadDualLink_3Gb;
  1089. }
  1090. }
  1091. }
  1092. }
  1093. }
  1094. return vpid;
  1095. }
  1096. std::vector<NTV2DeviceID> MultiViewCards()
  1097. {
  1098. return {DEVICE_ID_IOX3};
  1099. }
  1100. } // namespace aja