aja-common.cpp 31 KB

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