CECHandler.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. #include <cec/CECHandler.h>
  2. #include <utils/Logger.h>
  3. #include <algorithm>
  4. #include <libcec/cecloader.h>
  5. #include <QJsonArray>
  6. #include <QJsonDocument>
  7. #include <QJsonObject>
  8. #include <QDebug>
  9. #include <QFile>
  10. /* Enable to turn on detailed CEC logs */
  11. #define NO_VERBOSE_CEC
  12. CECHandler::CECHandler()
  13. {
  14. qRegisterMetaType<CECEvent>("CECEvent");
  15. _logger = Logger::getInstance("CEC");
  16. _cecCallbacks = getCallbacks();
  17. _cecConfig = getConfig();
  18. _cecConfig.callbacks = &_cecCallbacks;
  19. _cecConfig.callbackParam = this;
  20. }
  21. CECHandler::~CECHandler()
  22. {
  23. stop();
  24. }
  25. bool CECHandler::start()
  26. {
  27. if (_cecAdapter)
  28. return true;
  29. std::string library = std::string("" CEC_LIBRARY);
  30. _cecAdapter = LibCecInitialise(&_cecConfig, QFile::exists(QString::fromStdString(library)) ? library.c_str() : nullptr);
  31. if(!_cecAdapter)
  32. {
  33. Error(_logger, "Failed to loading libcec.so");
  34. return false;
  35. }
  36. Info(_logger, "CEC handler started");
  37. auto adapters = getAdapters();
  38. if (adapters.isEmpty())
  39. {
  40. Error(_logger, "Failed to find CEC adapter");
  41. UnloadLibCec(_cecAdapter);
  42. _cecAdapter = nullptr;
  43. return false;
  44. }
  45. Info(_logger, "Auto detecting CEC adapter");
  46. bool opened = false;
  47. for (const auto & adapter : adapters)
  48. {
  49. printAdapter(adapter);
  50. if (!opened && openAdapter(adapter))
  51. {
  52. Info(_logger, "CEC Handler initialized with adapter : %s", adapter.strComName);
  53. opened = true;
  54. }
  55. }
  56. if (!opened)
  57. {
  58. UnloadLibCec(_cecAdapter);
  59. _cecAdapter = nullptr;
  60. }
  61. return opened;
  62. }
  63. void CECHandler::stop()
  64. {
  65. if (_cecAdapter)
  66. {
  67. Info(_logger, "Stopping CEC handler");
  68. _cecAdapter->Close();
  69. UnloadLibCec(_cecAdapter);
  70. _cecAdapter = nullptr;
  71. }
  72. }
  73. CECConfig CECHandler::getConfig() const
  74. {
  75. CECConfig configuration;
  76. const std::string name("HyperionCEC");
  77. name.copy(configuration.strDeviceName, std::min(name.size(), sizeof(configuration.strDeviceName)));
  78. configuration.deviceTypes.Add(CEC::CEC_DEVICE_TYPE_RECORDING_DEVICE);
  79. configuration.clientVersion = CEC::LIBCEC_VERSION_CURRENT;
  80. return configuration;
  81. }
  82. CECCallbacks CECHandler::getCallbacks() const
  83. {
  84. CECCallbacks callbacks;
  85. callbacks.sourceActivated = onCecSourceActivated;
  86. callbacks.commandReceived = onCecCommandReceived;
  87. callbacks.alert = onCecAlert;
  88. callbacks.logMessage = onCecLogMessage;
  89. callbacks.keyPress = onCecKeyPress;
  90. callbacks.configurationChanged = onCecConfigurationChanged;
  91. callbacks.menuStateChanged = onCecMenuStateChanged;
  92. return callbacks;
  93. }
  94. QVector<CECAdapterDescriptor> CECHandler::getAdapters() const
  95. {
  96. if (!_cecAdapter)
  97. return {};
  98. QVector<CECAdapterDescriptor> descriptors(16);
  99. int8_t size = _cecAdapter->DetectAdapters(descriptors.data(), descriptors.size(), nullptr, true /*quickscan*/);
  100. descriptors.resize(size);
  101. return descriptors;
  102. }
  103. bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
  104. {
  105. if (!_cecAdapter)
  106. return false;
  107. if(!_cecAdapter->Open(descriptor.strComName))
  108. {
  109. Error(_logger, "%s", QSTRING_CSTR(QString("Failed to open the CEC adaper on port %1")
  110. .arg(descriptor.strComName))
  111. );
  112. return false;
  113. }
  114. return true;
  115. }
  116. void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const
  117. {
  118. Info(_logger, "%s", QSTRING_CSTR(QString("CEC Adapter:")));
  119. Info(_logger, "%s", QSTRING_CSTR(QString("\tName : %1").arg(descriptor.strComName)));
  120. Info(_logger, "%s", QSTRING_CSTR(QString("\tPath : %1").arg(descriptor.strComPath)));
  121. }
  122. QString CECHandler::scan() const
  123. {
  124. if (!_cecAdapter)
  125. return {};
  126. Info(_logger, "Starting CEC scan");
  127. QJsonArray devices;
  128. CECLogicalAddresses addresses = _cecAdapter->GetActiveDevices();
  129. for (int address = CEC::CECDEVICE_TV; address <= CEC::CECDEVICE_BROADCAST; ++address)
  130. {
  131. if (addresses[address])
  132. {
  133. CECLogicalAddress logicalAddress = (CECLogicalAddress)address;
  134. QJsonObject device;
  135. CECVendorId vendor = (CECVendorId)_cecAdapter->GetDeviceVendorId(logicalAddress);
  136. CECPowerStatus power = _cecAdapter->GetDevicePowerStatus(logicalAddress);
  137. device["name" ] = _cecAdapter->GetDeviceOSDName(logicalAddress).c_str();
  138. device["vendor" ] = _cecAdapter->ToString(vendor);
  139. device["address" ] = _cecAdapter->ToString(logicalAddress);
  140. device["power" ] = _cecAdapter->ToString(power);
  141. devices << device;
  142. Info(_logger, "%s", QSTRING_CSTR(QString("\tCECDevice: %1 / %2 / %3 / %4")
  143. .arg(device["name"].toString(),
  144. device["vendor"].toString(),
  145. device["address"].toString(),
  146. device["power"].toString()))
  147. );
  148. }
  149. }
  150. return QJsonDocument(devices).toJson(QJsonDocument::Compact);
  151. }
  152. void CECHandler::onCecLogMessage(void * context, const CECLogMessage * message)
  153. {
  154. #ifdef VERBOSE_CEC
  155. CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
  156. if (!handler)
  157. return;
  158. switch (message->level)
  159. {
  160. case CEC::CEC_LOG_ERROR:
  161. Error(handler->_logger, QString("%1")
  162. .arg(message->message)
  163. .toLocal8Bit());
  164. break;
  165. case CEC::CEC_LOG_WARNING:
  166. Warning(handler->_logger, QString("%1")
  167. .arg(message->message)
  168. .toLocal8Bit());
  169. break;
  170. case CEC::CEC_LOG_TRAFFIC:
  171. case CEC::CEC_LOG_NOTICE:
  172. Info(handler->_logger, QString("%1")
  173. .arg(message->message)
  174. .toLocal8Bit());
  175. break;
  176. case CEC::CEC_LOG_DEBUG:
  177. Debug(handler->_logger, QString("%1")
  178. .arg(message->message)
  179. .toLocal8Bit());
  180. break;
  181. default:
  182. break;
  183. }
  184. #endif
  185. }
  186. void CECHandler::onCecKeyPress(void * context, const CECKeyPress * key)
  187. {
  188. #ifdef VERBOSE_CEC
  189. CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
  190. if (!handler)
  191. return;
  192. CECAdapter * adapter = handler->_cecAdapter;
  193. Debug(handler->_logger, QString("CECHandler::onCecKeyPress: %1")
  194. .arg(adapter->ToString(key->keycode))
  195. .toLocal8Bit());
  196. #endif
  197. }
  198. void CECHandler::onCecAlert(void * context, const CECAlert alert, const CECParameter data)
  199. {
  200. #ifdef VERBOSE_CEC
  201. CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
  202. if (!handler)
  203. return;
  204. Error(handler->_logger, QString("CECHandler::onCecAlert: %1")
  205. .arg(alert)
  206. .toLocal8Bit());
  207. #endif
  208. }
  209. void CECHandler::onCecConfigurationChanged(void * context, const CECConfig * configuration)
  210. {
  211. #ifdef VERBOSE_CEC
  212. CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
  213. if (!handler)
  214. return;
  215. Debug(handler->_logger, QString("CECHandler::onCecConfigurationChanged: %1")
  216. .arg(configuration->strDeviceName)
  217. .toLocal8Bit());
  218. #endif
  219. }
  220. int CECHandler::onCecMenuStateChanged(void * context, const CECMenuState state)
  221. {
  222. #ifdef VERBOSE_CEC
  223. CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
  224. if (!handler)
  225. return 0;
  226. CECAdapter * adapter = handler->_cecAdapter;
  227. Debug(handler->_logger, QString("CECHandler::onCecMenuStateChanged: %1")
  228. .arg(adapter->ToString(state))
  229. .toLocal8Bit());
  230. #endif
  231. return 0;
  232. }
  233. void CECHandler::onCecCommandReceived(void * context, const CECCommand * command)
  234. {
  235. CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
  236. if (!handler)
  237. return;
  238. CECAdapter * adapter = handler->_cecAdapter;
  239. #ifdef VERBOSE_CEC
  240. Debug(handler->_logger, QString("CECHandler::onCecCommandReceived: %1 (%2 > %3)")
  241. .arg(adapter->ToString(command->opcode))
  242. .arg(adapter->ToString(command->initiator))
  243. .arg(adapter->ToString(command->destination))
  244. .toLocal8Bit());
  245. #endif
  246. /* We do NOT check sender */
  247. // if (address == CEC::CECDEVICE_TV)
  248. {
  249. if (command->opcode == CEC::CEC_OPCODE_SET_STREAM_PATH)
  250. {
  251. Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source activated: %1")
  252. .arg(adapter->ToString(command->initiator)))
  253. );
  254. emit handler->cecEvent(CECEvent::On);
  255. }
  256. if (command->opcode == CEC::CEC_OPCODE_STANDBY)
  257. {
  258. Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source deactivated: %1")
  259. .arg(adapter->ToString(command->initiator)))
  260. );
  261. emit handler->cecEvent(CECEvent::Off);
  262. }
  263. }
  264. }
  265. void CECHandler::onCecSourceActivated(void * context, const CECLogicalAddress address, const uint8_t activated)
  266. {
  267. /* We use CECHandler::onCecCommandReceived for
  268. * source activated/deactivated notifications. */
  269. #ifdef VERBOSE_CEC
  270. CECHandler * handler = qobject_cast<CECHandler*>(static_cast<QObject*>(context));
  271. if (!handler)
  272. return;
  273. CECAdapter * adapter = handler->_cecAdapter;
  274. Debug(handler->_logger, QString("CEC source %1 : %2")
  275. .arg(activated ? "activated" : "deactivated")
  276. .arg(adapter->ToString(address))
  277. .toLocal8Bit());
  278. #endif
  279. }