aja-routing.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. #include "aja-card-manager.hpp"
  2. #include "aja-common.hpp"
  3. #include "aja-routing.hpp"
  4. #include "aja-widget-io.hpp"
  5. #include <ajabase/common/common.h>
  6. #include <ajantv2/includes/ntv2card.h>
  7. #include <ajantv2/includes/ntv2devicefeatures.h>
  8. #include <ajantv2/includes/ntv2utils.h>
  9. #include <obs-module.h>
  10. namespace aja {
  11. /*
  12. * Parse the widget routing shorthand string into a map of input and output NTV2CrosspointIDs.
  13. * For example "sdi[0][0]->fb[0][0]" is shorthand for connecting the output crosspoint for
  14. * SDI1/Datastream1 (NTV2_XptSDIIn1) to the input crosspoint for Framestore1/Datastream1 (NTV2_XptFrameBuffer1Input).
  15. * These routing shorthand strings are found within the RoutingConfig structs in the "routing" sub-directory of the plugin.
  16. */
  17. bool Routing::ParseRouteString(const std::string &route,
  18. NTV2XptConnections &cnx)
  19. {
  20. blog(LOG_DEBUG, "aja::Routing::ParseRouteString: Input string: %s",
  21. route.c_str());
  22. std::string route_strip(route);
  23. aja::lower(route_strip);
  24. aja::replace(route_strip, " ", "");
  25. if (route_strip.empty()) {
  26. blog(LOG_DEBUG,
  27. "Routing::ParseRouteString: input string is empty!");
  28. return false;
  29. }
  30. /* TODO(paulh): Tally up the lines and tokens and check that they are all parsed OK.
  31. * Right now we just return true if ANY tokens were parsed. This is OK _for now_ because
  32. * the route strings currently only come from a known set.
  33. */
  34. NTV2StringList lines;
  35. lines = aja::split(route_strip, ';');
  36. if (lines.empty())
  37. lines.push_back(route_strip);
  38. int32_t parse_ok = 0;
  39. for (const auto &l : lines) {
  40. if (l.empty()) {
  41. blog(LOG_DEBUG,
  42. "aja::Routing::ParseRouteString: Empty line!");
  43. continue;
  44. }
  45. blog(LOG_DEBUG, "aja::Routing::ParseRouteString: Line: %s",
  46. l.c_str());
  47. NTV2StringList tokens = aja::split(l, "->");
  48. if (tokens.empty() || tokens.size() != 2) {
  49. blog(LOG_DEBUG,
  50. "aja::Routing::ParseRouteString: Invalid token count!");
  51. continue;
  52. }
  53. const std::string &left = tokens[0]; // output crosspoint
  54. const std::string &right = tokens[1]; // input crosspoint
  55. if (left.empty() || left.length() > 64) {
  56. blog(LOG_DEBUG,
  57. "aja::Routing::ParseRouteString: Invalid Left token!");
  58. continue;
  59. }
  60. if (right.empty() || right.length() > 64) {
  61. blog(LOG_DEBUG,
  62. "aja::Routing::ParseRouteString: Invalid right token!");
  63. continue;
  64. }
  65. blog(LOG_DEBUG,
  66. "aja::Routing::ParseRouteString: Left Token: %s -> Right Token: %s",
  67. left.c_str(), right.c_str());
  68. // Parse Output Crosspoint from left token
  69. int32_t out_chan = 0;
  70. int32_t out_ds = 0;
  71. std::string out_name(64, ' ');
  72. if (std::sscanf(left.c_str(), "%[A-Za-z_0-9][%d][%d]",
  73. &out_name[0], &out_chan, &out_ds)) {
  74. out_name = aja::rstrip(out_name).substr(
  75. 0, out_name.find_first_of('\0'));
  76. WidgetOutputSocket widget_out;
  77. if (WidgetOutputSocket::Find(out_name,
  78. (NTV2Channel)out_chan,
  79. out_ds, widget_out)) {
  80. blog(LOG_DEBUG,
  81. "aja::Routing::ParseRouteString: Found NTV2OutputCrosspointID %s",
  82. NTV2OutputCrosspointIDToString(
  83. widget_out.id)
  84. .c_str());
  85. // Parse Input Crosspoint from right token
  86. int32_t inp_chan = 0;
  87. int32_t inp_ds = 0;
  88. std::string inp_name(64, ' ');
  89. if (std::sscanf(right.c_str(),
  90. "%[A-Za-z_0-9][%d][%d]",
  91. &inp_name[0], &inp_chan,
  92. &inp_ds)) {
  93. inp_name = aja::rstrip(inp_name).substr(
  94. 0,
  95. inp_name.find_first_of('\0'));
  96. WidgetInputSocket widget_inp;
  97. if (WidgetInputSocket::Find(
  98. inp_name,
  99. (NTV2Channel)inp_chan,
  100. inp_ds, widget_inp)) {
  101. blog(LOG_DEBUG,
  102. "aja::Routing::ParseRouteString: Found NTV2InputCrosspointID %s",
  103. NTV2InputCrosspointIDToString(
  104. widget_inp.id)
  105. .c_str());
  106. cnx[widget_inp.id] =
  107. widget_out.id;
  108. parse_ok++;
  109. } else {
  110. blog(LOG_DEBUG,
  111. "aja::Routing::ParseRouteString: NTV2InputCrosspointID not found!");
  112. }
  113. }
  114. } else {
  115. blog(LOG_DEBUG,
  116. "aja::Routing::ParseRouteString: NTV2OutputCrosspointID not found!");
  117. }
  118. }
  119. }
  120. return parse_ok > 0;
  121. }
  122. void Routing::StartSourceAudio(const SourceProps &props, CNTV2Card *card)
  123. {
  124. if (!card)
  125. return;
  126. auto inputSrc = props.InitialInputSource();
  127. auto channel = props.Channel();
  128. auto audioSys = props.AudioSystem();
  129. card->WriteAudioSource(0, channel);
  130. card->SetAudioSystemInputSource(
  131. audioSys, NTV2InputSourceToAudioSource(inputSrc),
  132. NTV2InputSourceToEmbeddedAudioInput(inputSrc));
  133. card->SetNumberAudioChannels(kDefaultAudioChannels, audioSys);
  134. card->SetAudioRate(props.AudioRate(), audioSys);
  135. card->SetAudioBufferSize(NTV2_AUDIO_BUFFER_BIG, audioSys);
  136. // Fix for AJA NTV2 internal bug #11467
  137. ULWord magicAudioBits = 0;
  138. if (NTV2_INPUT_SOURCE_IS_HDMI(inputSrc)) {
  139. switch (inputSrc) {
  140. default:
  141. case NTV2_INPUTSOURCE_HDMI1:
  142. magicAudioBits = 0x00100000;
  143. break;
  144. case NTV2_INPUTSOURCE_HDMI2:
  145. magicAudioBits = 0x00110000;
  146. break;
  147. case NTV2_INPUTSOURCE_HDMI3:
  148. magicAudioBits = 0x00900000;
  149. break;
  150. case NTV2_INPUTSOURCE_HDMI4:
  151. magicAudioBits = 0x00910000;
  152. break;
  153. }
  154. } else if (NTV2_INPUT_SOURCE_IS_ANALOG(inputSrc)) {
  155. magicAudioBits = 0x00000990;
  156. } else { // SDI
  157. magicAudioBits = 0x00000320;
  158. }
  159. // TODO(paulh): Ask aja-seanl about these deprecated calls and if they are still needed
  160. ULWord oldValue = 0;
  161. if (card->ReadAudioSource(oldValue, channel)) {
  162. card->WriteAudioSource(oldValue | magicAudioBits, channel);
  163. }
  164. for (int a = 0; a < NTV2DeviceGetNumAudioSystems(card->GetDeviceID());
  165. a++) {
  166. card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_ON,
  167. NTV2AudioSystem(a));
  168. }
  169. card->StartAudioInput(audioSys);
  170. card->SetAudioCaptureEnable(audioSys, true);
  171. }
  172. void Routing::StopSourceAudio(const SourceProps &props, CNTV2Card *card)
  173. {
  174. if (card) {
  175. auto audioSys = props.AudioSystem();
  176. card->SetAudioCaptureEnable(audioSys, false);
  177. card->StopAudioInput(audioSys);
  178. }
  179. }
  180. bool Routing::ConfigureSourceRoute(const SourceProps &props, NTV2Mode mode,
  181. CNTV2Card *card, NTV2XptConnections &cnx)
  182. {
  183. if (!card)
  184. return false;
  185. auto deviceID = props.deviceID;
  186. NTV2VideoFormat vf = props.videoFormat;
  187. auto init_src = props.InitialInputSource();
  188. auto init_channel = props.Channel();
  189. RoutingConfigurator rc;
  190. RoutingPreset rp;
  191. ConnectionKind kind = ConnectionKind::Unknown;
  192. VPIDStandard vpidStandard = VPIDStandard_Unknown;
  193. HDMIWireFormat hwf = HDMIWireFormat::Unknown;
  194. if (NTV2_INPUT_SOURCE_IS_SDI(init_src)) {
  195. kind = ConnectionKind::SDI;
  196. if (props.autoDetect) {
  197. auto vpidList = props.vpids;
  198. if (vpidList.size() > 0)
  199. vpidStandard = vpidList.at(0).Standard();
  200. }
  201. if (vpidStandard == VPIDStandard_Unknown) {
  202. vpidStandard = DetermineVPIDStandard(
  203. props.ioSelect, props.videoFormat,
  204. props.pixelFormat, props.sdiTransport,
  205. props.sdi4kTransport);
  206. }
  207. } else if (NTV2_INPUT_SOURCE_IS_HDMI(init_src)) {
  208. kind = ConnectionKind::HDMI;
  209. if (NTV2_IS_FBF_RGB(props.pixelFormat)) {
  210. if (NTV2_IS_HD_VIDEO_FORMAT(vf)) {
  211. hwf = HDMIWireFormat::SD_HD_RGB;
  212. } else if (NTV2_IS_4K_VIDEO_FORMAT(vf)) {
  213. hwf = HDMIWireFormat::UHD_4K_RGB;
  214. }
  215. } else {
  216. if (NTV2_IS_HD_VIDEO_FORMAT(vf)) {
  217. hwf = HDMIWireFormat::SD_HD_YCBCR;
  218. } else if (NTV2_IS_4K_VIDEO_FORMAT(vf)) {
  219. hwf = HDMIWireFormat::UHD_4K_YCBCR;
  220. }
  221. }
  222. } else {
  223. blog(LOG_WARNING,
  224. "Unsupported connection kind. SDI and HDMI only!");
  225. return false;
  226. }
  227. if (!rc.FindFirstPreset(kind, props.deviceID, NTV2_MODE_CAPTURE, vf,
  228. props.pixelFormat, vpidStandard, hwf, rp)) {
  229. blog(LOG_WARNING, "No SDI capture routing preset found!");
  230. return false;
  231. }
  232. LogRoutingPreset(rp);
  233. // Substitute channel placeholders for actual indices
  234. std::string route_string = rp.route_string;
  235. // Channel-substitution for widgets associated with framestore channel(s)
  236. ULWord start_framestore_index =
  237. GetIndexForNTV2Channel(props.Framestore());
  238. const std::vector<std::string> fs_associated = {"fb", "tsi", "dli"};
  239. for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
  240. for (const auto &name : fs_associated) {
  241. std::string placeholder = std::string(
  242. name + "[{ch" + aja::to_string(c + 1) + "}]");
  243. aja::replace(
  244. route_string, placeholder,
  245. name + "[" +
  246. aja::to_string(start_framestore_index) +
  247. "]");
  248. }
  249. start_framestore_index++;
  250. }
  251. // Replace other channel placeholders
  252. ULWord start_channel_index = GetIndexForNTV2Channel(init_channel);
  253. for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
  254. std::string channel_placeholder =
  255. std::string("{ch" + aja::to_string(c + 1) + "}");
  256. aja::replace(route_string, channel_placeholder,
  257. aja::to_string(start_channel_index++));
  258. }
  259. if (!ParseRouteString(route_string, cnx))
  260. return false;
  261. card->ApplySignalRoute(cnx, false);
  262. // Apply SDI widget settings
  263. start_channel_index = GetIndexForNTV2Channel(init_channel);
  264. for (uint32_t i = (uint32_t)start_channel_index;
  265. i < (start_channel_index + rp.num_channels); i++) {
  266. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  267. if (::NTV2DeviceHasBiDirectionalSDI(deviceID)) {
  268. card->SetSDITransmitEnable(channel,
  269. mode == NTV2_MODE_DISPLAY);
  270. }
  271. card->SetSDIOut3GEnable(channel, rp.flags & kEnable3GOut);
  272. card->SetSDIOut3GbEnable(channel, rp.flags & kEnable3GbOut);
  273. card->SetSDIOut6GEnable(channel, rp.flags & kEnable6GOut);
  274. card->SetSDIOut12GEnable(channel, rp.flags & kEnable12GOut);
  275. card->SetSDIInLevelBtoLevelAConversion((UWord)i,
  276. rp.flags & kConvert3GIn);
  277. card->SetSDIOutLevelAtoLevelBConversion(
  278. (UWord)i, rp.flags & kConvert3GOut);
  279. card->SetSDIOutRGBLevelAConversion(
  280. (UWord)i, rp.flags & kConvert3GaRGBOut);
  281. }
  282. // Apply HDMI settings
  283. if (aja::IsIOSelectionHDMI(props.ioSelect)) {
  284. if (NTV2_IS_4K_VIDEO_FORMAT(props.videoFormat))
  285. card->SetHDMIV2Mode(NTV2_HDMI_V2_4K_CAPTURE);
  286. else
  287. card->SetHDMIV2Mode(NTV2_HDMI_V2_HDSD_BIDIRECTIONAL);
  288. }
  289. // Apply Framestore settings
  290. start_framestore_index = GetIndexForNTV2Channel(props.Framestore());
  291. for (uint32_t i = (uint32_t)start_framestore_index;
  292. i < (start_framestore_index + rp.num_framestores); i++) {
  293. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  294. card->EnableChannel(channel);
  295. card->SetMode(channel, mode);
  296. card->SetVANCMode(NTV2_VANCMODE_OFF, channel);
  297. card->SetVideoFormat(vf, false, false, channel);
  298. card->SetFrameBufferFormat(channel, props.pixelFormat);
  299. card->SetTsiFrameEnable(rp.flags & kEnable4KTSI, channel);
  300. card->Set4kSquaresEnable(rp.flags & kEnable4KSquares, channel);
  301. card->SetQuadQuadSquaresEnable(rp.flags & kEnable8KSquares,
  302. channel);
  303. }
  304. return true;
  305. }
  306. bool Routing::ConfigureOutputRoute(const OutputProps &props, NTV2Mode mode,
  307. CNTV2Card *card, NTV2XptConnections &cnx)
  308. {
  309. if (!card)
  310. return false;
  311. auto deviceID = props.deviceID;
  312. NTV2OutputDestinations outputDests;
  313. aja::IOSelectionToOutputDests(props.ioSelect, outputDests);
  314. if (outputDests.empty()) {
  315. blog(LOG_DEBUG,
  316. "No Output Destinations specified to configure routing!");
  317. return false;
  318. }
  319. auto init_dest = *outputDests.begin();
  320. RoutingConfigurator rc;
  321. RoutingPreset rp;
  322. ConnectionKind kind = ConnectionKind::Unknown;
  323. VPIDStandard vpidStandard = VPIDStandard_Unknown;
  324. HDMIWireFormat hwf = HDMIWireFormat::Unknown;
  325. if (NTV2_OUTPUT_DEST_IS_SDI(init_dest)) {
  326. kind = ConnectionKind::SDI;
  327. vpidStandard = DetermineVPIDStandard(
  328. props.ioSelect, props.videoFormat, props.pixelFormat,
  329. props.sdiTransport, props.sdi4kTransport);
  330. } else if (NTV2_OUTPUT_DEST_IS_HDMI(init_dest)) {
  331. kind = ConnectionKind::HDMI;
  332. hwf = HDMIWireFormat::Unknown;
  333. if (NTV2_IS_FBF_RGB(props.pixelFormat)) {
  334. if (NTV2_IS_HD_VIDEO_FORMAT(props.videoFormat)) {
  335. hwf = HDMIWireFormat::SD_HD_RGB;
  336. } else if (NTV2_IS_4K_VIDEO_FORMAT(props.videoFormat)) {
  337. hwf = HDMIWireFormat::UHD_4K_RGB;
  338. }
  339. } else {
  340. if (NTV2_IS_HD_VIDEO_FORMAT(props.videoFormat)) {
  341. hwf = HDMIWireFormat::SD_HD_YCBCR;
  342. } else if (NTV2_IS_4K_VIDEO_FORMAT(props.videoFormat)) {
  343. hwf = HDMIWireFormat::UHD_4K_YCBCR;
  344. }
  345. }
  346. } else {
  347. blog(LOG_WARNING,
  348. "Unsupported connection kind. SDI and HDMI only!");
  349. return false;
  350. }
  351. if (!rc.FindFirstPreset(kind, props.deviceID, NTV2_MODE_DISPLAY,
  352. props.videoFormat, props.pixelFormat,
  353. vpidStandard, hwf, rp)) {
  354. blog(LOG_WARNING, "No HDMI output routing preset found!");
  355. return false;
  356. }
  357. LogRoutingPreset(rp);
  358. std::string route_string = rp.route_string;
  359. // Replace framestore channel placeholders
  360. auto init_channel = NTV2OutputDestinationToChannel(init_dest);
  361. ULWord start_framestore_index =
  362. GetIndexForNTV2Channel(props.Framestore());
  363. if (rp.verbatim) {
  364. // Presets marked "verbatim" must only be routed on the specified channels
  365. start_framestore_index = 0;
  366. init_channel = NTV2_CHANNEL1;
  367. }
  368. // Channel-substitution for widgets associated with framestore channel(s)
  369. const std::vector<std::string> fs_associated = {"fb", "tsi", "dlo"};
  370. for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
  371. for (const auto &name : fs_associated) {
  372. std::string placeholder = std::string(
  373. name + "[{ch" + aja::to_string(c + 1) + "}]");
  374. aja::replace(
  375. route_string, placeholder,
  376. name + "[" +
  377. aja::to_string(start_framestore_index) +
  378. "]");
  379. }
  380. start_framestore_index++;
  381. }
  382. // Replace other channel placeholders
  383. ULWord start_channel_index = GetIndexForNTV2Channel(init_channel);
  384. for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
  385. std::string channel_placeholder =
  386. std::string("{ch" + aja::to_string(c + 1) + "}");
  387. aja::replace(route_string, channel_placeholder,
  388. aja::to_string(start_channel_index++));
  389. }
  390. if (!ParseRouteString(route_string, cnx))
  391. return false;
  392. card->ApplySignalRoute(cnx, false);
  393. // Apply SDI widget settings
  394. if (props.ioSelect != IOSelection::HDMIMonitorOut ||
  395. props.deviceID == DEVICE_ID_TTAP_PRO) {
  396. start_channel_index = GetIndexForNTV2Channel(init_channel);
  397. for (uint32_t i = (uint32_t)start_channel_index;
  398. i < (start_channel_index + rp.num_channels); i++) {
  399. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  400. if (::NTV2DeviceHasBiDirectionalSDI(deviceID)) {
  401. card->SetSDITransmitEnable(
  402. channel, mode == NTV2_MODE_DISPLAY);
  403. }
  404. card->SetSDIOut3GEnable(channel,
  405. rp.flags & kEnable3GOut);
  406. card->SetSDIOut3GbEnable(channel,
  407. rp.flags & kEnable3GbOut);
  408. card->SetSDIOut6GEnable(channel,
  409. rp.flags & kEnable6GOut);
  410. card->SetSDIOut12GEnable(channel,
  411. rp.flags & kEnable12GOut);
  412. card->SetSDIInLevelBtoLevelAConversion(
  413. (UWord)i, rp.flags & kConvert3GIn);
  414. card->SetSDIOutLevelAtoLevelBConversion(
  415. (UWord)i, rp.flags & kConvert3GOut);
  416. card->SetSDIOutRGBLevelAConversion(
  417. (UWord)i, rp.flags & kConvert3GaRGBOut);
  418. }
  419. }
  420. // Apply Framestore settings
  421. start_framestore_index = GetIndexForNTV2Channel(props.Framestore());
  422. if (rp.verbatim) {
  423. start_framestore_index = 0;
  424. }
  425. for (uint32_t i = (uint32_t)start_framestore_index;
  426. i < (start_framestore_index + rp.num_framestores); i++) {
  427. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  428. card->EnableChannel(channel);
  429. card->SetMode(channel, mode);
  430. card->SetVANCMode(NTV2_VANCMODE_OFF, channel);
  431. card->SetVideoFormat(props.videoFormat, false, false, channel);
  432. card->SetFrameBufferFormat(channel, props.pixelFormat);
  433. card->SetTsiFrameEnable(rp.flags & kEnable4KTSI, channel);
  434. card->Set4kSquaresEnable(rp.flags & kEnable4KSquares, channel);
  435. card->SetQuadQuadSquaresEnable(rp.flags & kEnable8KSquares,
  436. channel);
  437. }
  438. return true;
  439. }
  440. void Routing::ConfigureOutputAudio(const OutputProps &props, CNTV2Card *card)
  441. {
  442. if (!card)
  443. return;
  444. auto deviceID = card->GetDeviceID();
  445. auto audioSys = props.AudioSystem();
  446. auto channel = props.Channel();
  447. card->SetNumberAudioChannels(props.audioNumChannels, audioSys);
  448. card->SetAudioRate(props.AudioRate(), audioSys);
  449. card->SetAudioBufferSize(NTV2_AUDIO_BUFFER_BIG, audioSys);
  450. card->SetAudioOutputDelay(audioSys, 0);
  451. card->SetSDIOutputAudioSystem(channel, audioSys);
  452. card->SetSDIOutputDS2AudioSystem(channel, audioSys);
  453. /* NOTE(paulh):
  454. * The SDK has a specifies an SDI audio system by Channel rather than by SDI output
  455. * and certain devices require setting the SDI audio system to NTV2_CHANNEL1.
  456. * i.e.
  457. * SDI 1 = NTV2_CHANNEL1
  458. * SDI 2 = NTV2_CHANNEL2
  459. * ...
  460. * SDI 5/Monitor = NTV2_CHANNEL5
  461. * etc...
  462. *
  463. * This fixes AJA internal bugs: 10730, 10986, 16274
  464. */
  465. if (deviceID == DEVICE_ID_IOXT || deviceID == DEVICE_ID_IO4KUFC ||
  466. deviceID == DEVICE_ID_IO4KPLUS || deviceID == DEVICE_ID_KONA1 ||
  467. deviceID == DEVICE_ID_KONA3G || deviceID == DEVICE_ID_KONA4UFC ||
  468. deviceID == DEVICE_ID_KONA5 || deviceID == DEVICE_ID_KONA5_2X4K) {
  469. // Make sure SDI out 1 (aka Channel 1) is set to the correct sub-system
  470. card->SetSDIOutputAudioSystem(NTV2_CHANNEL1, audioSys);
  471. card->SetSDIOutputDS2AudioSystem(NTV2_CHANNEL1, audioSys);
  472. }
  473. // make sure that audio is setup for the SDI monitor output on devices that support it
  474. if (NTV2DeviceCanDoWidget(deviceID, NTV2_WgtSDIMonOut1)) {
  475. card->SetSDIOutputAudioSystem(NTV2_CHANNEL5, audioSys);
  476. card->SetSDIOutputDS2AudioSystem(NTV2_CHANNEL5, audioSys);
  477. }
  478. card->SetHDMIOutAudioRate(props.AudioRate());
  479. card->SetHDMIOutAudioFormat(NTV2_AUDIO_FORMAT_LPCM);
  480. card->SetAudioOutputMonitorSource(NTV2_AudioChannel1_2, channel);
  481. card->SetAESOutputSource(NTV2_AudioChannel1_4, audioSys,
  482. NTV2_AudioChannel1_4);
  483. card->SetAESOutputSource(NTV2_AudioChannel5_8, audioSys,
  484. NTV2_AudioChannel5_8);
  485. card->SetAESOutputSource(NTV2_AudioChannel9_12, audioSys,
  486. NTV2_AudioChannel9_12);
  487. card->SetAESOutputSource(NTV2_AudioChannel13_16, audioSys,
  488. NTV2_AudioChannel13_16);
  489. // make sure that audio is setup for HDMI output on devices that support it
  490. if (NTV2DeviceGetNumHDMIVideoOutputs(deviceID) > 0) {
  491. if (NTV2DeviceCanDoAudioMixer(deviceID)) {
  492. card->SetAudioMixerInputAudioSystem(
  493. NTV2_AudioMixerInputMain, audioSys);
  494. card->SetAudioMixerInputChannelSelect(
  495. NTV2_AudioMixerInputMain, NTV2_AudioChannel1_2);
  496. card->SetAudioMixerInputChannelsMute(
  497. NTV2_AudioMixerInputAux1,
  498. NTV2AudioChannelsMuteAll);
  499. card->SetAudioMixerInputChannelsMute(
  500. NTV2_AudioMixerInputAux2,
  501. NTV2AudioChannelsMuteAll);
  502. }
  503. card->SetHDMIOutAudioChannels(NTV2_HDMIAudio8Channels);
  504. card->SetHDMIOutAudioSource2Channel(NTV2_AudioChannel1_2,
  505. audioSys);
  506. card->SetHDMIOutAudioSource8Channel(NTV2_AudioChannel1_8,
  507. audioSys);
  508. }
  509. card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_OFF, audioSys);
  510. card->StopAudioOutput(audioSys);
  511. }
  512. void Routing::LogRoutingPreset(const RoutingPreset &rp)
  513. {
  514. auto hexStr = [&](uint8_t val) -> std::string {
  515. std::stringstream ss;
  516. ss << std::setfill('0') << std::setw(sizeof(uint8_t) * 2)
  517. << std::hex << (val | 0);
  518. return ss.str();
  519. };
  520. std::stringstream ss;
  521. ss << "\nPreset: " << rp.name;
  522. if (rp.kind == ConnectionKind::SDI) {
  523. ss << "\nVPID Standard: 0x"
  524. << hexStr(static_cast<uint8_t>(rp.vpid_standard));
  525. }
  526. ss << "\nMode: " << NTV2ModeToString(rp.mode)
  527. << "\nChannels: " << rp.num_channels
  528. << "\nFramestores: " << rp.num_framestores;
  529. blog(LOG_INFO, "[ AJA Crosspoint Routing Preset ]%s", ss.str().c_str());
  530. if (rp.device_ids.size() > 0) {
  531. ss.clear();
  532. for (auto id : rp.device_ids) {
  533. ss << " - " << NTV2DeviceIDToString(id) << "\n";
  534. }
  535. blog(LOG_INFO, "\nCompatible Device IDs: \n%s",
  536. ss.str().c_str());
  537. }
  538. }
  539. } // namespace aja