aja-routing.cpp 19 KB

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