aja-routing.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  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_lower(route);
  23. route_lower = aja::lower(route_lower);
  24. const std::string &route_strip = aja::replace(route_lower, " ", "");
  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. route_string = 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. route_string =
  257. aja::replace(route_string, channel_placeholder,
  258. aja::to_string(start_channel_index++));
  259. }
  260. if (!ParseRouteString(route_string, cnx))
  261. return false;
  262. card->ApplySignalRoute(cnx, false);
  263. // Apply SDI widget settings
  264. start_channel_index = GetIndexForNTV2Channel(init_channel);
  265. for (uint32_t i = (uint32_t)start_channel_index;
  266. i < (start_channel_index + rp.num_channels); i++) {
  267. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  268. if (::NTV2DeviceHasBiDirectionalSDI(deviceID)) {
  269. card->SetSDITransmitEnable(channel,
  270. mode == NTV2_MODE_DISPLAY);
  271. }
  272. card->SetSDIOut3GEnable(channel, rp.flags & kEnable3GOut);
  273. card->SetSDIOut3GbEnable(channel, rp.flags & kEnable3GbOut);
  274. card->SetSDIOut6GEnable(channel, rp.flags & kEnable6GOut);
  275. card->SetSDIOut12GEnable(channel, rp.flags & kEnable12GOut);
  276. card->SetSDIInLevelBtoLevelAConversion((UWord)i,
  277. rp.flags & kConvert3GIn);
  278. card->SetSDIOutLevelAtoLevelBConversion(
  279. (UWord)i, rp.flags & kConvert3GOut);
  280. card->SetSDIOutRGBLevelAConversion(
  281. (UWord)i, rp.flags & kConvert3GaRGBOut);
  282. }
  283. // Apply HDMI settings
  284. if (aja::IsIOSelectionHDMI(props.ioSelect)) {
  285. if (NTV2_IS_4K_VIDEO_FORMAT(props.videoFormat))
  286. card->SetHDMIV2Mode(NTV2_HDMI_V2_4K_CAPTURE);
  287. else
  288. card->SetHDMIV2Mode(NTV2_HDMI_V2_HDSD_BIDIRECTIONAL);
  289. }
  290. // Apply Framestore settings
  291. start_framestore_index = GetIndexForNTV2Channel(props.Framestore());
  292. for (uint32_t i = (uint32_t)start_framestore_index;
  293. i < (start_framestore_index + rp.num_framestores); i++) {
  294. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  295. card->EnableChannel(channel);
  296. card->SetMode(channel, mode);
  297. card->SetVANCMode(NTV2_VANCMODE_OFF, channel);
  298. card->SetVideoFormat(vf, false, false, channel);
  299. card->SetFrameBufferFormat(channel, props.pixelFormat);
  300. card->SetTsiFrameEnable(rp.flags & kEnable4KTSI, channel);
  301. card->Set4kSquaresEnable(rp.flags & kEnable4KSquares, channel);
  302. card->SetQuadQuadSquaresEnable(rp.flags & kEnable8KSquares,
  303. channel);
  304. }
  305. return true;
  306. }
  307. bool Routing::ConfigureOutputRoute(const OutputProps &props, NTV2Mode mode,
  308. CNTV2Card *card, NTV2XptConnections &cnx)
  309. {
  310. if (!card)
  311. return false;
  312. auto deviceID = props.deviceID;
  313. NTV2OutputDestinations outputDests;
  314. aja::IOSelectionToOutputDests(props.ioSelect, outputDests);
  315. if (outputDests.empty()) {
  316. blog(LOG_DEBUG,
  317. "No Output Destinations specified to configure routing!");
  318. return false;
  319. }
  320. auto init_dest = *outputDests.begin();
  321. RoutingConfigurator rc;
  322. RoutingPreset rp;
  323. ConnectionKind kind = ConnectionKind::Unknown;
  324. VPIDStandard vpidStandard = VPIDStandard_Unknown;
  325. HDMIWireFormat hwf = HDMIWireFormat::Unknown;
  326. if (NTV2_OUTPUT_DEST_IS_SDI(init_dest)) {
  327. kind = ConnectionKind::SDI;
  328. vpidStandard = DetermineVPIDStandard(
  329. props.ioSelect, props.videoFormat, props.pixelFormat,
  330. props.sdiTransport, props.sdi4kTransport);
  331. } else if (NTV2_OUTPUT_DEST_IS_HDMI(init_dest)) {
  332. kind = ConnectionKind::HDMI;
  333. hwf = HDMIWireFormat::Unknown;
  334. if (NTV2_IS_FBF_RGB(props.pixelFormat)) {
  335. if (NTV2_IS_HD_VIDEO_FORMAT(props.videoFormat)) {
  336. hwf = HDMIWireFormat::SD_HD_RGB;
  337. } else if (NTV2_IS_4K_VIDEO_FORMAT(props.videoFormat)) {
  338. hwf = HDMIWireFormat::UHD_4K_RGB;
  339. }
  340. } else {
  341. if (NTV2_IS_HD_VIDEO_FORMAT(props.videoFormat)) {
  342. hwf = HDMIWireFormat::SD_HD_YCBCR;
  343. } else if (NTV2_IS_4K_VIDEO_FORMAT(props.videoFormat)) {
  344. hwf = HDMIWireFormat::UHD_4K_YCBCR;
  345. }
  346. }
  347. } else {
  348. blog(LOG_WARNING,
  349. "Unsupported connection kind. SDI and HDMI only!");
  350. return false;
  351. }
  352. if (!rc.FindFirstPreset(kind, props.deviceID, NTV2_MODE_DISPLAY,
  353. props.videoFormat, props.pixelFormat,
  354. vpidStandard, hwf, rp)) {
  355. blog(LOG_WARNING, "No HDMI output routing preset found!");
  356. return false;
  357. }
  358. LogRoutingPreset(rp);
  359. std::string route_string = rp.route_string;
  360. // Replace framestore channel placeholders
  361. auto init_channel = NTV2OutputDestinationToChannel(init_dest);
  362. ULWord start_framestore_index =
  363. GetIndexForNTV2Channel(props.Framestore());
  364. if (rp.verbatim) {
  365. // Presets marked "verbatim" must only be routed on the specified channels
  366. start_framestore_index = 0;
  367. init_channel = NTV2_CHANNEL1;
  368. }
  369. // Channel-substitution for widgets associated with framestore channel(s)
  370. const std::vector<std::string> fs_associated = {"fb", "tsi", "dlo"};
  371. for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
  372. for (const auto &name : fs_associated) {
  373. std::string placeholder = std::string(
  374. name + "[{ch" + aja::to_string(c + 1) + "}]");
  375. route_string = aja::replace(
  376. route_string, placeholder,
  377. name + "[" +
  378. aja::to_string(start_framestore_index) +
  379. "]");
  380. }
  381. start_framestore_index++;
  382. }
  383. // Replace other channel placeholders
  384. ULWord start_channel_index = GetIndexForNTV2Channel(init_channel);
  385. for (ULWord c = 0; c < NTV2_MAX_NUM_CHANNELS; c++) {
  386. std::string channel_placeholder =
  387. std::string("{ch" + aja::to_string(c + 1) + "}");
  388. route_string =
  389. aja::replace(route_string, channel_placeholder,
  390. aja::to_string(start_channel_index++));
  391. }
  392. if (!ParseRouteString(route_string, cnx))
  393. return false;
  394. card->ApplySignalRoute(cnx, false);
  395. // Apply SDI widget settings
  396. if (props.ioSelect != IOSelection::HDMIMonitorOut ||
  397. props.deviceID == DEVICE_ID_TTAP_PRO) {
  398. start_channel_index = GetIndexForNTV2Channel(init_channel);
  399. for (uint32_t i = (uint32_t)start_channel_index;
  400. i < (start_channel_index + rp.num_channels); i++) {
  401. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  402. if (::NTV2DeviceHasBiDirectionalSDI(deviceID)) {
  403. card->SetSDITransmitEnable(
  404. channel, mode == NTV2_MODE_DISPLAY);
  405. }
  406. card->SetSDIOut3GEnable(channel,
  407. rp.flags & kEnable3GOut);
  408. card->SetSDIOut3GbEnable(channel,
  409. rp.flags & kEnable3GbOut);
  410. card->SetSDIOut6GEnable(channel,
  411. rp.flags & kEnable6GOut);
  412. card->SetSDIOut12GEnable(channel,
  413. rp.flags & kEnable12GOut);
  414. card->SetSDIInLevelBtoLevelAConversion(
  415. (UWord)i, rp.flags & kConvert3GIn);
  416. card->SetSDIOutLevelAtoLevelBConversion(
  417. (UWord)i, rp.flags & kConvert3GOut);
  418. card->SetSDIOutRGBLevelAConversion(
  419. (UWord)i, rp.flags & kConvert3GaRGBOut);
  420. }
  421. }
  422. // Apply Framestore settings
  423. start_framestore_index = GetIndexForNTV2Channel(props.Framestore());
  424. if (rp.verbatim) {
  425. start_framestore_index = 0;
  426. }
  427. for (uint32_t i = (uint32_t)start_framestore_index;
  428. i < (start_framestore_index + rp.num_framestores); i++) {
  429. NTV2Channel channel = GetNTV2ChannelForIndex(i);
  430. card->EnableChannel(channel);
  431. card->SetMode(channel, mode);
  432. card->SetVANCMode(NTV2_VANCMODE_OFF, channel);
  433. card->SetVideoFormat(props.videoFormat, false, false, channel);
  434. card->SetFrameBufferFormat(channel, props.pixelFormat);
  435. card->SetTsiFrameEnable(rp.flags & kEnable4KTSI, channel);
  436. card->Set4kSquaresEnable(rp.flags & kEnable4KSquares, channel);
  437. card->SetQuadQuadSquaresEnable(rp.flags & kEnable8KSquares,
  438. channel);
  439. }
  440. return true;
  441. }
  442. void Routing::ConfigureOutputAudio(const OutputProps &props, CNTV2Card *card)
  443. {
  444. if (!card)
  445. return;
  446. auto deviceID = card->GetDeviceID();
  447. auto audioSys = props.AudioSystem();
  448. auto channel = props.Channel();
  449. card->SetNumberAudioChannels(props.audioNumChannels, audioSys);
  450. card->SetAudioRate(props.AudioRate(), audioSys);
  451. card->SetAudioBufferSize(NTV2_AUDIO_BUFFER_BIG, audioSys);
  452. card->SetAudioOutputDelay(audioSys, 0);
  453. card->SetSDIOutputAudioSystem(channel, audioSys);
  454. card->SetSDIOutputDS2AudioSystem(channel, audioSys);
  455. /* NOTE(paulh):
  456. * The SDK has a specifies an SDI audio system by Channel rather than by SDI output
  457. * and certain devices require setting the SDI audio system to NTV2_CHANNEL1.
  458. * i.e.
  459. * SDI 1 = NTV2_CHANNEL1
  460. * SDI 2 = NTV2_CHANNEL2
  461. * ...
  462. * SDI 5/Monitor = NTV2_CHANNEL5
  463. * etc...
  464. *
  465. * This fixes AJA internal bugs: 10730, 10986, 16274
  466. */
  467. if (deviceID == DEVICE_ID_IOXT || deviceID == DEVICE_ID_IO4KUFC ||
  468. deviceID == DEVICE_ID_IO4KPLUS || deviceID == DEVICE_ID_KONA1 ||
  469. deviceID == DEVICE_ID_KONA3G || deviceID == DEVICE_ID_KONA4UFC ||
  470. deviceID == DEVICE_ID_KONA5 || deviceID == DEVICE_ID_KONA5_2X4K) {
  471. // Make sure SDI out 1 (aka Channel 1) is set to the correct sub-system
  472. card->SetSDIOutputAudioSystem(NTV2_CHANNEL1, audioSys);
  473. card->SetSDIOutputDS2AudioSystem(NTV2_CHANNEL1, audioSys);
  474. }
  475. // make sure that audio is setup for the SDI monitor output on devices that support it
  476. if (NTV2DeviceCanDoWidget(deviceID, NTV2_WgtSDIMonOut1)) {
  477. card->SetSDIOutputAudioSystem(NTV2_CHANNEL5, audioSys);
  478. card->SetSDIOutputDS2AudioSystem(NTV2_CHANNEL5, audioSys);
  479. }
  480. card->SetHDMIOutAudioRate(props.AudioRate());
  481. card->SetHDMIOutAudioFormat(NTV2_AUDIO_FORMAT_LPCM);
  482. card->SetAudioOutputMonitorSource(NTV2_AudioChannel1_2, channel);
  483. card->SetAESOutputSource(NTV2_AudioChannel1_4, audioSys,
  484. NTV2_AudioChannel1_4);
  485. card->SetAESOutputSource(NTV2_AudioChannel5_8, audioSys,
  486. NTV2_AudioChannel5_8);
  487. card->SetAESOutputSource(NTV2_AudioChannel9_12, audioSys,
  488. NTV2_AudioChannel9_12);
  489. card->SetAESOutputSource(NTV2_AudioChannel13_16, audioSys,
  490. NTV2_AudioChannel13_16);
  491. // make sure that audio is setup for HDMI output on devices that support it
  492. if (NTV2DeviceGetNumHDMIVideoOutputs(deviceID) > 0) {
  493. if (NTV2DeviceCanDoAudioMixer(deviceID)) {
  494. card->SetAudioMixerInputAudioSystem(
  495. NTV2_AudioMixerInputMain, audioSys);
  496. card->SetAudioMixerInputChannelSelect(
  497. NTV2_AudioMixerInputMain, NTV2_AudioChannel1_2);
  498. card->SetAudioMixerInputChannelsMute(
  499. NTV2_AudioMixerInputAux1,
  500. NTV2AudioChannelsMuteAll);
  501. card->SetAudioMixerInputChannelsMute(
  502. NTV2_AudioMixerInputAux2,
  503. NTV2AudioChannelsMuteAll);
  504. }
  505. card->SetHDMIOutAudioChannels(NTV2_HDMIAudio8Channels);
  506. card->SetHDMIOutAudioSource2Channel(NTV2_AudioChannel1_2,
  507. audioSys);
  508. card->SetHDMIOutAudioSource8Channel(NTV2_AudioChannel1_8,
  509. audioSys);
  510. }
  511. card->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_OFF, audioSys);
  512. card->StopAudioOutput(audioSys);
  513. }
  514. void Routing::LogRoutingPreset(const RoutingPreset &rp)
  515. {
  516. auto hexStr = [&](uint8_t val) -> std::string {
  517. std::stringstream ss;
  518. ss << std::setfill('0') << std::setw(sizeof(uint8_t) * 2)
  519. << std::hex << (val | 0);
  520. return ss.str();
  521. };
  522. std::stringstream ss;
  523. ss << "\nPreset: " << rp.name;
  524. if (rp.kind == ConnectionKind::SDI) {
  525. ss << "\nVPID Standard: 0x"
  526. << hexStr(static_cast<uint8_t>(rp.vpid_standard));
  527. }
  528. ss << "\nMode: " << NTV2ModeToString(rp.mode)
  529. << "\nChannels: " << rp.num_channels
  530. << "\nFramestores: " << rp.num_framestores;
  531. blog(LOG_INFO, "[ AJA Crosspoint Routing Preset ]%s", ss.str().c_str());
  532. if (rp.device_ids.size() > 0) {
  533. ss.clear();
  534. for (auto id : rp.device_ids) {
  535. ss << " - " << NTV2DeviceIDToString(id) << "\n";
  536. }
  537. blog(LOG_INFO, "\nCompatible Device IDs: \n%s",
  538. ss.str().c_str());
  539. }
  540. }
  541. } // aja