aja-card-manager.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. #include "aja-card-manager.hpp"
  2. #include "aja-common.hpp"
  3. #include "aja-output.hpp"
  4. #include "aja-source.hpp"
  5. #include "obs-properties.h"
  6. #include <util/base.h>
  7. #include <ajantv2/includes/ntv2card.h>
  8. #include <ajantv2/includes/ntv2devicescanner.h>
  9. #include <ajantv2/includes/ntv2devicefeatures.h>
  10. #include <ajantv2/includes/ntv2utils.h>
  11. #include <ajabase/system/process.h>
  12. #include <ajabase/system/systemtime.h>
  13. static const uint32_t kStreamingAppID = NTV2_FOURCC('O', 'B', 'S', ' ');
  14. namespace aja {
  15. CardManager &CardManager::Instance()
  16. {
  17. static CardManager instance;
  18. return instance;
  19. }
  20. CardEntry::CardEntry(uint32_t cardIndex, const std::string &cardID)
  21. : mCardIndex{cardIndex},
  22. mCardID{cardID},
  23. mCard{std::make_unique<CNTV2Card>((UWord)cardIndex)},
  24. mChannelPwnz{},
  25. mMutex{}
  26. {
  27. }
  28. CardEntry::~CardEntry()
  29. {
  30. if (mCard) {
  31. mCard->Close();
  32. mCard.reset();
  33. }
  34. }
  35. CNTV2Card *CardEntry::GetCard()
  36. {
  37. return mCard.get();
  38. }
  39. bool CardEntry::Initialize()
  40. {
  41. if (!mCard) {
  42. blog(LOG_ERROR, "Invalid card instance %s!", mCardID.c_str());
  43. return false;
  44. }
  45. const NTV2DeviceID deviceID = mCard->GetDeviceID();
  46. // Briefly enter Standard Tasks mode to reset card via AJA Daemon/Agent.
  47. auto taskMode = NTV2_STANDARD_TASKS;
  48. mCard->GetEveryFrameServices(taskMode);
  49. if (taskMode != NTV2_STANDARD_TASKS) {
  50. mCard->SetEveryFrameServices(NTV2_STANDARD_TASKS);
  51. AJATime::Sleep(100);
  52. }
  53. mCard->SetEveryFrameServices(NTV2_OEM_TASKS);
  54. const int32_t obsPid = (int32_t)AJAProcess::GetPid();
  55. mCard->AcquireStreamForApplicationWithReference((ULWord)kStreamingAppID,
  56. obsPid);
  57. mCard->SetSuspendHostAudio(true);
  58. mCard->ClearRouting();
  59. if (NTV2DeviceCanDoMultiFormat(deviceID)) {
  60. mCard->SetMultiFormatMode(true);
  61. }
  62. mCard->SetReference(NTV2_REFERENCE_FREERUN);
  63. for (UWord i = 0; i < aja::CardNumAudioSystems(deviceID); i++) {
  64. mCard->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_OFF,
  65. static_cast<NTV2AudioSystem>(i));
  66. }
  67. auto numFramestores = aja::CardNumFramestores(deviceID);
  68. for (UWord i = 0; i < NTV2DeviceGetNumVideoInputs(deviceID); i++) {
  69. mCard->SetInputFrame(static_cast<NTV2Channel>(i), 0xff);
  70. // Disable 3G Level B converter by default
  71. if (NTV2DeviceCanDo3GLevelConversion(deviceID)) {
  72. mCard->SetSDIInLevelBtoLevelAConversion(
  73. static_cast<NTV2Channel>(i), false);
  74. }
  75. }
  76. // SDI Outputs Default State
  77. for (UWord i = 0; i < NTV2DeviceGetNumVideoOutputs(deviceID); i++) {
  78. auto channel = GetNTV2ChannelForIndex(i);
  79. if (NTV2DeviceCanDo3GOut(deviceID, i)) {
  80. mCard->SetSDIOut3GEnable(channel, true);
  81. mCard->SetSDIOut3GbEnable(channel, false);
  82. }
  83. if (NTV2DeviceCanDo12GOut(deviceID, i)) {
  84. mCard->SetSDIOut6GEnable(channel, false);
  85. mCard->SetSDIOut12GEnable(channel, false);
  86. }
  87. if (NTV2DeviceCanDo3GLevelConversion(deviceID)) {
  88. mCard->SetSDIOutLevelAtoLevelBConversion(i, false);
  89. mCard->SetSDIOutRGBLevelAConversion(i, false);
  90. }
  91. }
  92. for (UWord i = 0; i < numFramestores; i++) {
  93. auto channel = GetNTV2ChannelForIndex(i);
  94. if (isAutoCirculateRunning(channel)) {
  95. mCard->AutoCirculateStop(channel, true);
  96. }
  97. mCard->SetVideoFormat(NTV2_FORMAT_1080p_5994_A, false, false,
  98. channel);
  99. mCard->SetFrameBufferFormat(channel, NTV2_FBF_8BIT_YCBCR);
  100. mCard->DisableChannel(channel);
  101. }
  102. blog(LOG_DEBUG, "NTV2 Card Initialized: %s", mCardID.c_str());
  103. return true;
  104. }
  105. uint32_t CardEntry::GetCardIndex() const
  106. {
  107. return mCardIndex;
  108. }
  109. std::string CardEntry::GetCardID() const
  110. {
  111. return mCardID;
  112. }
  113. std::string CardEntry::GetDisplayName() const
  114. {
  115. if (mCard) {
  116. std::ostringstream oss;
  117. oss << mCard->GetIndexNumber() << " - "
  118. << mCard->GetModelName();
  119. const std::string &serial = GetSerial();
  120. if (!serial.empty())
  121. oss << " (" << serial << ")";
  122. return oss.str();
  123. }
  124. // very bad if we get here...
  125. return "Unknown";
  126. }
  127. std::string CardEntry::GetSerial() const
  128. {
  129. std::string serial;
  130. if (mCard)
  131. mCard->GetSerialNumberString(serial);
  132. return serial;
  133. }
  134. NTV2DeviceID CardEntry::GetDeviceID() const
  135. {
  136. NTV2DeviceID id = DEVICE_ID_NOTFOUND;
  137. if (mCard)
  138. id = mCard->GetDeviceID();
  139. return id;
  140. }
  141. bool CardEntry::ChannelReady(NTV2Channel chan, const std::string &owner) const
  142. {
  143. const std::lock_guard<std::mutex> lock(mMutex);
  144. for (const auto &pwn : mChannelPwnz) {
  145. if (pwn.second & (1 << static_cast<int32_t>(chan))) {
  146. return pwn.first == owner;
  147. }
  148. }
  149. return true;
  150. }
  151. bool CardEntry::AcquireChannel(NTV2Channel chan, NTV2Mode mode,
  152. const std::string &owner)
  153. {
  154. bool acquired = false;
  155. if (ChannelReady(chan, owner)) {
  156. const std::lock_guard<std::mutex> lock(mMutex);
  157. if (mChannelPwnz.find(owner) != mChannelPwnz.end()) {
  158. if (mChannelPwnz[owner] &
  159. (1 << static_cast<int32_t>(chan))) {
  160. acquired = true;
  161. } else {
  162. mChannelPwnz[owner] |=
  163. (1 << static_cast<int32_t>(chan));
  164. acquired = true;
  165. }
  166. } else {
  167. mChannelPwnz[owner] |=
  168. (1 << static_cast<int32_t>(chan));
  169. acquired = true;
  170. }
  171. // Acquire interrupt handles
  172. if (acquired && mCard) {
  173. if (mode == NTV2_MODE_CAPTURE) {
  174. mCard->EnableInputInterrupt(chan);
  175. mCard->SubscribeInputVerticalEvent(chan);
  176. } else if (mode == NTV2_MODE_DISPLAY) {
  177. mCard->EnableOutputInterrupt(chan);
  178. mCard->SubscribeOutputVerticalEvent(chan);
  179. }
  180. }
  181. }
  182. return acquired;
  183. }
  184. bool CardEntry::ReleaseChannel(NTV2Channel chan, NTV2Mode mode,
  185. const std::string &owner)
  186. {
  187. const std::lock_guard<std::mutex> lock(mMutex);
  188. for (const auto &pwn : mChannelPwnz) {
  189. if (pwn.first == owner) {
  190. if (mChannelPwnz[owner] &
  191. (1 << static_cast<int32_t>(chan))) {
  192. mChannelPwnz[owner] ^=
  193. (1 << static_cast<int32_t>(chan));
  194. // Release interrupt handles
  195. if (mCard) {
  196. if (mode == NTV2_MODE_CAPTURE) {
  197. mCard->DisableInputInterrupt(
  198. chan);
  199. mCard->UnsubscribeInputVerticalEvent(
  200. chan);
  201. } else if (mode == NTV2_MODE_DISPLAY) {
  202. mCard->DisableOutputInterrupt(
  203. chan);
  204. mCard->UnsubscribeOutputVerticalEvent(
  205. chan);
  206. }
  207. }
  208. return true;
  209. }
  210. }
  211. }
  212. return false;
  213. }
  214. bool CardEntry::InputSelectionReady(IOSelection io, NTV2DeviceID id,
  215. const std::string &owner) const
  216. {
  217. if (id == DEVICE_ID_KONA1 && io == IOSelection::SDI1) {
  218. return true;
  219. } else {
  220. NTV2InputSourceSet inputSources;
  221. aja::IOSelectionToInputSources(io, inputSources);
  222. if (inputSources.size() > 0) {
  223. size_t channelsReady = 0;
  224. for (auto &&src : inputSources) {
  225. auto channel = NTV2InputSourceToChannel(src);
  226. if (ChannelReady(channel, owner))
  227. channelsReady++;
  228. }
  229. if (channelsReady == inputSources.size())
  230. return true;
  231. }
  232. }
  233. return false;
  234. }
  235. bool CardEntry::OutputSelectionReady(IOSelection io, NTV2DeviceID id,
  236. const std::string &owner) const
  237. {
  238. /* Handle checking special case outputs before all other outputs.
  239. * 1. HDMI Monitor uses framestore 4
  240. * 2. SDI Monitor on Io 4K/Io 4K Plus, etc. uses framestore 4.
  241. * 3. Everything else...
  242. */
  243. if (aja::CardCanDoHDMIMonitorOutput(id) &&
  244. io == IOSelection::HDMIMonitorOut) {
  245. NTV2Channel hdmiMonChannel = NTV2_CHANNEL4;
  246. return ChannelReady(hdmiMonChannel, owner);
  247. } else if (aja::CardCanDoSDIMonitorOutput(id) &&
  248. io == IOSelection::SDI5) {
  249. NTV2Channel sdiMonChannel = NTV2_CHANNEL4;
  250. return ChannelReady(sdiMonChannel, owner);
  251. } else if (id == DEVICE_ID_KONA1 && io == IOSelection::SDI1) {
  252. return true;
  253. } else {
  254. NTV2OutputDestinations outputDests;
  255. aja::IOSelectionToOutputDests(io, outputDests);
  256. if (outputDests.size() > 0) {
  257. size_t channelsReady = 0;
  258. for (auto &&dst : outputDests) {
  259. auto channel =
  260. NTV2OutputDestinationToChannel(dst);
  261. if (ChannelReady(channel, owner))
  262. channelsReady++;
  263. }
  264. if (channelsReady == outputDests.size())
  265. return true;
  266. }
  267. }
  268. return false;
  269. }
  270. bool CardEntry::AcquireInputSelection(IOSelection io, NTV2DeviceID id,
  271. const std::string &owner)
  272. {
  273. UNUSED_PARAMETER(id);
  274. NTV2InputSourceSet inputSources;
  275. aja::IOSelectionToInputSources(io, inputSources);
  276. std::vector<NTV2Channel> acquiredChannels;
  277. for (auto &&src : inputSources) {
  278. auto acqChan = NTV2InputSourceToChannel(src);
  279. if (AcquireChannel(acqChan, NTV2_MODE_CAPTURE, owner)) {
  280. blog(LOG_DEBUG, "Source %s acquired channel %s",
  281. owner.c_str(),
  282. NTV2ChannelToString(acqChan).c_str());
  283. acquiredChannels.push_back(acqChan);
  284. } else {
  285. blog(LOG_DEBUG,
  286. "Source %s could not acquire channel %s",
  287. owner.c_str(),
  288. NTV2ChannelToString(acqChan).c_str());
  289. }
  290. }
  291. // Release channels if we couldn't acquire all required channels.
  292. if (acquiredChannels.size() != inputSources.size()) {
  293. for (auto &&ac : acquiredChannels) {
  294. ReleaseChannel(ac, NTV2_MODE_CAPTURE, owner);
  295. }
  296. }
  297. return acquiredChannels.size() == inputSources.size();
  298. }
  299. bool CardEntry::ReleaseInputSelection(IOSelection io, NTV2DeviceID id,
  300. const std::string &owner)
  301. {
  302. UNUSED_PARAMETER(id);
  303. NTV2InputSourceSet currentInputSources;
  304. aja::IOSelectionToInputSources(io, currentInputSources);
  305. uint32_t releasedCount = 0;
  306. for (auto &&src : currentInputSources) {
  307. auto relChan = NTV2InputSourceToChannel(src);
  308. if (ReleaseChannel(relChan, NTV2_MODE_CAPTURE, owner)) {
  309. blog(LOG_DEBUG, "Released Channel %s",
  310. NTV2ChannelToString(relChan).c_str());
  311. releasedCount++;
  312. }
  313. }
  314. return releasedCount == currentInputSources.size();
  315. }
  316. bool CardEntry::AcquireOutputSelection(IOSelection io, NTV2DeviceID id,
  317. const std::string &owner)
  318. {
  319. std::vector<NTV2Channel> acquiredChannels;
  320. NTV2OutputDestinations outputDests;
  321. aja::IOSelectionToOutputDests(io, outputDests);
  322. // Handle acquiring special case outputs --
  323. // HDMI Monitor uses framestore 4
  324. if (aja::CardCanDoHDMIMonitorOutput(id) &&
  325. io == IOSelection::HDMIMonitorOut) {
  326. NTV2Channel hdmiMonChannel = NTV2_CHANNEL4;
  327. if (AcquireChannel(hdmiMonChannel, NTV2_MODE_DISPLAY, owner)) {
  328. blog(LOG_DEBUG, "Output %s acquired channel %s",
  329. owner.c_str(),
  330. NTV2ChannelToString(hdmiMonChannel).c_str());
  331. acquiredChannels.push_back(hdmiMonChannel);
  332. } else {
  333. blog(LOG_DEBUG,
  334. "Output %s could not acquire channel %s",
  335. owner.c_str(),
  336. NTV2ChannelToString(hdmiMonChannel).c_str());
  337. }
  338. } else if (aja::CardCanDoSDIMonitorOutput(id) &&
  339. io == IOSelection::SDI5) {
  340. // SDI Monitor on io4K/io4K+/etc. uses framestore 4
  341. NTV2Channel sdiMonChannel = NTV2_CHANNEL4;
  342. if (AcquireChannel(sdiMonChannel, NTV2_MODE_DISPLAY, owner)) {
  343. blog(LOG_DEBUG, "Output %s acquired channel %s",
  344. owner.c_str(),
  345. NTV2ChannelToString(sdiMonChannel).c_str());
  346. acquiredChannels.push_back(sdiMonChannel);
  347. } else {
  348. blog(LOG_DEBUG,
  349. "Output %s could not acquire channel %s",
  350. owner.c_str(),
  351. NTV2ChannelToString(sdiMonChannel).c_str());
  352. }
  353. } else {
  354. // Handle acquiring all other channels
  355. for (auto &&dst : outputDests) {
  356. auto acqChan = NTV2OutputDestinationToChannel(dst);
  357. if (AcquireChannel(acqChan, NTV2_MODE_DISPLAY, owner)) {
  358. acquiredChannels.push_back(acqChan);
  359. blog(LOG_DEBUG, "Output %s acquired channel %s",
  360. owner.c_str(),
  361. NTV2ChannelToString(acqChan).c_str());
  362. } else {
  363. blog(LOG_DEBUG,
  364. "Output %s could not acquire channel %s",
  365. owner.c_str(),
  366. NTV2ChannelToString(acqChan).c_str());
  367. }
  368. }
  369. // Release channels if we couldn't acquire all required channels.
  370. if (acquiredChannels.size() != outputDests.size()) {
  371. for (auto &&ac : acquiredChannels) {
  372. ReleaseChannel(ac, NTV2_MODE_DISPLAY, owner);
  373. }
  374. }
  375. }
  376. return acquiredChannels.size() == outputDests.size();
  377. }
  378. bool CardEntry::ReleaseOutputSelection(IOSelection io, NTV2DeviceID id,
  379. const std::string &owner)
  380. {
  381. NTV2OutputDestinations currentOutputDests;
  382. aja::IOSelectionToOutputDests(io, currentOutputDests);
  383. uint32_t releasedCount = 0;
  384. // Handle releasing special case outputs --
  385. // HDMI Monitor uses framestore 4
  386. if (aja::CardCanDoHDMIMonitorOutput(id) &&
  387. io == IOSelection::HDMIMonitorOut) {
  388. NTV2Channel hdmiMonChannel = NTV2_CHANNEL4;
  389. if (ReleaseChannel(hdmiMonChannel, NTV2_MODE_DISPLAY, owner)) {
  390. blog(LOG_DEBUG, "Released Channel %s",
  391. NTV2ChannelToString(hdmiMonChannel).c_str());
  392. releasedCount++;
  393. }
  394. } else if (aja::CardCanDoSDIMonitorOutput(id) &&
  395. io == IOSelection::SDI5) {
  396. // SDI Monitor on io4K/io4K+/etc. uses framestore 4
  397. NTV2Channel sdiMonChannel = NTV2_CHANNEL4;
  398. if (ReleaseChannel(sdiMonChannel, NTV2_MODE_DISPLAY, owner)) {
  399. blog(LOG_DEBUG, "Released Channel %s",
  400. NTV2ChannelToString(sdiMonChannel).c_str());
  401. releasedCount++;
  402. }
  403. } else {
  404. // Release all other channels
  405. for (auto &&dst : currentOutputDests) {
  406. auto relChan = NTV2OutputDestinationToChannel(dst);
  407. if (ReleaseChannel(relChan, NTV2_MODE_DISPLAY, owner)) {
  408. blog(LOG_DEBUG, "Released Channel %s",
  409. NTV2ChannelToString(relChan).c_str());
  410. releasedCount++;
  411. }
  412. }
  413. }
  414. return releasedCount == currentOutputDests.size();
  415. }
  416. bool CardEntry::UpdateChannelOwnerName(const std::string &oldName,
  417. const std::string &newName)
  418. {
  419. const std::lock_guard<std::mutex> lock(mMutex);
  420. for (const auto &pwn : mChannelPwnz) {
  421. if (pwn.first == oldName) {
  422. mChannelPwnz.insert(std::pair<std::string, int32_t>{
  423. newName, pwn.second});
  424. mChannelPwnz.erase(oldName);
  425. return true;
  426. }
  427. }
  428. return false;
  429. }
  430. bool CardEntry::isAutoCirculateRunning(NTV2Channel chan)
  431. {
  432. if (!mCard)
  433. return false;
  434. AUTOCIRCULATE_STATUS acStatus;
  435. if (mCard->AutoCirculateGetStatus(chan, acStatus)) {
  436. if (acStatus.acState != NTV2_AUTOCIRCULATE_RUNNING &&
  437. acStatus.acState != NTV2_AUTOCIRCULATE_STARTING &&
  438. acStatus.acState != NTV2_AUTOCIRCULATE_PAUSED &&
  439. acStatus.acState != NTV2_AUTOCIRCULATE_INIT) {
  440. return false;
  441. }
  442. }
  443. return true;
  444. }
  445. void CardManager::ClearCardEntries()
  446. {
  447. const std::lock_guard<std::mutex> lock(mMutex);
  448. for (auto &&entry : mCardEntries) {
  449. CNTV2Card *card = entry.second->GetCard();
  450. if (card) {
  451. card->SetSuspendHostAudio(false);
  452. card->SetEveryFrameServices(NTV2_STANDARD_TASKS);
  453. // Workaround for AJA internal bug #11378
  454. // Set HDMI output back to Audio System 1 on card release
  455. if (NTV2DeviceGetNumHDMIVideoOutputs(
  456. card->GetDeviceID()) > 0) {
  457. card->SetHDMIOutAudioSource8Channel(
  458. NTV2_AudioChannel1_8,
  459. NTV2_AUDIOSYSTEM_1);
  460. }
  461. int32_t pid = (int32_t)AJAProcess::GetPid();
  462. card->ReleaseStreamForApplicationWithReference(
  463. (ULWord)kStreamingAppID, pid);
  464. }
  465. }
  466. mCardEntries.clear();
  467. }
  468. void CardManager::EnumerateCards()
  469. {
  470. const std::lock_guard<std::mutex> lock(mMutex);
  471. CNTV2DeviceScanner scanner;
  472. for (const auto &iter : scanner.GetDeviceInfoList()) {
  473. CNTV2Card card((UWord)iter.deviceIndex);
  474. const std::string &cardID = aja::MakeCardID(card);
  475. // New Card Entry
  476. if (mCardEntries.find(cardID) == mCardEntries.end()) {
  477. CardEntryPtr cardEntry = std::make_shared<CardEntry>(
  478. iter.deviceIndex, cardID);
  479. if (cardEntry && cardEntry->Initialize())
  480. mCardEntries.emplace(cardID, cardEntry);
  481. } else {
  482. // Card fell off of the bus and came back with a new physical index?
  483. auto currEntry = mCardEntries[cardID];
  484. if (currEntry) {
  485. if (currEntry->GetCardIndex() !=
  486. iter.deviceIndex) {
  487. mCardEntries.erase(cardID);
  488. CardEntryPtr cardEntry =
  489. std::make_shared<CardEntry>(
  490. iter.deviceIndex,
  491. cardID);
  492. if (cardEntry &&
  493. cardEntry->Initialize())
  494. mCardEntries.emplace(cardID,
  495. cardEntry);
  496. }
  497. }
  498. }
  499. }
  500. }
  501. size_t CardManager::NumCardEntries() const
  502. {
  503. const std::lock_guard<std::mutex> lock(mMutex);
  504. return mCardEntries.size();
  505. }
  506. CNTV2Card *CardManager::GetCard(const std::string &cardID)
  507. {
  508. auto entry = GetCardEntry(cardID);
  509. if (entry)
  510. return entry->GetCard();
  511. return nullptr;
  512. }
  513. const CardEntryPtr CardManager::GetCardEntry(const std::string &cardID) const
  514. {
  515. const std::lock_guard<std::mutex> lock(mMutex);
  516. for (const auto &entry : mCardEntries) {
  517. if (entry.second && entry.second->GetCardID() == cardID)
  518. return entry.second;
  519. }
  520. return nullptr;
  521. }
  522. const CardEntries &CardManager::GetCardEntries() const
  523. {
  524. const std::lock_guard<std::mutex> lock(mMutex);
  525. return mCardEntries;
  526. }
  527. const CardEntries::iterator CardManager::begin()
  528. {
  529. const std::lock_guard<std::mutex> lock(mMutex);
  530. return mCardEntries.begin();
  531. }
  532. const CardEntries::iterator CardManager::end()
  533. {
  534. const std::lock_guard<std::mutex> lock(mMutex);
  535. return mCardEntries.end();
  536. }
  537. } // namespace aja