diff --git a/Manager/src/cmdparser.cpp b/Manager/src/cmdparser.cpp index 607a482..178915d 100644 --- a/Manager/src/cmdparser.cpp +++ b/Manager/src/cmdparser.cpp @@ -18,12 +18,20 @@ */ #include +#include #include #include +#include #include #include #include #include +#include + +#ifdef _WIN32 +#include +#include +#endif #include "cmdparser.h" #include "VCamUtils/src/ipcbridge.h" @@ -99,6 +107,8 @@ namespace AkVCam { const StringVector &args); int showSupportedFormats(const StringMap &flags, const StringVector &args); + int showDefaultFormat(const StringMap &flags, + const StringVector &args); int showFormats(const StringMap &flags, const StringVector &args); int addFormat(const StringMap &flags, const StringVector &args); int removeFormat(const StringMap &flags, const StringVector &args); @@ -106,6 +116,7 @@ namespace AkVCam { int update(const StringMap &flags, const StringVector &args); int loadSettings(const StringMap &flags, const StringVector &args); int stream(const StringMap &flags, const StringVector &args); + int listenEvents(const StringMap &flags, const StringVector &args); int showControls(const StringMap &flags, const StringVector &args); int readControl(const StringMap &flags, const StringVector &args); int writeControls(const StringMap &flags, const StringVector &args); @@ -114,6 +125,7 @@ namespace AkVCam { int logLevel(const StringMap &flags, const StringVector &args); int setLogLevel(const StringMap &flags, const StringVector &args); int showClients(const StringMap &flags, const StringVector &args); + int dumpInfo(const StringMap &flags, const StringVector &args); void loadGenerals(Settings &settings); VideoFormatMatrix readFormats(Settings &settings); std::vector readFormat(Settings &settings); @@ -131,6 +143,7 @@ namespace AkVCam { }; std::string operator *(const std::string &str, size_t n); + std::string operator *(size_t n, const std::string &str); } AkVCam::CmdParser::CmdParser() @@ -141,6 +154,9 @@ AkVCam::CmdParser::CmdParser() this->addFlags("", {"-h", "--help"}, "Show help."); + this->addFlags("", + {"-v", "--version"}, + "Show program version."); this->addFlags("", {"-p", "--parseable"}, "Show parseable output."); @@ -178,6 +194,16 @@ AkVCam::CmdParser::CmdParser() this->addFlags("supported-formats", {"-o", "--output"}, "Show supported output formats."); + this->addCommand("default-format", + "", + "Default device format.", + AKVCAM_BIND_FUNC(CmdParserPrivate::showDefaultFormat)); + this->addFlags("default-format", + {"-i", "--input"}, + "Default input format."); + this->addFlags("default-format", + {"-o", "--output"}, + "Default output format."); this->addCommand("formats", "DEVICE", "Show device formats.", @@ -210,6 +236,10 @@ AkVCam::CmdParser::CmdParser() "DEVICE FORMAT WIDTH HEIGHT", "Read frames from stdin and send them to the device.", AKVCAM_BIND_FUNC(CmdParserPrivate::stream)); + this->addCommand("listen-events", + "", + "Keep the manager running and listening to global events.", + AKVCAM_BIND_FUNC(CmdParserPrivate::listenEvents)); this->addCommand("controls", "DEVICE", "Show device controls.", @@ -263,6 +293,10 @@ AkVCam::CmdParser::CmdParser() "", "Show clients using the camera.", AKVCAM_BIND_FUNC(CmdParserPrivate::showClients)); + this->addCommand("dump", + "", + "Show all information in a parseable XML format.", + AKVCAM_BIND_FUNC(CmdParserPrivate::dumpInfo)); } AkVCam::CmdParser::~CmdParser() @@ -279,8 +313,10 @@ int AkVCam::CmdParser::parse(int argc, char **argv) for (int i = 1; i < argc; i++) { std::string arg = argv[i]; + char *p = nullptr; + strtod(arg.c_str(), &p); - if (arg[0] == '-') { + if (arg[0] == '-' && strlen(p) != 0) { auto flag = this->d->parserFlag(command->flags, arg); if (!flag) { @@ -338,6 +374,9 @@ int AkVCam::CmdParser::parse(int argc, char **argv) } } + if (this->d->m_ipcBridge.needsRoot(command->command)) + return this->d->m_ipcBridge.sudo(argc, argv); + return command->func(flags, arguments); } @@ -638,6 +677,12 @@ int AkVCam::CmdParserPrivate::defaultHandler(const StringMap &flags, if (flags.empty() || this->containsFlag(flags, "", "-h")) return this->showHelp(flags, args); + if (this->containsFlag(flags, "", "-v")) { + std::cout << COMMONS_VERSION << std::endl; + + return 0; + } + if (this->containsFlag(flags, "", "-p")) this->m_parseable = true; @@ -864,6 +909,21 @@ int AkVCam::CmdParserPrivate::showSupportedFormats(const StringMap &flags, return 0; } +int AkVCam::CmdParserPrivate::showDefaultFormat(const AkVCam::StringMap &flags, + const AkVCam::StringVector &args) +{ + UNUSED(args); + + auto type = + this->containsFlag(flags, "default-format", "-i")? + IpcBridge::StreamTypeInput: + IpcBridge::StreamTypeOutput; + auto format = this->m_ipcBridge.defaultPixelFormat(type); + std::cout << VideoFormat::stringFromFourcc(format) << std::endl; + + return 0; +} + int AkVCam::CmdParserPrivate::showFormats(const StringMap &flags, const StringVector &args) { @@ -948,7 +1008,8 @@ int AkVCam::CmdParserPrivate::addFormat(const StringMap &flags, return -1; } - auto formats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput); + auto formats = + this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput); auto fit = std::find(formats.begin(), formats.end(), format); if (fit == formats.end()) { @@ -1141,7 +1202,8 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags, return -1; } - auto formats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput); + auto formats = + this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput); auto fit = std::find(formats.begin(), formats.end(), format); if (fit == formats.end()) { @@ -1186,6 +1248,11 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags, AkVCam::VideoFrame frame(fmt); size_t bufferSize = 0; +#ifdef _WIN32 + // Set std::cin in binary mode. + _setmode(_fileno(stdin), _O_BINARY); +#endif + do { std::cin.read(reinterpret_cast(frame.data().data() + bufferSize), @@ -1203,6 +1270,42 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags, return 0; } +int AkVCam::CmdParserPrivate::listenEvents(const AkVCam::StringMap &flags, + const AkVCam::StringVector &args) +{ + UNUSED(flags); + UNUSED(args); + + auto serverStateChanged = [] (void *, IpcBridge::ServerState state) { + if (state == IpcBridge::ServerStateAvailable) + std::cout << "ServerAvailable" << std::endl; + else + std::cout << "ServerGone" << std::endl; + }; + auto devicesChanged = [] (void *, const std::vector &) { + std::cout << "DevicesUpdated" << std::endl; + }; + auto pictureChanged = [] (void *, const std::string &) { + std::cout << "PictureUpdated" << std::endl; + }; + + this->m_ipcBridge.connectServerStateChanged(this, serverStateChanged); + this->m_ipcBridge.connectDevicesChanged(this, devicesChanged); + this->m_ipcBridge.connectPictureChanged(this, pictureChanged); + + static bool exit = false; + auto signalHandler = [] (int) { + exit = true; + }; + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + + while (!exit) + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + + return 0; +} + int AkVCam::CmdParserPrivate::showControls(const StringMap &flags, const StringVector &args) { @@ -1311,11 +1414,14 @@ int AkVCam::CmdParserPrivate::readControl(const StringMap &flags, } if (this->containsFlag(flags, "get-control", "-l")) { - for (size_t i = 0; i< control.menu.size(); i++) + for (size_t i = 0; i < control.menu.size(); i++) if (this->m_parseable) std::cout << control.menu[i] << std::endl; else - std::cout << i << ": " << control.menu[i] << std::endl; + std::cout << i + << ": " + << control.menu[i] + << std::endl; } } @@ -1582,6 +1688,189 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags, return 0; } +int AkVCam::CmdParserPrivate::dumpInfo(const AkVCam::StringMap &flags, + const AkVCam::StringVector &args) +{ + UNUSED(flags); + UNUSED(args); + static const auto indent = 4 * std::string(" "); + + std::cout << "" << std::endl; + std::cout << "" << std::endl; + std::cout << indent << "" << std::endl; + + auto devices = this->m_ipcBridge.devices(); + + for (auto &device: devices) { + std::cout << 2 * indent << "" << std::endl; + std::cout << 3 * indent << "" << device << "" << std::endl; + std::cout << 3 * indent + << "" + << this->m_ipcBridge.description(device) + << "" + << std::endl; + std::cout << 3 * indent << "" << std::endl; + + for (auto &format: this->m_ipcBridge.formats(device)) { + std::cout << 4 * indent << "" << std::endl; + std::cout << 5 * indent + << "" + << VideoFormat::stringFromFourcc(format.fourcc()) + << "" + << std::endl; + std::cout << 5 * indent + << "" + << format.width() + << "" + << std::endl; + std::cout << 5 * indent + << "" + << format.height() + << "" + << std::endl; + std::cout << 5 * indent + << "" + << format.minimumFrameRate().toString() + << "" + << std::endl; + std::cout << 4 * indent << "" << std::endl; + } + + std::cout << 3 * indent << "" << std::endl; + std::cout << 3 * indent << "" << std::endl; + + for (auto &control: this->m_ipcBridge.controls(device)) { + std::cout << 4 * indent << "" << std::endl; + std::cout << 5 * indent + << "" + << control.id + << "" + << std::endl; + std::cout << 5 * indent + << "" + << control.description + << "" + << std::endl; + auto typeStr = typeStrMap(); + std::cout << 5 * indent + << "" + << typeStr[control.type] + << "" + << std::endl; + std::cout << 5 * indent + << "" + << control.minimum + << "" + << std::endl; + std::cout << 5 * indent + << "" + << control.maximum + << "" + << std::endl; + std::cout << 5 * indent + << "" + << control.step + << "" + << std::endl; + std::cout << 5 * indent + << "" + << control.defaultValue + << "" + << std::endl; + std::cout << 5 * indent + << "" + << control.value + << "" + << std::endl; + + if (!control.menu.empty() && control.type == ControlTypeMenu) { + std::cout << 5 * indent << "" << std::endl; + + for (auto &item: control.menu) + std::cout << 6 * indent + << "" + << item + << "" + << std::endl; + + std::cout << 5 * indent << "" << std::endl; + } + + std::cout << 4 * indent << "" << std::endl; + } + + std::cout << 3 * indent << "" << std::endl; + std::cout << 2 * indent << "" << std::endl; + } + + std::cout << indent << "" << std::endl; + std::cout << indent << "" << std::endl; + + for (auto &format: this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeInput)) + std::cout << 2 * indent + << "" + << VideoFormat::stringFromFourcc(format) + << "" + << std::endl; + + std::cout << indent << "" << std::endl; + + auto defInputFormat = + this->m_ipcBridge.defaultPixelFormat(IpcBridge::StreamTypeInput); + std::cout << indent + << "" + << VideoFormat::stringFromFourcc(defInputFormat) + << "" + << std::endl; + + std::cout << indent << "" << std::endl; + + for (auto &format: this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput)) + std::cout << 2 * indent + << "" + << VideoFormat::stringFromFourcc(format) + << "" + << std::endl; + + std::cout << indent << "" << std::endl; + + auto defOutputFormat = + this->m_ipcBridge.defaultPixelFormat(IpcBridge::StreamTypeOutput); + std::cout << indent + << "" + << VideoFormat::stringFromFourcc(defOutputFormat) + << "" + << std::endl; + + std::cout << indent << "" << std::endl; + + for (auto &pid: this->m_ipcBridge.clientsPids()) { + std::cout << 2 * indent << "" << std::endl; + std::cout << 3 * indent << "" << pid << "" << std::endl; + std::cout << 3 * indent + << "" + << this->m_ipcBridge.clientExe(pid) + << "" + << std::endl; + std::cout << 2 * indent << "" << std::endl; + } + + std::cout << indent << "" << std::endl; + std::cout << indent + << "" + << this->m_ipcBridge.picture() + << "" + << std::endl; + std::cout << indent + << "" + << this->m_ipcBridge.logLevel() + << "" + << std::endl; + std::cout << "" << std::endl; + + return 0; +} + void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings) { settings.beginGroup("General"); @@ -1654,7 +1943,7 @@ std::vector AkVCam::CmdParserPrivate::readFormat(Settings & VideoFormat format(pixFormat, width, height, - {frame_rate}); + {frame_rate}); if (format.isValid()) formats.push_back(format); @@ -1735,7 +2024,8 @@ void AkVCam::CmdParserPrivate::createDevice(Settings &settings, } auto deviceId = this->m_ipcBridge.addDevice(description); - auto supportedFormats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput); + auto supportedFormats = + this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput); for (auto &format: formats) { auto it = std::find(supportedFormats.begin(), @@ -1781,3 +2071,13 @@ std::string AkVCam::operator *(const std::string &str, size_t n) return ss.str(); } + +std::string AkVCam::operator *(size_t n, const std::string &str) +{ + std::stringstream ss; + + for (size_t i = 0; i < n; i++) + ss << str; + + return ss.str(); +} diff --git a/VCamUtils/src/fraction.cpp b/VCamUtils/src/fraction.cpp index e6df9c5..c13af4f 100644 --- a/VCamUtils/src/fraction.cpp +++ b/VCamUtils/src/fraction.cpp @@ -142,3 +142,10 @@ std::string AkVCam::Fraction::toString() const return ss.str(); } + +std::ostream &operator <<(std::ostream &os, const AkVCam::Fraction &fraction) +{ + os << fraction.toString(); + + return os; +} diff --git a/VCamUtils/src/fraction.h b/VCamUtils/src/fraction.h index 6b4beb9..55f88be 100644 --- a/VCamUtils/src/fraction.h +++ b/VCamUtils/src/fraction.h @@ -53,4 +53,6 @@ namespace AkVCam }; } +std::ostream &operator <<(std::ostream &os, const AkVCam::Fraction &fraction); + #endif // FRACTION_H diff --git a/VCamUtils/src/image/videoformat.cpp b/VCamUtils/src/image/videoformat.cpp index 3d1a907..d7ca89e 100644 --- a/VCamUtils/src/image/videoformat.cpp +++ b/VCamUtils/src/image/videoformat.cpp @@ -17,8 +17,9 @@ * Web-Site: http://webcamoid.github.io/ */ -#include #include +#include +#include #include "videoformat.h" #include "../utils.h" @@ -421,3 +422,38 @@ size_t AkVCam::VideoFormatGlobals::byplNV(size_t plane, size_t width) return align32(size_t(width)); } + +std::ostream &operator <<(std::ostream &os, const AkVCam::VideoFormat &format) +{ + auto formatStr = AkVCam::VideoFormat::stringFromFourcc(format.fourcc()); + + os << "VideoFormat(" + << formatStr + << ' ' + << format.width() + << 'x' + << format.height() + << ' ' + << format.minimumFrameRate() + << ')'; + + return os; +} + +std::ostream &operator <<(std::ostream &os, const AkVCam::VideoFormats &formats) +{ + bool writeComma = false; + os << "VideoFormats("; + + for (auto &format: formats) { + if (writeComma) + os << ", "; + + os << format; + writeComma = true; + } + + os << ')'; + + return os; +} diff --git a/VCamUtils/src/image/videoformat.h b/VCamUtils/src/image/videoformat.h index b98347e..f36946b 100644 --- a/VCamUtils/src/image/videoformat.h +++ b/VCamUtils/src/image/videoformat.h @@ -28,7 +28,9 @@ namespace AkVCam { + class VideoFormat; class VideoFormatPrivate; + using VideoFormats = std::vector; class VideoFormat { @@ -76,4 +78,7 @@ namespace AkVCam }; } +std::ostream &operator <<(std::ostream &os, const AkVCam::VideoFormat &format); +std::ostream &operator <<(std::ostream &os, const AkVCam::VideoFormats &formats); + #endif // AKVCAMUTILS_VIDEOFORMAT_H diff --git a/VCamUtils/src/ipcbridge.h b/VCamUtils/src/ipcbridge.h index 064e548..5e4dfa3 100644 --- a/VCamUtils/src/ipcbridge.h +++ b/VCamUtils/src/ipcbridge.h @@ -115,10 +115,10 @@ namespace AkVCam void setDescription(const std::string &deviceId, const std::string &description); - // Output pixel formats supported by the driver. + // Pixel formats supported by the driver. std::vector supportedPixelFormats(StreamType type) const; - // Default output pixel format of the driver. + // Default pixel format of the driver. PixelFormat defaultPixelFormat(StreamType type) const; // Return supported formats for the device. @@ -171,6 +171,9 @@ namespace AkVCam // Decrement the count of device listeners bool removeListener(const std::string &deviceId); + bool needsRoot(const std::string &operation) const; + int sudo(int argc, char **argv) const; + private: IpcBridgePrivate *d; diff --git a/VCamUtils/src/settings.cpp b/VCamUtils/src/settings.cpp index c6e1dc3..d753da3 100644 --- a/VCamUtils/src/settings.cpp +++ b/VCamUtils/src/settings.cpp @@ -108,10 +108,11 @@ bool AkVCam::Settings::load(const std::string &fileName) if (this->d->m_configs.count(currentGroup) < 1) this->d->m_configs[currentGroup] = {}; } else if (!element.key.empty() && !element.value.empty()) { - if (currentGroup.empty()) { + if (currentGroup.empty()) currentGroup = "General"; + + if (this->d->m_configs.count(currentGroup) < 1) this->d->m_configs[currentGroup] = {}; - } this->d->m_configs[currentGroup][element.key] = element.value; } @@ -210,14 +211,13 @@ bool AkVCam::Settings::contains(const std::string &key) const if (this->d->m_currentArray.empty()) { contains = groupConfigs.count(key) > 0; } else { - std::string arrayKey; - std::stringstream ss(arrayKey); - ss << this->d->m_currentArray - << '/' - << this->d->m_arrayIndex + 1 - << '/' - << key; - contains = groupConfigs.count(arrayKey) > 0; + std::stringstream arrayKey; + arrayKey << this->d->m_currentArray + << '/' + << this->d->m_arrayIndex + 1 + << '/' + << key; + contains = groupConfigs.count(arrayKey.str()) > 0; } return contains; @@ -237,14 +237,13 @@ std::string AkVCam::Settings::value(const std::string &key) const if (this->d->m_currentArray.empty()) { value = groupConfigs[key]; } else { - std::string arrayKey; - std::stringstream ss(arrayKey); - ss << this->d->m_currentArray - << '/' - << this->d->m_arrayIndex + 1 - << '/' - << key; - value = groupConfigs[arrayKey]; + std::stringstream arrayKey; + arrayKey << this->d->m_currentArray + << '/' + << this->d->m_arrayIndex + 1 + << '/' + << key; + value = groupConfigs[arrayKey.str()]; } return value; @@ -358,7 +357,7 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line, if (line.empty() || line[0] == '#' || line[0] == ';') { if (ok) - *ok = false; + *ok = true; return {}; } diff --git a/VCamUtils/src/utils.cpp b/VCamUtils/src/utils.cpp index 00e93b6..e148319 100644 --- a/VCamUtils/src/utils.cpp +++ b/VCamUtils/src/utils.cpp @@ -44,12 +44,20 @@ std::string AkVCam::replace(const std::string &str, const std::string &from, const std::string &to) { - auto newStr = str; + std::string newStr; - for (auto pos = newStr.find(from); - pos != std::string::npos; - pos = newStr.find(from)) - newStr.replace(pos, from.size(), to); + for (size_t i = 0; i < str.size(); i++) { + auto pos = str.find(from, i); + + if (pos == std::string::npos) { + newStr += str.substr(i); + + break; + } else { + newStr += str.substr(i, pos - i) + to; + i = pos + from.size() - 1; + } + } return newStr; } diff --git a/cmio/Assistant/src/assistant.cpp b/cmio/Assistant/src/assistant.cpp index 6a5282e..a6bd986 100644 --- a/cmio/Assistant/src/assistant.cpp +++ b/cmio/Assistant/src/assistant.cpp @@ -26,7 +26,6 @@ #include "assistant.h" #include "assistantglobals.h" -#include "PlatformUtils/src/preferences.h" #include "PlatformUtils/src/utils.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoframe.h" @@ -63,11 +62,11 @@ namespace AkVCam bool startTimer(); void stopTimer(); static void timerTimeout(CFRunLoopTimerRef timer, void *info); - void releaseDevicesFromPeer(const std::string &portName); void peerDied(); + void removePortByName(const std::string &portName); + void releaseDevicesFromPeer(const std::string &portName); void requestPort(xpc_connection_t client, xpc_object_t event); void addPort(xpc_connection_t client, xpc_object_t event); - void removePortByName(const std::string &portName); void removePort(xpc_connection_t client, xpc_object_t event); void devicesUpdate(xpc_connection_t client, xpc_object_t event); void setBroadcasting(xpc_connection_t client, xpc_object_t event); @@ -76,9 +75,9 @@ namespace AkVCam void listeners(xpc_connection_t client, xpc_object_t event); void listener(xpc_connection_t client, xpc_object_t event); void broadcasting(xpc_connection_t client, xpc_object_t event); - void controlsUpdated(xpc_connection_t client, xpc_object_t event); void listenerAdd(xpc_connection_t client, xpc_object_t event); void listenerRemove(xpc_connection_t client, xpc_object_t event); + void controlsUpdated(xpc_connection_t client, xpc_object_t event); }; } @@ -199,40 +198,10 @@ void AkVCam::AssistantPrivate::timerTimeout(CFRunLoopTimerRef timer, void *info) CFRunLoopStop(CFRunLoopGetMain()); } -void AkVCam::AssistantPrivate::releaseDevicesFromPeer(const std::string &portName) -{ - AkLogFunction(); - - for (auto &config: this->m_deviceConfigs) - if (config.second.broadcaster == portName) { - config.second.broadcaster.clear(); - - auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING); - xpc_dictionary_set_string(dictionary, "device", config.first.c_str()); - xpc_dictionary_set_string(dictionary, "broadcaster", ""); - - for (auto &client: this->m_clients) { - auto reply = - xpc_connection_send_message_with_reply_sync(client.second, - dictionary); - xpc_release(reply); - } - - xpc_release(dictionary); - } else { - auto it = std::find(config.second.listeners.begin(), - config.second.listeners.end(), - portName); - - if (it != config.second.listeners.end()) - config.second.listeners.erase(it); - } -} - void AkVCam::AssistantPrivate::peerDied() { AkLogFunction(); + std::vector deadPeers; for (auto &client: this->m_clients) { auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); @@ -249,8 +218,57 @@ void AkVCam::AssistantPrivate::peerDied() xpc_release(reply); if (!alive) - this->removePortByName(client.first); + deadPeers.push_back(client.first); } + + for (auto &peer: deadPeers) + this->removePortByName(peer); +} + +void AkVCam::AssistantPrivate::removePortByName(const std::string &portName) +{ + AkLogFunction(); + AkLogInfo() << "Port: " << portName << std::endl; + + for (auto &peer: this->m_clients) + if (peer.first == portName) { + xpc_release(peer.second); + this->m_clients.erase(portName); + + break; + } + + if (this->m_clients.empty()) + this->startTimer(); + + this->releaseDevicesFromPeer(portName); +} + +void AkVCam::AssistantPrivate::releaseDevicesFromPeer(const std::string &portName) +{ + AkLogFunction(); + + for (auto &config: this->m_deviceConfigs) + if (config.second.broadcaster == portName) { + config.second.broadcaster.clear(); + + auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); + xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING); + xpc_dictionary_set_string(dictionary, "device", config.first.c_str()); + xpc_dictionary_set_string(dictionary, "broadcaster", ""); + + for (auto &client: this->m_clients) + xpc_connection_send_message(client.second, dictionary); + + xpc_release(dictionary); + } else { + auto it = std::find(config.second.listeners.begin(), + config.second.listeners.end(), + portName); + + if (it != config.second.listeners.end()) + config.second.listeners.erase(it); + } } void AkVCam::AssistantPrivate::requestPort(xpc_connection_t client, @@ -299,25 +317,6 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client, xpc_release(reply); } -void AkVCam::AssistantPrivate::removePortByName(const std::string &portName) -{ - AkLogFunction(); - AkLogInfo() << "Port: " << portName << std::endl; - - for (auto &peer: this->m_clients) - if (peer.first == portName) { - xpc_release(peer.second); - this->m_clients.erase(portName); - - break; - } - - if (this->m_clients.empty()) - this->startTimer(); - - this->releaseDevicesFromPeer(portName); -} - void AkVCam::AssistantPrivate::removePort(xpc_connection_t client, xpc_object_t event) { @@ -332,21 +331,38 @@ void AkVCam::AssistantPrivate::devicesUpdate(xpc_connection_t client, { UNUSED(client); AkLogFunction(); - auto notification = xpc_copy(event); - for (auto &client: this->m_clients) - xpc_connection_send_message(client.second, notification); + auto devicesList = xpc_dictionary_get_array(event, "devices"); + DeviceConfigs configs; - xpc_release(notification); + for (size_t i = 0; i < xpc_array_get_count(devicesList); i++) { + std::string device = xpc_array_get_string(devicesList, i); + + if (this->m_deviceConfigs.count(device) > 0) + configs[device] = this->m_deviceConfigs[device]; + else + configs[device] = {}; + } + + this->m_deviceConfigs = configs; + + if (xpc_dictionary_get_bool(event, "propagate")) { + auto notification = xpc_copy(event); + + for (auto &client: this->m_clients) + xpc_connection_send_message(client.second, notification); + + xpc_release(notification); + } } void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client, xpc_object_t event) { + UNUSED(client); AkLogFunction(); std::string deviceId = xpc_dictionary_get_string(event, "device"); std::string broadcaster = xpc_dictionary_get_string(event, "broadcaster"); - bool ok = false; if (this->m_deviceConfigs.count(deviceId) > 0) if (this->m_deviceConfigs[deviceId].broadcaster != broadcaster) { @@ -359,13 +375,7 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client, xpc_connection_send_message(client.second, notification); xpc_release(notification); - ok = true; } - - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_bool(reply, "status", ok); - xpc_connection_send_message(client, reply); - xpc_release(reply); } void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client, @@ -378,6 +388,7 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client, bool ok = true; for (auto &client: this->m_clients) { + AkLogDebug() << "Sending frame to " << client.first << std::endl; auto reply = xpc_connection_send_message_with_reply_sync(client.second, event); auto replyType = xpc_get_type(reply); @@ -387,6 +398,10 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client, isOk = xpc_dictionary_get_bool(reply, "status"); ok &= isOk; + + if (!isOk) + AkLogError() << "Failed sending frame." << std::endl; + xpc_release(reply); } @@ -400,25 +415,12 @@ void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client, { UNUSED(client); AkLogFunction(); - auto reply = xpc_dictionary_create_reply(event); - bool ok = true; + auto notification = xpc_copy(event); - for (auto &client: this->m_clients) { - auto reply = xpc_connection_send_message_with_reply_sync(client.second, - event); - auto replyType = xpc_get_type(reply); - bool isOk = false; + for (auto &client: this->m_clients) + xpc_connection_send_message(client.second, notification); - if (replyType == XPC_TYPE_DICTIONARY) - isOk = xpc_dictionary_get_bool(reply, "status"); - - ok &= isOk; - xpc_release(reply); - } - - xpc_dictionary_set_bool(reply, "status", ok); - xpc_connection_send_message(client, reply); - xpc_release(reply); + xpc_release(notification); } void AkVCam::AssistantPrivate::listeners(xpc_connection_t client, @@ -484,22 +486,6 @@ void AkVCam::AssistantPrivate::broadcasting(xpc_connection_t client, xpc_release(reply); } -void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client, xpc_object_t event) -{ - UNUSED(client); - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - - if (this->m_deviceConfigs.count(deviceId) > 0) { - auto notification = xpc_copy(event); - - for (auto &client: this->m_clients) - xpc_connection_send_message(client.second, notification); - - xpc_release(notification); - } -} - void AkVCam::AssistantPrivate::listenerAdd(xpc_connection_t client, xpc_object_t event) { @@ -559,3 +545,27 @@ void AkVCam::AssistantPrivate::listenerRemove(xpc_connection_t client, xpc_connection_send_message(client, reply); xpc_release(reply); } + +void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client, + xpc_object_t event) +{ + UNUSED(client); + AkLogFunction(); + std::string deviceId = xpc_dictionary_get_string(event, "device"); + + if (this->m_deviceConfigs.count(deviceId) <= 0) { + AkLogError() << "'" + << deviceId + << "' device in not in the devices list." + << std::endl; + + return; + } + + auto notification = xpc_copy(event); + + for (auto &client: this->m_clients) + xpc_connection_send_message(client.second, notification); + + xpc_release(notification); +} diff --git a/cmio/Assistant/src/main.cpp b/cmio/Assistant/src/main.cpp index f3105f5..b5b9aef 100644 --- a/cmio/Assistant/src/main.cpp +++ b/cmio/Assistant/src/main.cpp @@ -47,8 +47,10 @@ int main(int argc, char **argv) auto timeout = strtod(argv[i + 1], nullptr); AkLogInfo() << "Set timeout: " << timeout << std::endl; assistant()->setTimeout(timeout); - - break; + } else if (strcmp(argv[i], "--loglevel") == 0 && i + 1 < argc) { + auto loglevel = strtoul(argv[i + 1], nullptr, 10); + AkVCam::Logger::setLogLevel(loglevel); + AkLogInfo() << "Set loglevel: " << loglevel << std::endl; } AkLogDebug() << "Setting up handler" << std::endl; diff --git a/cmio/PlatformUtils/src/preferences.cpp b/cmio/PlatformUtils/src/preferences.cpp index cebf58b..fcf4b2c 100644 --- a/cmio/PlatformUtils/src/preferences.cpp +++ b/cmio/PlatformUtils/src/preferences.cpp @@ -61,7 +61,11 @@ void AkVCam::Preferences::write(const std::string &key, AkLogFunction(); AkLogInfo() << "Writing: " << key << " = " << *value << std::endl; auto cfKey = cfTypeFromStd(key); - CFPreferencesSetAppValue(CFStringRef(*cfKey), *value, PREFERENCES_ID); + CFPreferencesSetValue(CFStringRef(*cfKey), + *value, + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); } void AkVCam::Preferences::write(const std::string &key, @@ -71,7 +75,11 @@ void AkVCam::Preferences::write(const std::string &key, AkLogInfo() << "Writing: " << key << " = " << value << std::endl; auto cfKey = cfTypeFromStd(key); auto cfValue = cfTypeFromStd(value); - CFPreferencesSetAppValue(CFStringRef(*cfKey), *cfValue, PREFERENCES_ID); + CFPreferencesSetValue(CFStringRef(*cfKey), + *cfValue, + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); } void AkVCam::Preferences::write(const std::string &key, int value) @@ -80,7 +88,11 @@ void AkVCam::Preferences::write(const std::string &key, int value) AkLogInfo() << "Writing: " << key << " = " << value << std::endl; auto cfKey = cfTypeFromStd(key); auto cfValue = cfTypeFromStd(value); - CFPreferencesSetAppValue(CFStringRef(*cfKey), *cfValue, PREFERENCES_ID); + CFPreferencesSetValue(CFStringRef(*cfKey), + *cfValue, + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); } void AkVCam::Preferences::write(const std::string &key, double value) @@ -89,7 +101,11 @@ void AkVCam::Preferences::write(const std::string &key, double value) AkLogInfo() << "Writing: " << key << " = " << value << std::endl; auto cfKey = cfTypeFromStd(key); auto cfValue = cfTypeFromStd(value); - CFPreferencesSetAppValue(CFStringRef(*cfKey), *cfValue, PREFERENCES_ID); + CFPreferencesSetValue(CFStringRef(*cfKey), + *cfValue, + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); } void AkVCam::Preferences::write(const std::string &key, @@ -103,8 +119,10 @@ std::shared_ptr AkVCam::Preferences::read(const std::string &key) { AkLogFunction(); auto cfKey = cfTypeFromStd(key); - auto cfValue = CFTypeRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), - PREFERENCES_ID)); + auto cfValue = CFTypeRef(CFPreferencesCopyValue(CFStringRef(*cfKey), + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost)); if (!cfValue) return {}; @@ -122,8 +140,10 @@ std::string AkVCam::Preferences::readString(const std::string &key, AkLogFunction(); auto cfKey = cfTypeFromStd(key); auto cfValue = - CFStringRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), - PREFERENCES_ID)); + CFStringRef(CFPreferencesCopyValue(CFStringRef(*cfKey), + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost)); auto value = defaultValue; if (cfValue) { @@ -139,8 +159,10 @@ int AkVCam::Preferences::readInt(const std::string &key, int defaultValue) AkLogFunction(); auto cfKey = cfTypeFromStd(key); auto cfValue = - CFNumberRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), - PREFERENCES_ID)); + CFNumberRef(CFPreferencesCopyValue(CFStringRef(*cfKey), + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost)); auto value = defaultValue; if (cfValue) { @@ -157,8 +179,10 @@ double AkVCam::Preferences::readDouble(const std::string &key, AkLogFunction(); auto cfKey = cfTypeFromStd(key); auto cfValue = - CFNumberRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), - PREFERENCES_ID)); + CFNumberRef(CFPreferencesCopyValue(CFStringRef(*cfKey), + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost)); auto value = defaultValue; if (cfValue) { @@ -174,8 +198,10 @@ bool AkVCam::Preferences::readBool(const std::string &key, bool defaultValue) AkLogFunction(); auto cfKey = cfTypeFromStd(key); auto cfValue = - CFBooleanRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), - PREFERENCES_ID)); + CFBooleanRef(CFPreferencesCopyValue(CFStringRef(*cfKey), + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost)); auto value = defaultValue; if (cfValue) { @@ -202,7 +228,11 @@ void AkVCam::Preferences::deleteKey(const std::string &key) AkLogFunction(); AkLogInfo() << "Deleting " << key << std::endl; auto cfKey = cfTypeFromStd(key); - CFPreferencesSetAppValue(CFStringRef(*cfKey), nullptr, PREFERENCES_ID); + CFPreferencesSetValue(CFStringRef(*cfKey), + nullptr, + PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); } void AkVCam::Preferences::deleteAllKeys(const std::string &key) @@ -250,7 +280,9 @@ void AkVCam::Preferences::moveAll(const std::string &keyFrom, void AkVCam::Preferences::sync() { AkLogFunction(); - CFPreferencesAppSynchronize(PREFERENCES_ID); + CFPreferencesSynchronize(PREFERENCES_ID, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); } std::string AkVCam::Preferences::addDevice(const std::string &description) diff --git a/cmio/VCamIPC/src/ipcbridge.mm b/cmio/VCamIPC/src/ipcbridge.mm index 843fdc7..9b80861 100644 --- a/cmio/VCamIPC/src/ipcbridge.mm +++ b/cmio/VCamIPC/src/ipcbridge.mm @@ -64,6 +64,7 @@ namespace AkVCam void remove(IpcBridge *bridge); inline std::vector &bridges(); inline const std::vector &controls() const; + void updateDevices(xpc_connection_t port, bool propagate); // Message handling methods void isAlive(xpc_connection_t client, xpc_object_t event); @@ -155,6 +156,8 @@ bool AkVCam::IpcBridge::registerPeer() xpc_type_t replyType; bool status = false; + AkLogDebug() << "Requesting service." << std::endl; + auto serverMessagePort = xpc_connection_create_mach_service(CMIO_ASSISTANT_NAME, nullptr, @@ -163,6 +166,8 @@ bool AkVCam::IpcBridge::registerPeer() if (!serverMessagePort) goto registerEndPoint_failed; + AkLogDebug() << "Setting event handler." << std::endl; + xpc_connection_set_event_handler(serverMessagePort, ^(xpc_object_t event) { if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { ipcBridgePrivate().connectionInterrupted(); @@ -170,6 +175,8 @@ bool AkVCam::IpcBridge::registerPeer() }); xpc_connection_resume(serverMessagePort); + AkLogDebug() << "Requesting port." << std::endl; + dictionary = xpc_dictionary_create(nullptr, nullptr, 0); xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_REQUEST_PORT); reply = xpc_connection_send_message_with_reply_sync(serverMessagePort, @@ -185,6 +192,8 @@ bool AkVCam::IpcBridge::registerPeer() if (replyType != XPC_TYPE_DICTIONARY) goto registerEndPoint_failed; + AkLogDebug() << "Creating message port." << std::endl; + messagePort = xpc_connection_create(nullptr, nullptr); if (!messagePort) @@ -207,6 +216,8 @@ bool AkVCam::IpcBridge::registerPeer() xpc_connection_resume(messagePort); + AkLogDebug() << "Adding port to the server." << std::endl; + dictionary = xpc_dictionary_create(nullptr, nullptr, 0); xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_ADD_PORT); xpc_dictionary_set_string(dictionary, "port", portName.c_str()); @@ -229,6 +240,7 @@ bool AkVCam::IpcBridge::registerPeer() this->d->m_serverMessagePort = serverMessagePort; AkLogInfo() << "SUCCESSFUL" << std::endl; + this->d->updateDevices(serverMessagePort, false); return true; @@ -273,9 +285,9 @@ void AkVCam::IpcBridge::unregisterPeer() std::vector AkVCam::IpcBridge::devices() const { AkLogFunction(); - AkLogInfo() << "Devices:" << std::endl; std::vector devices; auto nCameras = Preferences::camerasCount(); + AkLogInfo() << "Devices:" << std::endl; for (size_t i = 0; i < nCameras; i++) { auto deviceId = Preferences::cameraPath(i); @@ -522,16 +534,20 @@ std::string AkVCam::IpcBridge::clientExe(uint64_t pid) const std::string AkVCam::IpcBridge::addDevice(const std::string &description) { + AkLogFunction(); + return Preferences::addDevice(description); } void AkVCam::IpcBridge::removeDevice(const std::string &deviceId) { + AkLogFunction(); + Preferences::removeCamera(deviceId); } void AkVCam::IpcBridge::addFormat(const std::string &deviceId, - const AkVCam::VideoFormat &format, + const VideoFormat &format, int index) { AkLogFunction(); @@ -556,16 +572,7 @@ void AkVCam::IpcBridge::removeFormat(const std::string &deviceId, int index) void AkVCam::IpcBridge::updateDevices() { AkLogFunction(); - - if (!this->d->m_serverMessagePort) - return; - - auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, - "message", - AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE); - xpc_connection_send_message(this->d->m_serverMessagePort, dictionary); - xpc_release(dictionary); + this->d->updateDevices(this->d->m_serverMessagePort, true); } bool AkVCam::IpcBridge::deviceStart(const std::string &deviceId, @@ -594,23 +601,12 @@ bool AkVCam::IpcBridge::deviceStart(const std::string &deviceId, xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING); xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); xpc_dictionary_set_string(dictionary, "broadcaster", this->d->m_portName.c_str()); - auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, - dictionary); + xpc_connection_send_message(this->d->m_serverMessagePort, dictionary); xpc_release(dictionary); - auto replyType = xpc_get_type(reply); - if (replyType != XPC_TYPE_DICTIONARY) { - AkLogError() << "Invalid reply received." << std::endl; - xpc_release(reply); - - return false; - } - - bool status = xpc_dictionary_get_bool(reply, "status"); - xpc_release(reply); this->d->m_broadcasting.push_back(deviceId); - return status; + return true; } void AkVCam::IpcBridge::deviceStop(const std::string &deviceId) @@ -631,10 +627,9 @@ void AkVCam::IpcBridge::deviceStop(const std::string &deviceId) xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING); xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); xpc_dictionary_set_string(dictionary, "broadcaster", ""); - auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, - dictionary); + xpc_connection_send_message(this->d->m_serverMessagePort, dictionary); xpc_release(dictionary); - xpc_release(reply); + this->d->m_broadcasting.erase(it); } @@ -766,6 +761,21 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId) return status; } +bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const +{ + UNUSED(operation); + + return false; +} + +int AkVCam::IpcBridge::sudo(int argc, char **argv) const +{ + UNUSED(argc); + UNUSED(argv); + + return 0; +} + AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self): self(self), m_messagePort(nullptr), @@ -833,6 +843,32 @@ const std::vector &AkVCam::IpcBridgePrivate::controls() c return controls; } +void AkVCam::IpcBridgePrivate::updateDevices(xpc_connection_t port, bool propagate) +{ + AkLogFunction(); + + if (!port) + return; + + auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); + xpc_dictionary_set_int64(dictionary, + "message", + AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE); + auto devices = xpc_array_create(nullptr, 0); + + for (size_t i = 0; i < Preferences::camerasCount(); i++) { + auto path = Preferences::cameraPath(i); + auto pathObj = xpc_string_create(path.c_str()); + xpc_array_append_value(devices, pathObj); + AkLogDebug() << "Device " << i << ": " << path << std::endl; + } + + xpc_dictionary_set_value(dictionary, "devices", devices); + xpc_dictionary_set_bool(dictionary, "propagate", propagate); + xpc_connection_send_message(port, dictionary); + xpc_release(dictionary); +} + void AkVCam::IpcBridgePrivate::isAlive(xpc_connection_t client, xpc_object_t event) { diff --git a/cmio/VirtualCamera/src/device.cpp b/cmio/VirtualCamera/src/device.cpp index 07e65f9..0964b9a 100644 --- a/cmio/VirtualCamera/src/device.cpp +++ b/cmio/VirtualCamera/src/device.cpp @@ -68,6 +68,7 @@ OSStatus AkVCam::Device::createObject() OSStatus AkVCam::Device::registerObject(bool regist) { AkLogFunction(); + AkLogDebug() << "Register: " << regist << std::endl; OSStatus status = kCMIOHardwareUnspecifiedError; if (!this->m_isCreated @@ -91,6 +92,11 @@ OSStatus AkVCam::Device::registerObject(bool regist) &this->m_objectID); } + if (status == kCMIOHardwareNoError) + AkLogDebug() << "Ok"; + else + AkLogDebug() << "Error registering device" << std::endl; + return status; } @@ -134,6 +140,7 @@ std::list AkVCam::Device::addStreams(int n) OSStatus AkVCam::Device::registerStreams(bool regist) { AkLogFunction(); + AkLogDebug() << "Register: " << regist << std::endl; OSStatus status = kCMIOHardwareUnspecifiedError; if (!this->m_isCreated @@ -163,6 +170,11 @@ OSStatus AkVCam::Device::registerStreams(bool regist) streams.data()); } + if (status == kCMIOHardwareNoError) + AkLogDebug() << "Ok"; + else + AkLogDebug() << "Error registering streams" << std::endl; + return status; } @@ -338,6 +350,7 @@ OSStatus AkVCam::Device::processRS422Command(CMIODeviceRS422Command *ioRS422Comm void AkVCam::Device::updateStreamsProperty() { + AkLogFunction(); std::vector streams; for (auto &stream: this->m_streams) diff --git a/cmio/VirtualCamera/src/plugininterface.cpp b/cmio/VirtualCamera/src/plugininterface.cpp index 6f91444..deee02b 100644 --- a/cmio/VirtualCamera/src/plugininterface.cpp +++ b/cmio/VirtualCamera/src/plugininterface.cpp @@ -437,6 +437,7 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId, stream->properties().setProperty(kCMIOStreamPropertyDirection, 0); if (device->registerStreams() != kCMIOHardwareNoError) { + AkLogDebug() << "Failed registering streams" << std::endl; device->registerStreams(false); goto createDevice_failed; @@ -444,6 +445,7 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId, // Register the device. if (device->registerObject() != kCMIOHardwareNoError) { + AkLogDebug() << "Failed registering device" << std::endl; device->registerObject(false); device->registerStreams(false); diff --git a/cmio/VirtualCamera/src/stream.cpp b/cmio/VirtualCamera/src/stream.cpp index 9a348f6..adb73e9 100644 --- a/cmio/VirtualCamera/src/stream.cpp +++ b/cmio/VirtualCamera/src/stream.cpp @@ -309,6 +309,7 @@ void AkVCam::Stream::frameReady(const AkVCam::VideoFrame &frame) void AkVCam::Stream::setBroadcasting(const std::string &broadcaster) { AkLogFunction(); + AkLogDebug() << "Broadcaster: " << broadcaster << std::endl; if (this->d->m_broadcaster == broadcaster) return; @@ -325,6 +326,7 @@ void AkVCam::Stream::setBroadcasting(const std::string &broadcaster) void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror) { AkLogFunction(); + AkLogDebug() << "Mirror: " << horizontalMirror << std::endl; if (this->d->m_horizontalMirror == horizontalMirror) return; @@ -342,6 +344,7 @@ void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror) void AkVCam::Stream::setVerticalMirror(bool verticalMirror) { AkLogFunction(); + AkLogDebug() << "Mirror: " << verticalMirror << std::endl; if (this->d->m_verticalMirror == verticalMirror) return; @@ -359,6 +362,7 @@ void AkVCam::Stream::setVerticalMirror(bool verticalMirror) void AkVCam::Stream::setScaling(Scaling scaling) { AkLogFunction(); + AkLogDebug() << "Scaling: " << scaling << std::endl; if (this->d->m_scaling == scaling) return; @@ -376,6 +380,7 @@ void AkVCam::Stream::setScaling(Scaling scaling) void AkVCam::Stream::setAspectRatio(AspectRatio aspectRatio) { AkLogFunction(); + AkLogDebug() << "Aspect ratio: " << aspectRatio << std::endl; if (this->d->m_aspectRatio == aspectRatio) return; @@ -393,6 +398,7 @@ void AkVCam::Stream::setAspectRatio(AspectRatio aspectRatio) void AkVCam::Stream::setSwapRgb(bool swap) { AkLogFunction(); + AkLogDebug() << "Swap: " << swap << std::endl; if (this->d->m_swapRgb == swap) return; @@ -625,11 +631,14 @@ void AkVCam::StreamPrivate::sendFrame(const VideoFrame &frame) void AkVCam::StreamPrivate::updateTestFrame() { + AkLogFunction(); this->m_testFrameAdapted = this->applyAdjusts(this->m_testFrame); } AkVCam::VideoFrame AkVCam::StreamPrivate::applyAdjusts(const VideoFrame &frame) { + AkLogFunction(); + VideoFormat format; this->self->m_properties.getProperty(kCMIOStreamPropertyFormatDescription, &format); diff --git a/cmio/cmio.pri b/cmio/cmio.pri index 7ad7668..bb8ca2f 100644 --- a/cmio/cmio.pri +++ b/cmio/cmio.pri @@ -21,7 +21,7 @@ isEmpty(CMIO_PLUGIN_NAME): isEmpty(CMIO_PLUGIN_ASSISTANT_NAME): CMIO_PLUGIN_ASSISTANT_NAME = AkVCamAssistant isEmpty(CMIO_PLUGIN_DEVICE_PREFIX): - CMIO_PLUGIN_DEVICE_PREFIX = /akvcam/video + CMIO_PLUGIN_DEVICE_PREFIX = AkVCamVideoDevice isEmpty(CMIO_PLUGIN_VENDOR): CMIO_PLUGIN_VENDOR = "Webcamoid Project" isEmpty(CMIO_PLUGIN_PRODUCT): diff --git a/dshow/Assistant/src/service.cpp b/dshow/Assistant/src/service.cpp index 8af500c..f2f91ac 100644 --- a/dshow/Assistant/src/service.cpp +++ b/dshow/Assistant/src/service.cpp @@ -31,6 +31,7 @@ #include "service.h" #include "PlatformUtils/src/messageserver.h" +#include "PlatformUtils/src/preferences.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/timer.h" @@ -53,27 +54,26 @@ namespace AkVCam SERVICE_STATUS m_status; SERVICE_STATUS_HANDLE m_statusHandler; MessageServer m_messageServer; - AssistantPeers m_servers; AssistantPeers m_clients; DeviceConfigs m_deviceConfigs; Timer m_timer; std::mutex m_peerMutex; ServicePrivate(); + inline static uint64_t id(); static void stateChanged(void *userData, MessageServer::State state); - static void checkPeers(void *userData); void sendStatus(DWORD currentState, DWORD exitCode, DWORD wait); - inline static uint64_t id(); + static void checkPeers(void *userData); void removePortByName(const std::string &portName); void releaseDevicesFromPeer(const std::string &portName); void requestPort(Message *message); void addPort(Message *message); void removePort(Message *message); + void devicesUpdate(Message *message); void setBroadCasting(Message *message); void frameReady(Message *message); void pictureUpdated(Message *message); - void deviceUpdate(Message *message); void listeners(Message *message); void listener(Message *message); void broadcasting(Message *message); @@ -271,7 +271,7 @@ AkVCam::ServicePrivate::ServicePrivate() {AKVCAM_ASSISTANT_MSG_REQUEST_PORT , AKVCAM_BIND_FUNC(ServicePrivate::requestPort) }, {AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(ServicePrivate::addPort) }, {AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(ServicePrivate::removePort) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(ServicePrivate::deviceUpdate) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(ServicePrivate::devicesUpdate) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(ServicePrivate::listenerAdd) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , AKVCAM_BIND_FUNC(ServicePrivate::listenerRemove) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS , AKVCAM_BIND_FUNC(ServicePrivate::listeners) }, @@ -282,6 +282,14 @@ AkVCam::ServicePrivate::ServicePrivate() }); this->m_timer.setInterval(60000); this->m_timer.connectTimeout(this, &ServicePrivate::checkPeers); + this->m_timer.start(); +} + +uint64_t AkVCam::ServicePrivate::id() +{ + static uint64_t id = 0; + + return id++; } void AkVCam::ServicePrivate::stateChanged(void *userData, @@ -308,37 +316,6 @@ void AkVCam::ServicePrivate::stateChanged(void *userData, } } -void AkVCam::ServicePrivate::checkPeers(void *userData) -{ - auto self = reinterpret_cast(userData); - std::vector removePorts; - - self->m_peerMutex.lock(); - std::vector allPeers { - &self->m_clients, - &self->m_servers - }; - - for (auto peers: allPeers) - for (auto &peer: *peers) { - Message message; - message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE; - message.dataSize = sizeof(MsgIsAlive); - MessageServer::sendMessage(peer.second, &message); - auto requestData = messageData(&message); - - if (!requestData->alive) - removePorts.push_back(peer.first); - } - - self->m_peerMutex.unlock(); - - for (auto &port: removePorts) { - AkLogWarning() << port << " died, removing..." << std::endl; - self->removePortByName(port); - } -} - void AkVCam::ServicePrivate::sendStatus(DWORD currentState, DWORD exitCode, DWORD wait) @@ -358,51 +335,55 @@ void AkVCam::ServicePrivate::sendStatus(DWORD currentState, SetServiceStatus(this->m_statusHandler, &this->m_status); } -uint64_t AkVCam::ServicePrivate::id() +void AkVCam::ServicePrivate::checkPeers(void *userData) { - static uint64_t id = 0; + AkLogFunction(); + auto self = reinterpret_cast(userData); + std::vector deadPeers; - return id++; + self->m_peerMutex.lock(); + + for (auto &client: self->m_clients) { + Message message; + message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE; + message.dataSize = sizeof(MsgIsAlive); + MessageServer::sendMessage(client.second, &message); + auto requestData = messageData(&message); + + if (!requestData->alive) + deadPeers.push_back(client.first); + } + + self->m_peerMutex.unlock(); + + for (auto &port: deadPeers) { + AkLogWarning() << port << " died, removing..." << std::endl; + self->removePortByName(port); + } } void AkVCam::ServicePrivate::removePortByName(const std::string &portName) { AkLogFunction(); - AkLogInfo() << "Port: " << portName << std::endl; + AkLogDebug() << "Port: " << portName << std::endl; this->m_peerMutex.lock(); - std::vector allPeers { - &this->m_clients, - &this->m_servers - }; + for (auto &peer: this->m_clients) + if (peer.first == portName) { + this->m_clients.erase(portName); - bool breakLoop = false; - - for (auto peers: allPeers) { - for (auto &peer: *peers) - if (peer.first == portName) { - peers->erase(portName); - breakLoop = true; - - break; - } - - if (breakLoop) break; - } + } - bool peersEmpty = this->m_servers.empty() && this->m_clients.empty(); this->m_peerMutex.unlock(); - - if (peersEmpty) - this->m_timer.stop(); - this->releaseDevicesFromPeer(portName); } void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName) { + AkLogFunction(); + for (auto &config: this->m_deviceConfigs) if (config.second.broadcaster == portName) { config.second.broadcaster.clear(); @@ -428,6 +409,8 @@ void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName) if (it != config.second.listeners.end()) config.second.listeners.erase(it); } + + AkLogInfo() << portName << " released." << std::endl; } void AkVCam::ServicePrivate::requestPort(AkVCam::Message *message) @@ -453,15 +436,9 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message) bool ok = true; this->m_peerMutex.lock(); - AssistantPeers *peers; - if (portName.find(AKVCAM_ASSISTANT_CLIENT_NAME) != std::string::npos) - peers = &this->m_clients; - else - peers = &this->m_servers; - - for (auto &peer: *peers) - if (peer.first == portName) { + for (auto &client: this->m_clients) + if (client.first == portName) { ok = false; break; @@ -469,16 +446,10 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message) if (ok) { AkLogInfo() << "Adding Peer: " << portName << std::endl; - (*peers)[portName] = pipeName; + this->m_clients[portName] = pipeName; } - size_t nPeers = this->m_servers.size() + this->m_clients.size(); - this->m_peerMutex.unlock(); - - if (ok && nPeers == 1) - this->m_timer.start(); - data->status = ok; } @@ -490,6 +461,31 @@ void AkVCam::ServicePrivate::removePort(AkVCam::Message *message) this->removePortByName(data->port); } +void AkVCam::ServicePrivate::devicesUpdate(AkVCam::Message *message) +{ + AkLogFunction(); + auto data = messageData(message); + DeviceConfigs configs; + + for (size_t i = 0; i < Preferences::camerasCount(); i++) { + auto device = Preferences::cameraPath(i); + + if (this->m_deviceConfigs.count(device) > 0) + configs[device] = this->m_deviceConfigs[device]; + else + configs[device] = {}; + } + + this->m_peerMutex.lock(); + this->m_deviceConfigs = configs; + + if (data->propagate) + for (auto &client: this->m_clients) + MessageServer::sendMessage(client.second, message); + + this->m_peerMutex.unlock(); +} + void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message) { AkLogFunction(); @@ -498,25 +494,22 @@ void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message) std::string broadcaster(data->broadcaster); data->status = false; - if (this->m_deviceConfigs.count(deviceId) < 1) - this->m_deviceConfigs[deviceId] = {}; + if (this->m_deviceConfigs.count(deviceId) > 0) + if (this->m_deviceConfigs[deviceId].broadcaster != broadcaster) { + AkLogInfo() << "Device: " << deviceId << std::endl; + AkLogInfo() << "Broadcaster: " << broadcaster << std::endl; + this->m_deviceConfigs[deviceId].broadcaster = broadcaster; + data->status = true; - if (this->m_deviceConfigs[deviceId].broadcaster == broadcaster) - return; + this->m_peerMutex.lock(); - AkLogInfo() << "Device: " << deviceId << std::endl; - AkLogInfo() << "Broadcaster: " << broadcaster << std::endl; - this->m_deviceConfigs[deviceId].broadcaster = broadcaster; - data->status = true; + for (auto &client: this->m_clients) { + Message msg(message); + MessageServer::sendMessage(client.second, &msg); + } - this->m_peerMutex.lock(); - - for (auto &client: this->m_clients) { - Message msg(message); - MessageServer::sendMessage(client.second, &msg); - } - - this->m_peerMutex.unlock(); + this->m_peerMutex.unlock(); + } } void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message) @@ -541,17 +534,6 @@ void AkVCam::ServicePrivate::pictureUpdated(AkVCam::Message *message) this->m_peerMutex.unlock(); } -void AkVCam::ServicePrivate::deviceUpdate(AkVCam::Message *message) -{ - AkLogFunction(); - this->m_peerMutex.lock(); - - for (auto &client: this->m_clients) - MessageServer::sendMessage(client.second, message); - - this->m_peerMutex.unlock(); -} - void AkVCam::ServicePrivate::listeners(AkVCam::Message *message) { AkLogFunction(); @@ -707,7 +689,7 @@ DWORD WINAPI controlHandler(DWORD control, AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); - AkVCam::servicePrivate()->m_messageServer.stop(); + AkVCam::servicePrivate()->m_messageServer.stop(true); result = NO_ERROR; break; @@ -732,7 +714,7 @@ BOOL WINAPI controlDebugHandler(DWORD control) AkLogFunction(); if (control == CTRL_BREAK_EVENT || control == CTRL_C_EVENT) { - AkVCam::servicePrivate()->m_messageServer.stop(); + AkVCam::servicePrivate()->m_messageServer.stop(true); return TRUE; } diff --git a/dshow/PlatformUtils/src/messagecommons.h b/dshow/PlatformUtils/src/messagecommons.h index 2c01b30..1ffd58b 100644 --- a/dshow/PlatformUtils/src/messagecommons.h +++ b/dshow/PlatformUtils/src/messagecommons.h @@ -141,14 +141,9 @@ namespace AkVCam char port[MAX_STRING]; }; - struct MsgDeviceAdded + struct MsgDevicesUpdated { - char device[MAX_STRING]; - }; - - struct MsgDeviceRemoved - { - char device[MAX_STRING]; + bool propagate; }; struct MsgBroadcasting diff --git a/dshow/PlatformUtils/src/messageserver.cpp b/dshow/PlatformUtils/src/messageserver.cpp index 503bd73..e7e8b96 100644 --- a/dshow/PlatformUtils/src/messageserver.cpp +++ b/dshow/PlatformUtils/src/messageserver.cpp @@ -17,6 +17,8 @@ * Web-Site: http://webcamoid.github.io/ */ +#include +#include #include #include #include @@ -29,7 +31,15 @@ #include "VCamUtils/src/logger.h" namespace AkVCam -{ +{ + struct PipeThread + { + std::shared_ptr thread; + std::atomic finished {false}; + }; + + using PipeThreadPtr = std::shared_ptr; + class MessageServerPrivate { public: @@ -38,9 +48,8 @@ namespace AkVCam std::map m_handlers; MessageServer::ServerMode m_mode {MessageServer::ServerModeReceive}; MessageServer::PipeState m_pipeState {MessageServer::PipeStateGone}; - HANDLE m_pipe {INVALID_HANDLE_VALUE}; - OVERLAPPED m_overlapped; - std::thread m_thread; + std::thread m_mainThread; + std::vector m_clientsThreads; std::mutex m_mutex; std::condition_variable_any m_exitCheckLoop; int m_checkInterval {5000}; @@ -52,10 +61,8 @@ namespace AkVCam bool startSend(); void stopSend(); void messagesLoop(); + void processPipe(PipeThreadPtr pipeThread, HANDLE pipe); void checkLoop(); - HRESULT waitResult(DWORD *bytesTransferred); - bool readMessage(Message *message); - bool writeMessage(const Message &message); }; } @@ -66,7 +73,7 @@ AkVCam::MessageServer::MessageServer() AkVCam::MessageServer::~MessageServer() { - this->stop(true); + this->stop(); delete this->d; } @@ -177,27 +184,116 @@ BOOL AkVCam::MessageServer::sendMessage(const std::string &pipeName, Message *messageOut, uint32_t timeout) { - DWORD bytesTransferred = 0; + AkLogFunction(); + AkLogDebug() << "Pipe: " << pipeName << std::endl; + AkLogDebug() << "Message ID: " << stringFromMessageId(messageIn.messageId) << std::endl; - return CallNamedPipeA(pipeName.c_str(), - const_cast(&messageIn), - DWORD(sizeof(Message)), - messageOut, - DWORD(sizeof(Message)), - &bytesTransferred, - timeout); + // CallNamedPie can sometimes return false without ever sending any data to + // the server, try many times before returning. + bool result; + + for (int i = 0; i < 5; i++) { + DWORD bytesTransferred = 0; + result = CallNamedPipeA(pipeName.c_str(), + const_cast(&messageIn), + DWORD(sizeof(Message)), + messageOut, + DWORD(sizeof(Message)), + &bytesTransferred, + timeout); + + if (result) + break; + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + + if (!result) { + AkLogError() << "Error sending message" << std::endl; + auto lastError = GetLastError(); + + if (lastError) + AkLogError() << stringFromError(lastError) << std::endl; + } + + return result; } AkVCam::MessageServerPrivate::MessageServerPrivate(MessageServer *self): self(self) { - memset(&this->m_overlapped, 0, sizeof(OVERLAPPED)); } bool AkVCam::MessageServerPrivate::startReceive(bool wait) { + AkLogFunction(); AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateAboutToStart) - bool ok = false; + this->m_running = true; + + if (wait) { + AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStarted) + AkLogDebug() << "Server ready." << std::endl; + this->messagesLoop(); + } else { + this->m_mainThread = std::thread(&MessageServerPrivate::messagesLoop, this); + AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStarted) + AkLogDebug() << "Server ready." << std::endl; + } + + return true; +} + +void AkVCam::MessageServerPrivate::stopReceive(bool wait) +{ + AkLogFunction(); + + if (!this->m_running) + return; + + AkLogDebug() << "Stopping clients threads." << std::endl; + AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateAboutToStop) + this->m_running = false; + + // wake up current pipe thread. + Message message; + message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE; + message.dataSize = sizeof(MsgIsAlive); + MessageServer::sendMessage(this->m_pipeName, &message); + + if (!wait) + this->m_mainThread.join(); +} + +bool AkVCam::MessageServerPrivate::startSend() +{ + AkLogFunction(); + AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl; + this->m_running = true; + this->m_mainThread = std::thread(&MessageServerPrivate::checkLoop, this); + + return true; +} + +void AkVCam::MessageServerPrivate::stopSend() +{ + AkLogFunction(); + + if (!this->m_running) + return; + + AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl; + this->m_running = false; + this->m_mutex.lock(); + this->m_exitCheckLoop.notify_all(); + this->m_mutex.unlock(); + this->m_mainThread.join(); + this->m_pipeState = MessageServer::PipeStateGone; +} + +void AkVCam::MessageServerPrivate::messagesLoop() +{ + AkLogFunction(); + AkLogDebug() << "Initializing security descriptor." << std::endl; // Define who can read and write from pipe. @@ -205,7 +301,7 @@ bool AkVCam::MessageServerPrivate::startReceive(bool wait) * * https://msdn.microsoft.com/en-us/library/windows/desktop/aa379570(v=vs.85).aspx */ - TCHAR descriptor[] = + static const TCHAR descriptor[] = TEXT("D:") // Discretionary ACL TEXT("(D;OICI;GA;;;BG)") // Deny access to Built-in Guests TEXT("(D;OICI;GA;;;AN)") // Deny access to Anonymous Logon @@ -216,144 +312,160 @@ bool AkVCam::MessageServerPrivate::startReceive(bool wait) PSECURITY_DESCRIPTOR securityDescriptor = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - if (!securityDescriptor) - goto startReceive_failed; + if (!securityDescriptor) { + AkLogError() << "Security descriptor not allocated" << std::endl; + + return; + } if (!InitializeSecurityDescriptor(securityDescriptor, - SECURITY_DESCRIPTOR_REVISION)) - goto startReceive_failed; + SECURITY_DESCRIPTOR_REVISION)) { + LocalFree(securityDescriptor); + AkLogError() << "Can't initialize security descriptor: " + << stringFromError(GetLastError()) + << " (" << GetLastError() << ")" + << std::endl; + + return; + } + + AkLogDebug() << "Getting security descriptor from string." << std::endl; if (!ConvertStringSecurityDescriptorToSecurityDescriptor(descriptor, SDDL_REVISION_1, &securityDescriptor, - nullptr)) - goto startReceive_failed; + nullptr)) { + LocalFree(securityDescriptor); + AkLogError() << "Can't read security descriptor from string: " + << stringFromError(GetLastError()) + << " (" << GetLastError() << ")" + << std::endl; + + return; + } securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); securityAttributes.lpSecurityDescriptor = securityDescriptor; securityAttributes.bInheritHandle = TRUE; - - // Create a read/write message type pipe. - this->m_pipe = CreateNamedPipeA(this->m_pipeName.c_str(), - PIPE_ACCESS_DUPLEX - | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_MESSAGE - | PIPE_READMODE_BYTE - | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, - sizeof(Message), - sizeof(Message), - NMPWAIT_USE_DEFAULT_WAIT, - &securityAttributes); - - if (this->m_pipe == INVALID_HANDLE_VALUE) - goto startReceive_failed; - - memset(&this->m_overlapped, 0, sizeof(OVERLAPPED)); - this->m_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); - AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStarted) - this->m_running = true; - - if (wait) - this->messagesLoop(); - else - this->m_thread = - std::thread(&MessageServerPrivate::messagesLoop, this); - - ok = true; - -startReceive_failed: - - if (!ok) { - AkLogError() << "Error starting server: " - << errorToString(GetLastError()) - << " (" << GetLastError() << ")" - << std::endl; - AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped) - } - - if (securityDescriptor) - LocalFree(securityDescriptor); - - return ok; -} - -void AkVCam::MessageServerPrivate::stopReceive(bool wait) -{ - if (!this->m_running) - return; - - this->m_running = false; - SetEvent(this->m_overlapped.hEvent); - - if (wait) - this->m_thread.join(); -} - -bool AkVCam::MessageServerPrivate::startSend() -{ - this->m_running = true; - this->m_thread = std::thread(&MessageServerPrivate::checkLoop, this); - - return true; -} - -void AkVCam::MessageServerPrivate::stopSend() -{ - if (!this->m_running) - return; - - this->m_running = false; - this->m_mutex.lock(); - this->m_exitCheckLoop.notify_all(); - this->m_mutex.unlock(); - this->m_thread.join(); - this->m_pipeState = MessageServer::PipeStateGone; -} - -void AkVCam::MessageServerPrivate::messagesLoop() -{ - DWORD bytesTransferred = 0; + AkLogDebug() << "Pipe name: " << this->m_pipeName << std::endl; while (this->m_running) { - HRESULT result = S_OK; + // Clean death threads. + for (;;) { + auto it = std::find_if(this->m_clientsThreads.begin(), + this->m_clientsThreads.end(), + [] (const PipeThreadPtr &thread) -> bool { + return thread->finished; + }); - // Wait for a connection. - if (!ConnectNamedPipe(this->m_pipe, &this->m_overlapped)) - result = this->waitResult(&bytesTransferred); + if (it == this->m_clientsThreads.end()) + break; - if (result == S_OK) { - Message message; - - if (this->readMessage(&message)) { - if (this->m_handlers.count(message.messageId)) - this->m_handlers[message.messageId](&message); - - this->writeMessage(message); - } + (*it)->thread->join(); + this->m_clientsThreads.erase(it); } - DisconnectNamedPipe(this->m_pipe); + AkLogDebug() << "Creating pipe." << std::endl; + auto pipe = CreateNamedPipeA(this->m_pipeName.c_str(), + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE + | PIPE_READMODE_MESSAGE + | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + sizeof(Message), + sizeof(Message), + 0, + &securityAttributes); + + if (pipe == INVALID_HANDLE_VALUE) + continue; + + AkLogDebug() << "Connecting pipe." << std::endl; + auto connected = ConnectNamedPipe(pipe, nullptr); + + if (connected || GetLastError() == ERROR_PIPE_CONNECTED) { + auto thread = std::make_shared(); + thread->thread = + std::make_shared(&MessageServerPrivate::processPipe, + this, + thread, + pipe); + this->m_clientsThreads.push_back(thread); + } else { + AkLogError() << "Failed connecting pipe." << std::endl; + CloseHandle(pipe); + } } + for (auto &thread: this->m_clientsThreads) + thread->thread->join(); + + LocalFree(securityDescriptor); AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped) + AkLogDebug() << "Server stopped." << std::endl; +} - if (this->m_overlapped.hEvent != INVALID_HANDLE_VALUE) { - CloseHandle(this->m_overlapped.hEvent); - memset(&this->m_overlapped, 0, sizeof(OVERLAPPED)); +void AkVCam::MessageServerPrivate::processPipe(PipeThreadPtr pipeThread, + HANDLE pipe) +{ + for (;;) { + AkLogDebug() << "Reading message." << std::endl; + + Message message; + DWORD bytesTransferred = 0; + auto result = ReadFile(pipe, + &message, + DWORD(sizeof(Message)), + &bytesTransferred, + nullptr); + + if (!result || bytesTransferred == 0) { + AkLogError() << "Failed reading from pipe." << std::endl; + auto lastError = GetLastError(); + + if (lastError) + AkLogError() << stringFromError(lastError) << std::endl; + + break; + } + + AkLogDebug() << "Message ID: " << stringFromMessageId(message.messageId) << std::endl; + + if (this->m_handlers.count(message.messageId)) + this->m_handlers[message.messageId](&message); + + AkLogDebug() << "Writing message." << std::endl; + result = WriteFile(pipe, + &message, + DWORD(sizeof(Message)), + &bytesTransferred, + nullptr); + + if (!result || bytesTransferred != DWORD(sizeof(Message))) { + AkLogError() << "Failed writing to pipe." << std::endl; + auto lastError = GetLastError(); + + if (lastError) + AkLogError() << stringFromError(lastError) << std::endl; + + break; + } } - if (this->m_pipe != INVALID_HANDLE_VALUE) { - CloseHandle(this->m_pipe); - this->m_pipe = INVALID_HANDLE_VALUE; - } - - AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped) + AkLogDebug() << "Closing pipe." << std::endl; + FlushFileBuffers(pipe); + DisconnectNamedPipe(pipe); + CloseHandle(pipe); + pipeThread->finished = true; + AkLogDebug() << "Pipe thread finished." << std::endl; } void AkVCam::MessageServerPrivate::checkLoop() { + AkLogFunction(); + while (this->m_running) { + AkLogDebug() << "Waiting for pipe: " << this->m_pipeName << std::endl; auto result = WaitNamedPipeA(this->m_pipeName.c_str(), NMPWAIT_NOWAIT); if (result @@ -378,62 +490,3 @@ void AkVCam::MessageServerPrivate::checkLoop() this->m_mutex.unlock(); } } - -HRESULT AkVCam::MessageServerPrivate::waitResult(DWORD *bytesTransferred) -{ - auto lastError = GetLastError(); - - if (lastError == ERROR_IO_PENDING) { - if (WaitForSingleObject(this->m_overlapped.hEvent, - INFINITE) == WAIT_OBJECT_0) { - if (!GetOverlappedResult(this->m_pipe, - &this->m_overlapped, - bytesTransferred, - FALSE)) - return S_FALSE; - } else { - CancelIo(this->m_pipe); - - return S_FALSE; - } - } else { - AkLogWarning() << "Wait result failed: " - << errorToString(lastError) - << " (" << lastError << ")" - << std::endl; - - return E_FAIL; - } - - return S_OK; -} - -bool AkVCam::MessageServerPrivate::readMessage(Message *message) -{ - DWORD bytesTransferred = 0; - HRESULT result = S_OK; - - if (!ReadFile(this->m_pipe, - message, - DWORD(sizeof(Message)), - &bytesTransferred, - &this->m_overlapped)) - result = this->waitResult(&bytesTransferred); - - return result == S_OK; -} - -bool AkVCam::MessageServerPrivate::writeMessage(const Message &message) -{ - DWORD bytesTransferred = 0; - HRESULT result = S_OK; - - if (!WriteFile(this->m_pipe, - &message, - DWORD(sizeof(Message)), - &bytesTransferred, - &this->m_overlapped)) - result = this->waitResult(&bytesTransferred); - - return result == S_OK; -} diff --git a/dshow/PlatformUtils/src/preferences.cpp b/dshow/PlatformUtils/src/preferences.cpp index 9e554ba..d7f8626 100644 --- a/dshow/PlatformUtils/src/preferences.cpp +++ b/dshow/PlatformUtils/src/preferences.cpp @@ -30,7 +30,7 @@ #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/logger.h" -#define REG_PREFIX "SOFTWARE\\Webcamoid\\VirtualCamera\\" +#define REG_PREFIX "SOFTWARE\\Webcamoid\\VirtualCamera" namespace AkVCam { @@ -42,128 +42,154 @@ namespace AkVCam bool readValue(const std::string &key, DWORD dataTypeFlags, PVOID data, - LPDWORD dataSize); - void setValue(const std::string &key, + LPDWORD dataSize, + bool global); + bool setValue(const std::string &key, DWORD dataType, LPCSTR data, - DWORD dataSize); + DWORD dataSize, + bool global); } } -void AkVCam::Preferences::write(const std::string &key, - const std::string &value) +bool AkVCam::Preferences::write(const std::string &key, + const std::string &value, + bool global) { AkLogFunction(); AkLogInfo() << "Writing: " << key << " = " << value << std::endl; - setValue(key, REG_SZ, value.c_str(), DWORD(value.size())); + + return setValue(key, REG_SZ, value.c_str(), DWORD(value.size()), global); } -void AkVCam::Preferences::write(const std::string &key, int value) +bool AkVCam::Preferences::write(const std::string &key, int value, bool global) { AkLogFunction(); AkLogInfo() << "Writing: " << key << " = " << value << std::endl; - setValue(key, - REG_DWORD, - reinterpret_cast(&value), - DWORD(sizeof(int))); + + return setValue(key, + REG_DWORD, + reinterpret_cast(&value), + DWORD(sizeof(int)), + global); } -void AkVCam::Preferences::write(const std::string &key, double value) +bool AkVCam::Preferences::write(const std::string &key, + double value, + bool global) { AkLogFunction(); AkLogInfo() << "Writing: " << key << " = " << value << std::endl; auto val = std::to_string(value); - setValue(key, - REG_SZ, - val.c_str(), - DWORD(val.size())); + + return setValue(key, + REG_SZ, + val.c_str(), + DWORD(val.size()), + global); } -void AkVCam::Preferences::write(const std::string &key, - std::vector &value) +bool AkVCam::Preferences::write(const std::string &key, + std::vector &value, + bool global) { AkLogFunction(); - write(key, join(value, ",")); + + return write(key, join(value, ","), global); } std::string AkVCam::Preferences::readString(const std::string &key, - const std::string &defaultValue) + const std::string &defaultValue, + bool global) { AkLogFunction(); char value[MAX_PATH]; memset(value, 0, MAX_PATH * sizeof(char)); DWORD valueSize = MAX_PATH; - if (!readValue(key, RRF_RT_REG_SZ, &value, &valueSize)) + if (!readValue(key, RRF_RT_REG_SZ, &value, &valueSize, global)) return defaultValue; return {value}; } -int AkVCam::Preferences::readInt(const std::string &key, int defaultValue) +int AkVCam::Preferences::readInt(const std::string &key, + int defaultValue, + bool global) { AkLogFunction(); DWORD value = 0; DWORD valueSize = sizeof(DWORD); - if (!readValue(key, RRF_RT_REG_DWORD, &value, &valueSize)) + if (!readValue(key, RRF_RT_REG_DWORD, &value, &valueSize, global)) return defaultValue; return int(value); } double AkVCam::Preferences::readDouble(const std::string &key, - double defaultValue) + double defaultValue, + bool global) { AkLogFunction(); - auto value = readString(key, std::to_string(defaultValue)); + auto value = readString(key, std::to_string(defaultValue), global); std::string::size_type sz; return std::stod(value, &sz); } -bool AkVCam::Preferences::readBool(const std::string &key, bool defaultValue) +bool AkVCam::Preferences::readBool(const std::string &key, + bool defaultValue, + bool global) { AkLogFunction(); - return readInt(key, defaultValue) != 0; + return readInt(key, defaultValue, global) != 0; } -void AkVCam::Preferences::deleteKey(const std::string &key) +bool AkVCam::Preferences::deleteKey(const std::string &key, bool global) { AkLogFunction(); AkLogInfo() << "Deleting " << key << std::endl; + HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER; std::string subKey; std::string val; splitSubKey(key, subKey, val); + bool ok = false; if (val.empty()) { - deleteTree(HKEY_CURRENT_USER, subKey.c_str(), KEY_WOW64_64KEY); + ok = deleteTree(rootKey, + subKey.c_str(), + KEY_WOW64_64KEY) == ERROR_SUCCESS; } else { HKEY hkey = nullptr; - auto result = RegOpenKeyExA(HKEY_CURRENT_USER, + auto result = RegOpenKeyExA(rootKey, subKey.c_str(), 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &hkey); if (result == ERROR_SUCCESS) { - RegDeleteValueA(hkey, val.c_str()); + ok = RegDeleteValueA(hkey, val.c_str()) == ERROR_SUCCESS; RegCloseKey(hkey); } } + + return ok; } -void AkVCam::Preferences::move(const std::string &keyFrom, - const std::string &keyTo) +bool AkVCam::Preferences::move(const std::string &keyFrom, + const std::string &keyTo, + bool global) { AkLogFunction(); AkLogInfo() << "From: " << keyFrom << std::endl; AkLogInfo() << "To: " << keyTo << std::endl; - + HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER; + bool ok = false; std::string subKeyFrom = REG_PREFIX "\\" + keyFrom; HKEY hkeyFrom = nullptr; - auto result = RegOpenKeyExA(HKEY_CURRENT_USER, + auto result = RegOpenKeyExA(rootKey, subKeyFrom.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, @@ -172,7 +198,7 @@ void AkVCam::Preferences::move(const std::string &keyFrom, if (result == ERROR_SUCCESS) { std::string subKeyTo = REG_PREFIX "\\" + keyTo; HKEY hkeyTo = nullptr; - result = RegCreateKeyExA(HKEY_CURRENT_USER, + result = RegCreateKeyExA(rootKey, subKeyTo.c_str(), 0, nullptr, @@ -186,26 +212,34 @@ void AkVCam::Preferences::move(const std::string &keyFrom, result = copyTree(hkeyFrom, nullptr, hkeyTo, KEY_WOW64_64KEY); if (result == ERROR_SUCCESS) - deleteKey(keyFrom); + ok = deleteKey(keyFrom, global); RegCloseKey(hkeyTo); } RegCloseKey(hkeyFrom); } + + return ok; } std::string AkVCam::Preferences::addDevice(const std::string &description) { AkLogFunction(); auto path = createDevicePath(); - int cameraIndex = readInt("Cameras\\size") + 1; - write("Cameras\\size", cameraIndex); - write("Cameras\\" + std::to_string(cameraIndex) + "\\description", - description); - write("Cameras\\" + std::to_string(cameraIndex) + "\\path", path); - return path; + if (path.empty()) + return {}; + + bool ok = true; + int cameraIndex = readInt("Cameras\\size", 0, true) + 1; + ok &= write("Cameras\\size", cameraIndex, true); + ok &= write("Cameras\\" + std::to_string(cameraIndex) + "\\description", + description, + true); + ok &= write("Cameras\\" + std::to_string(cameraIndex) + "\\path", path, true); + + return ok? path: std::string(); } std::string AkVCam::Preferences::addCamera(const std::string &description, @@ -224,20 +258,28 @@ std::string AkVCam::Preferences::addCamera(const std::string &path, return {}; auto path_ = path.empty()? createDevicePath(): path; - int cameraIndex = readInt("Cameras\\") + 1; - write("Cameras\\size", cameraIndex); - write("Cameras\\" - + std::to_string(cameraIndex) - + "\\description", - description); - write("Cameras\\" - + std::to_string(cameraIndex) - + "\\path", - path_); - write("Cameras\\" - + std::to_string(cameraIndex) - + "\\Formats\\size", - int(formats.size())); + + if (path.empty()) + return {}; + + bool ok = true; + int cameraIndex = readInt("Cameras\\", 0, true) + 1; + ok &= write("Cameras\\size", cameraIndex, true); + ok &= write("Cameras\\" + + std::to_string(cameraIndex) + + "\\description", + description, + true); + ok &= write("Cameras\\" + + std::to_string(cameraIndex) + + "\\path", + path_, + true); + ok &= write("Cameras\\" + + std::to_string(cameraIndex) + + "\\Formats\\size", + int(formats.size()), + true); for (size_t i = 0; i < formats.size(); i++) { auto &format = formats[i]; @@ -246,43 +288,49 @@ std::string AkVCam::Preferences::addCamera(const std::string &path, + "\\Formats\\" + std::to_string(i + 1); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); - write(prefix + "\\format", formatStr); - write(prefix + "\\width", format.width()); - write(prefix + "\\height", format.height()); - write(prefix + "\\fps", format.minimumFrameRate().toString()); + ok &= write(prefix + "\\format", formatStr, true); + ok &= write(prefix + "\\width", format.width(), true); + ok &= write(prefix + "\\height", format.height(), true); + ok &= write(prefix + "\\fps", + format.minimumFrameRate().toString(), + true); } - return path_; + return ok? path_: std::string(); } -void AkVCam::Preferences::removeCamera(const std::string &path) +bool AkVCam::Preferences::removeCamera(const std::string &path) { AkLogFunction(); AkLogInfo() << "Device: " << path << std::endl; int cameraIndex = cameraFromPath(path); if (cameraIndex < 0) - return; + return false; - cameraSetFormats(size_t(cameraIndex), {}); + bool ok = true; + ok &= cameraSetFormats(size_t(cameraIndex), {}); auto nCameras = camerasCount(); - deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + '\\'); + ok &= deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + '\\', true); for (auto i = size_t(cameraIndex + 1); i < nCameras; i++) - move("Cameras\\" + std::to_string(i + 1), - "Cameras\\" + std::to_string(i)); + ok &= move("Cameras\\" + std::to_string(i + 1), + "Cameras\\" + std::to_string(i), + true); if (nCameras > 1) - write("Cameras\\size", int(nCameras - 1)); + ok &= write("Cameras\\size", int(nCameras - 1), true); else - deleteKey("Cameras\\"); + ok &= deleteKey("Cameras\\", true); + + return ok; } size_t AkVCam::Preferences::camerasCount() { AkLogFunction(); - int nCameras = readInt("Cameras\\size"); + int nCameras = readInt("Cameras\\size", 0, true); AkLogInfo() << "Cameras: " << nCameras << std::endl; return size_t(nCameras); @@ -359,31 +407,38 @@ std::string AkVCam::Preferences::cameraDescription(size_t cameraIndex) return readString("Cameras\\" + std::to_string(cameraIndex + 1) - + "\\description"); + + "\\description", + {}, + true); } -void AkVCam::Preferences::cameraSetDescription(size_t cameraIndex, +bool AkVCam::Preferences::cameraSetDescription(size_t cameraIndex, const std::string &description) { if (cameraIndex >= camerasCount()) - return; + return false; - write("Cameras\\" + std::to_string(cameraIndex + 1) + "\\description", - description); + return write("Cameras\\" + std::to_string(cameraIndex + 1) + "\\description", + description, + true); } std::string AkVCam::Preferences::cameraPath(size_t cameraIndex) { return readString("Cameras\\" + std::to_string(cameraIndex + 1) - + "\\path"); + + "\\path", + {}, + true); } size_t AkVCam::Preferences::formatsCount(size_t cameraIndex) { return size_t(readInt("Cameras\\" + std::to_string(cameraIndex + 1) - + "\\Formats\\size")); + + "\\Formats\\size", + 0, + true)); } AkVCam::VideoFormat AkVCam::Preferences::cameraFormat(size_t cameraIndex, @@ -394,11 +449,11 @@ AkVCam::VideoFormat AkVCam::Preferences::cameraFormat(size_t cameraIndex, + std::to_string(cameraIndex + 1) + "\\Formats\\" + std::to_string(formatIndex + 1); - auto format = readString(prefix + "\\format"); + auto format = readString(prefix + "\\format", {}, true); auto fourcc = VideoFormat::fourccFromString(format); - int width = readInt(prefix + "\\width"); - int height = readInt(prefix + "\\height"); - auto fps = Fraction(readString(prefix + "\\fps")); + int width = readInt(prefix + "\\width", 0, true); + int height = readInt(prefix + "\\height", 0, true); + auto fps = Fraction(readString(prefix + "\\fps", {}, true)); return VideoFormat(fourcc, width, height, {fps}); } @@ -418,19 +473,22 @@ std::vector AkVCam::Preferences::cameraFormats(size_t camer return formats; } -void AkVCam::Preferences::cameraSetFormats(size_t cameraIndex, +bool AkVCam::Preferences::cameraSetFormats(size_t cameraIndex, const std::vector &formats) { AkLogFunction(); if (cameraIndex >= camerasCount()) - return; + return false; - deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + "\\Formats\\"); - write("Cameras\\" - + std::to_string(cameraIndex + 1) - + "\\Formats\\size", - int(formats.size())); + bool ok = true; + ok &= deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + "\\Formats\\", + true); + ok &= write("Cameras\\" + + std::to_string(cameraIndex + 1) + + "\\Formats\\size", + int(formats.size()), + true); for (size_t i = 0; i < formats.size(); i++) { auto &format = formats[i]; @@ -439,14 +497,16 @@ void AkVCam::Preferences::cameraSetFormats(size_t cameraIndex, + "\\Formats\\" + std::to_string(i + 1); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); - write(prefix + "\\format", formatStr); - write(prefix + "\\width", format.width()); - write(prefix + "\\height", format.height()); - write(prefix + "\\fps", format.minimumFrameRate().toString()); + ok &= write(prefix + "\\format", formatStr, true); + ok &= write(prefix + "\\width", format.width(), true); + ok &= write(prefix + "\\height", format.height(), true); + ok &= write(prefix + "\\fps", format.minimumFrameRate().toString(), true); } + + return ok; } -void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex, +bool AkVCam::Preferences::cameraAddFormat(size_t cameraIndex, const AkVCam::VideoFormat &format, int index) { @@ -456,11 +516,13 @@ void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex, if (index < 0 || index > int(formats.size())) index = int(formats.size()); + bool ok = true; formats.insert(formats.begin() + index, format); - write("Cameras\\" - + std::to_string(cameraIndex + 1) - + "\\Formats\\size", - int(formats.size())); + ok &= write("Cameras\\" + + std::to_string(cameraIndex + 1) + + "\\Formats\\size", + int(formats.size()), + true); for (size_t i = 0; i < formats.size(); i++) { auto &format = formats[i]; @@ -469,26 +531,30 @@ void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex, + "\\Formats\\" + std::to_string(i + 1); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); - write(prefix + "\\format", formatStr); - write(prefix + "\\width", format.width()); - write(prefix + "\\height", format.height()); - write(prefix + "\\fps", format.minimumFrameRate().toString()); + ok &= write(prefix + "\\format", formatStr, true); + ok &= write(prefix + "\\width", format.width(), true); + ok &= write(prefix + "\\height", format.height(), true); + ok &= write(prefix + "\\fps", format.minimumFrameRate().toString(), true); } + + return ok; } -void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index) +bool AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index) { AkLogFunction(); auto formats = cameraFormats(cameraIndex); if (index < 0 || index >= int(formats.size())) - return; + return false; + bool ok = true; formats.erase(formats.begin() + index); - write("Cameras\\" - + std::to_string(cameraIndex + 1) - + "\\Formats\\size", - int(formats.size())); + ok &= write("Cameras\\" + + std::to_string(cameraIndex + 1) + + "\\Formats\\size", + int(formats.size()), + true); for (size_t i = 0; i < formats.size(); i++) { auto &format = formats[i]; @@ -497,11 +563,15 @@ void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index) + "\\Formats\\" + std::to_string(i + 1); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); - write(prefix + "\\format", formatStr); - write(prefix + "\\width", format.width()); - write(prefix + "\\height", format.height()); - write(prefix + "\\fps", format.minimumFrameRate().toString()); + ok &= write(prefix + "\\format", formatStr, true); + ok &= write(prefix + "\\width", format.width(), true); + ok &= write(prefix + "\\height", format.height(), true); + ok &= write(prefix + "\\fps", + format.minimumFrameRate().toString(), + true); } + + return ok; } int AkVCam::Preferences::cameraControlValue(size_t cameraIndex, @@ -513,15 +583,15 @@ int AkVCam::Preferences::cameraControlValue(size_t cameraIndex, + key); } -void AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex, +bool AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex, const std::string &key, int value) { - write("Cameras\\" - + std::to_string(cameraIndex + 1) - + "\\Controls\\" - + key, - value); + return write("Cameras\\" + + std::to_string(cameraIndex + 1) + + "\\Controls\\" + + key, + value); } std::string AkVCam::Preferences::picture() @@ -529,19 +599,19 @@ std::string AkVCam::Preferences::picture() return readString("picture"); } -void AkVCam::Preferences::setPicture(const std::string &picture) +bool AkVCam::Preferences::setPicture(const std::string &picture) { - write("picture", picture); + return write("picture", picture); } int AkVCam::Preferences::logLevel() { - return readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT); + return readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT, true); } -void AkVCam::Preferences::setLogLevel(int logLevel) +bool AkVCam::Preferences::setLogLevel(int logLevel) { - write("loglevel", logLevel); + return write("loglevel", logLevel, true); } void AkVCam::Preferences::splitSubKey(const std::string &key, @@ -564,16 +634,18 @@ void AkVCam::Preferences::splitSubKey(const std::string &key, bool AkVCam::Preferences::readValue(const std::string &key, DWORD dataTypeFlags, PVOID data, - LPDWORD dataSize) + LPDWORD dataSize, + bool global) { AkLogFunction(); + HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER; std::string subKey; std::string val; splitSubKey(key, subKey, val); AkLogDebug() << "SubKey: " << subKey << std::endl; AkLogDebug() << "Value: " << val << std::endl; HKEY hkey = nullptr; - auto result = RegOpenKeyExA(HKEY_CURRENT_USER, + auto result = RegOpenKeyExA(rootKey, subKey.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, @@ -594,19 +666,21 @@ bool AkVCam::Preferences::readValue(const std::string &key, return result == ERROR_SUCCESS; } -void AkVCam::Preferences::setValue(const std::string &key, +bool AkVCam::Preferences::setValue(const std::string &key, DWORD dataType, LPCSTR data, - DWORD dataSize) + DWORD dataSize, + bool global) { AkLogFunction(); + HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER; std::string subKey; std::string val; splitSubKey(key, subKey, val); AkLogDebug() << "SubKey: " << subKey << std::endl; AkLogDebug() << "Value: " << val << std::endl; HKEY hkey = nullptr; - LONG result = RegCreateKeyExA(HKEY_CURRENT_USER, + LONG result = RegCreateKeyExA(rootKey, subKey.c_str(), 0, nullptr, @@ -617,13 +691,15 @@ void AkVCam::Preferences::setValue(const std::string &key, nullptr); if (result != ERROR_SUCCESS) - return; + return false; - RegSetValueExA(hkey, - val.c_str(), - 0, - dataType, - reinterpret_cast(data), - dataSize); + result = RegSetValueExA(hkey, + val.c_str(), + 0, + dataType, + reinterpret_cast(data), + dataSize); RegCloseKey(hkey); + + return result == ERROR_SUCCESS; } diff --git a/dshow/PlatformUtils/src/preferences.h b/dshow/PlatformUtils/src/preferences.h index 124c302..55bb82c 100644 --- a/dshow/PlatformUtils/src/preferences.h +++ b/dshow/PlatformUtils/src/preferences.h @@ -31,51 +31,64 @@ namespace AkVCam namespace Preferences { - void write(const std::string &key, const std::string &value); - void write(const std::string &key, int value); - void write(const std::string &key, double value); - void write(const std::string &key, std::vector &value); + bool write(const std::string &key, + const std::string &value, + bool global=false); + bool write(const std::string &key, int value, bool global=false); + bool write(const std::string &key, double value, bool global=false); + bool write(const std::string &key, + std::vector &value, + bool global=false); std::string readString(const std::string &key, - const std::string &defaultValue={}); - int readInt(const std::string &key, int defaultValue=0); - double readDouble(const std::string &key, double defaultValue=0.0); - bool readBool(const std::string &key, bool defaultValue=false); - void deleteKey(const std::string &key); - void move(const std::string &keyFrom, const std::string &keyTo); + const std::string &defaultValue={}, + bool global=false); + int readInt(const std::string &key, + int defaultValue=0, + bool global=false); + double readDouble(const std::string &key, + double defaultValue=0.0, + bool global=false); + bool readBool(const std::string &key, + bool defaultValue=false, + bool global=false); + bool deleteKey(const std::string &key, bool global=false); + bool move(const std::string &keyFrom, + const std::string &keyTo, + bool global=false); std::string addDevice(const std::string &description); std::string addCamera(const std::string &description, const std::vector &formats); std::string addCamera(const std::string &path, const std::string &description, const std::vector &formats); - void removeCamera(const std::string &path); + bool removeCamera(const std::string &path); size_t camerasCount(); std::string createDevicePath(); int cameraFromCLSID(const CLSID &clsid); int cameraFromPath(const std::string &path); bool cameraExists(const std::string &path); std::string cameraDescription(size_t cameraIndex); - void cameraSetDescription(size_t cameraIndex, + bool cameraSetDescription(size_t cameraIndex, const std::string &description); std::string cameraPath(size_t cameraIndex); size_t formatsCount(size_t cameraIndex); VideoFormat cameraFormat(size_t cameraIndex, size_t formatIndex); std::vector cameraFormats(size_t cameraIndex); - void cameraSetFormats(size_t cameraIndex, + bool cameraSetFormats(size_t cameraIndex, const std::vector &formats); - void cameraAddFormat(size_t cameraIndex, + bool cameraAddFormat(size_t cameraIndex, const VideoFormat &format, int index); - void cameraRemoveFormat(size_t cameraIndex, int index); + bool cameraRemoveFormat(size_t cameraIndex, int index); int cameraControlValue(size_t cameraIndex, const std::string &key); - void cameraSetControlValue(size_t cameraIndex, + bool cameraSetControlValue(size_t cameraIndex, const std::string &key, int value); std::string picture(); - void setPicture(const std::string &picture); + bool setPicture(const std::string &picture); int logLevel(); - void setLogLevel(int logLevel); + bool setLogLevel(int logLevel); } } diff --git a/dshow/PlatformUtils/src/sharedmemory.cpp b/dshow/PlatformUtils/src/sharedmemory.cpp index 96fb7b9..2013bde 100644 --- a/dshow/PlatformUtils/src/sharedmemory.cpp +++ b/dshow/PlatformUtils/src/sharedmemory.cpp @@ -130,7 +130,7 @@ bool AkVCam::SharedMemory::open(size_t pageSize, OpenMode mode) AkLogError() << "Error opening shared memory (" << this->d->m_name << "): " - << errorToString(GetLastError()) + << stringFromError(GetLastError()) << " (" << GetLastError() << ")" << std::endl; diff --git a/dshow/PlatformUtils/src/utils.cpp b/dshow/PlatformUtils/src/utils.cpp index 868dfae..443493c 100644 --- a/dshow/PlatformUtils/src/utils.cpp +++ b/dshow/PlatformUtils/src/utils.cpp @@ -27,6 +27,7 @@ #include #include "utils.h" +#include "messagecommons.h" #include "VCamUtils/src/utils.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoframe.h" @@ -102,7 +103,16 @@ bool AkVCam::fileExists(const std::string &path) return GetFileAttributesA(path.c_str()) & FILE_ATTRIBUTE_ARCHIVE; } -std::string AkVCam::errorToString(DWORD errorCode) +std::string AkVCam::realPath(const std::string &path) +{ + char rpath[MAX_PATH]; + memset(rpath, 0, MAX_PATH); + GetFullPathNameA(path.c_str(), MAX_PATH, rpath, nullptr); + + return std::string(rpath); +} + +std::string AkVCam::stringFromError(DWORD errorCode) { CHAR *errorStr = nullptr; auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER @@ -170,6 +180,32 @@ std::string AkVCam::createClsidStrFromStr(const std::string &str) return stringFromIid(createClsidFromStr(str)); } +std::string AkVCam::stringFromMessageId(uint32_t messageId) +{ + static const std::map clsidToString { + {AKVCAM_ASSISTANT_MSG_ISALIVE , "ISALIVE" }, + {AKVCAM_ASSISTANT_MSG_FRAME_READY , "FRAME_READY" }, + {AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED , "PICTURE_UPDATED" }, + {AKVCAM_ASSISTANT_MSG_REQUEST_PORT , "REQUEST_PORT" }, + {AKVCAM_ASSISTANT_MSG_ADD_PORT , "ADD_PORT" }, + {AKVCAM_ASSISTANT_MSG_REMOVE_PORT , "REMOVE_PORT" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , "DEVICE_UPDATE" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS , "DEVICE_LISTENERS" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER , "DEVICE_LISTENER" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , "DEVICE_LISTENER_ADD" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , "DEVICE_LISTENER_REMOVE" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , "DEVICE_BROADCASTING" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , "DEVICE_SETBROADCASTING" }, + {AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, "DEVICE_CONTROLS_UPDATED"}, + }; + + for (auto &id: clsidToString) + if (id.first == messageId) + return id.second; + + return "AKVCAM_ASSISTANT_MSG_(" + std::to_string(messageId) + ")"; +} + std::string AkVCam::stringFromIid(const IID &iid) { LPWSTR strIID = nullptr; diff --git a/dshow/PlatformUtils/src/utils.h b/dshow/PlatformUtils/src/utils.h index 1a7381a..fce3042 100644 --- a/dshow/PlatformUtils/src/utils.h +++ b/dshow/PlatformUtils/src/utils.h @@ -39,9 +39,11 @@ namespace AkVCam std::string moduleFileName(HINSTANCE hinstDLL); std::string dirname(const std::string &path); bool fileExists(const std::string &path); - std::string errorToString(DWORD errorCode); + std::string realPath(const std::string &path); + std::string stringFromError(DWORD errorCode); CLSID createClsidFromStr(const std::string &str); std::string createClsidStrFromStr(const std::string &str); + std::string stringFromMessageId(uint32_t messageId); std::string stringFromIid(const IID &iid); std::string stringFromResult(HRESULT result); std::string stringFromClsid(const CLSID &clsid); diff --git a/dshow/VCamIPC/src/ipcbridge.cpp b/dshow/VCamIPC/src/ipcbridge.cpp index 001a28c..478afb0 100644 --- a/dshow/VCamIPC/src/ipcbridge.cpp +++ b/dshow/VCamIPC/src/ipcbridge.cpp @@ -66,6 +66,7 @@ namespace AkVCam ~IpcBridgePrivate(); inline const std::vector &controls() const; + void updateDevices(bool propagate); void updateDeviceSharedProperties(); void updateDeviceSharedProperties(const std::string &deviceId, const std::string &owner); @@ -74,13 +75,21 @@ namespace AkVCam // Message handling methods void isAlive(Message *message); + void deviceUpdate(Message *message); void frameReady(Message *message); void pictureUpdated(Message *message); - void deviceUpdate(Message *message); - void listenerAdd(Message *message); - void listenerRemove (Message *message); void setBroadcasting(Message *message); void controlsUpdated(Message *message); + void listenerAdd(Message *message); + void listenerRemove (Message *message); + + bool isRoot() const; + int sudo(const std::vector ¶meters, + const std::string &directory={}, + bool show=false); + std::string manager() const; + std::string alternativeManager() const; + std::string alternativePlugin() const; }; static const int maxFrameWidth = 1920; @@ -97,12 +106,17 @@ AkVCam::IpcBridge::IpcBridge() AkVCam::Logger::setLogLevel(loglevel); this->d->m_mainServer.start(); this->registerPeer(); + this->d->updateDeviceSharedProperties(); + this->d->m_mainServer.connectPipeStateChanged(this, + &IpcBridgePrivate::pipeStateChanged); } AkVCam::IpcBridge::~IpcBridge() { + this->d->m_mainServer.disconnectPipeStateChanged(this, + &IpcBridgePrivate::pipeStateChanged); this->unregisterPeer(); - this->d->m_mainServer.stop(true); + this->d->m_mainServer.stop(); delete this->d; } @@ -113,6 +127,7 @@ std::string AkVCam::IpcBridge::picture() const void AkVCam::IpcBridge::setPicture(const std::string &picture) { + AkLogFunction(); Preferences::setPicture(picture); Message message; message.messageId = AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED; @@ -131,6 +146,7 @@ int AkVCam::IpcBridge::logLevel() const void AkVCam::IpcBridge::setLogLevel(int logLevel) { + AkLogFunction(); Preferences::setLogLevel(logLevel); Logger::setLogLevel(logLevel); } @@ -139,6 +155,11 @@ bool AkVCam::IpcBridge::registerPeer() { AkLogFunction(); + if (!this->d->m_portName.empty()) + return true; + + AkLogDebug() << "Requesting port." << std::endl; + Message message; message.messageId = AKVCAM_ASSISTANT_MSG_REQUEST_PORT; message.dataSize = sizeof(MsgRequestPort); @@ -149,10 +170,18 @@ bool AkVCam::IpcBridge::registerPeer() return false; std::string portName(requestData->port); + AkLogInfo() << "Recommended port name: " << portName << std::endl; + + if (portName.empty()) { + AkLogError() << "The returned por name is empty." << std::endl; + + return false; + } + + AkLogDebug() << "Starting message server." << std::endl; auto pipeName = "\\\\.\\pipe\\" + portName; this->d->m_messageServer.setPipeName(pipeName); this->d->m_messageServer.setHandlers(this->d->m_messageHandlers); - AkLogInfo() << "Recommended port name: " << portName << std::endl; if (!this->d->m_messageServer.start()) { AkLogError() << "Can't start message server" << std::endl; @@ -160,6 +189,8 @@ bool AkVCam::IpcBridge::registerPeer() return false; } + AkLogInfo() << "Registering port: " << portName << std::endl; + message.clear(); message.messageId = AKVCAM_ASSISTANT_MSG_ADD_PORT; message.dataSize = sizeof(MsgAddPort); @@ -171,17 +202,17 @@ bool AkVCam::IpcBridge::registerPeer() pipeName.c_str(), (std::min)(pipeName.size(), MAX_STRING)); - AkLogInfo() << "Registering port name: " << portName << std::endl; - if (!MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME, &message)) { - this->d->m_messageServer.stop(true); + AkLogError() << "Failed registering port." << std::endl; + this->d->m_messageServer.stop(); return false; } if (!addData->status) { - this->d->m_messageServer.stop(true); + AkLogError() << "Failed registering port." << std::endl; + this->d->m_messageServer.stop(); return false; } @@ -191,6 +222,9 @@ bool AkVCam::IpcBridge::registerPeer() this->d->m_portName = portName; AkLogInfo() << "Peer registered as " << portName << std::endl; + AkLogInfo() << "SUCCESSFUL" << std::endl; + this->d->updateDevices(false); + return true; } @@ -201,7 +235,6 @@ void AkVCam::IpcBridge::unregisterPeer() if (this->d->m_portName.empty()) return; - this->d->m_sharedMemory.setName({}); Message message; message.messageId = AKVCAM_ASSISTANT_MSG_REMOVE_PORT; message.dataSize = sizeof(MsgRemovePort); @@ -211,15 +244,17 @@ void AkVCam::IpcBridge::unregisterPeer() (std::min)(this->d->m_portName.size(), MAX_STRING)); MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME, &message); - this->d->m_messageServer.stop(true); + this->d->m_messageServer.stop(); + this->d->m_sharedMemory.setName({}); + this->d->m_globalMutex = {}; this->d->m_portName.clear(); } std::vector AkVCam::IpcBridge::devices() const { AkLogFunction(); - auto nCameras = Preferences::camerasCount(); std::vector devices; + auto nCameras = Preferences::camerasCount(); AkLogInfo() << "Devices:" << std::endl; for (size_t i = 0; i < nCameras; i++) { @@ -285,9 +320,11 @@ std::vector AkVCam::IpcBridge::formats(const std::string &d return Preferences::cameraFormats(size_t(cameraIndex)); } + void AkVCam::IpcBridge::setFormats(const std::string &deviceId, const std::vector &formats) { + AkLogFunction(); auto cameraIndex = Preferences::cameraFromPath(deviceId); if (cameraIndex >= 0) @@ -425,10 +462,22 @@ std::vector AkVCam::IpcBridge::clientsPids() const if (pluginPath.empty()) return {}; - auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll"; + std::vector plugins; + + // First check for the existence of the main plugin binary. + auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll"; AkLogDebug() << "Plugin binary: " << path << std::endl; - if (!fileExists(path)) + if (fileExists(path)) + plugins.push_back(path); + + // Check if the alternative architecture plugin exists. + auto altPlugin = this->d->alternativePlugin(); + + if (!altPlugin.empty()) + plugins.push_back(path); + + if (plugins.empty()) return {}; std::vector pids; @@ -449,8 +498,18 @@ std::vector AkVCam::IpcBridge::clientsPids() const | PROCESS_VM_READ, FALSE, process[i]); - if (!processHnd) - continue; + + if (!processHnd) + continue; + + char processName[MAX_PATH]; + memset(&processName, 0, sizeof(MAX_PATH)); + + if (GetModuleBaseNameA(processHnd, + nullptr, + processName, + MAX_PATH)) + AkLogDebug() << "Enumerating modules for '" << processName << "'" << std::endl; HMODULE modules[nElements]; memset(modules, 0, nElements * sizeof(HMODULE)); @@ -470,7 +529,11 @@ std::vector AkVCam::IpcBridge::clientsPids() const modules[j], moduleName, MAX_PATH)) { - if (moduleName == path) { + auto it = std::find(plugins.begin(), + plugins.end(), + moduleName); + + if (it != plugins.end()) { auto pidsIt = std::find(pids.begin(), pids.end(), process[i]); @@ -513,11 +576,15 @@ std::string AkVCam::IpcBridge::clientExe(uint64_t pid) const std::string AkVCam::IpcBridge::addDevice(const std::string &description) { + AkLogFunction(); + return Preferences::addDevice(description); } void AkVCam::IpcBridge::removeDevice(const std::string &deviceId) { + AkLogFunction(); + Preferences::removeCamera(deviceId); } @@ -567,12 +634,24 @@ void AkVCam::IpcBridge::updateDevices() RegisterServerFunc(GetProcAddress(hmodule, "DllRegisterServer")); if (registerServer) { - (*registerServer)(); + AkLogDebug() << "Registering server" << std::endl; + auto result = (*registerServer)(); + AkLogDebug() << "Server registered with code " << result << std::endl; + this->d->updateDevices(true); + auto lockFileName = tempPath() + "\\akvcam_update.lck"; - Message message; - message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE; - message.dataSize = 0; - this->d->m_mainServer.sendMessage(&message); + if (!fileExists(lockFileName)) { + std::ofstream lockFile; + lockFile.open(lockFileName); + lockFile << std::endl; + lockFile.close(); + auto altManager = this->d->alternativeManager(); + + if (!altManager.empty()) + this->d->sudo({altManager, "update"}); + + DeleteFileA(lockFileName.c_str()); + } } else { AkLogError() << "Can't locate DllRegisterServer function." << std::endl; } @@ -626,9 +705,6 @@ bool AkVCam::IpcBridge::deviceStart(const std::string &deviceId, return false; } - if (!data->status) - return false; - this->d->m_broadcasting.push_back(deviceId); return true; @@ -746,15 +822,42 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId) return data->status; } +bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const +{ + static const std::vector operations { + "add-device", + "remove-device", + "remove-devices", + "set-description", + "add-format", + "remove-format", + "remove-formats", + "update", + "load", + "set-loglevel", + }; + + auto it = std::find(operations.begin(), operations.end(), operation); + + return it != operations.end() && !this->d->isRoot(); +} + +int AkVCam::IpcBridge::sudo(int argc, char **argv) const +{ + AkLogFunction(); + std::vector arguments; + + for (int i = 0; i < argc; i++) + arguments.push_back(argv[i]); + + return this->d->sudo(arguments); +} + AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self): self(self) { this->m_mainServer.setPipeName("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME); this->m_mainServer.setMode(MessageServer::ServerModeSend); - this->m_mainServer.connectPipeStateChanged(this, - &IpcBridgePrivate::pipeStateChanged); - this->updateDeviceSharedProperties(); - this->m_messageHandlers = std::map { {AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) }, {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) }, @@ -769,7 +872,6 @@ AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self): AkVCam::IpcBridgePrivate::~IpcBridgePrivate() { - this->m_mainServer.stop(true); } const std::vector &AkVCam::IpcBridgePrivate::controls() const @@ -797,8 +899,22 @@ const std::vector &AkVCam::IpcBridgePrivate::controls() c return controls; } +void AkVCam::IpcBridgePrivate::updateDevices(bool propagate) +{ + AkLogFunction(); + + Message message; + message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE; + message.dataSize = sizeof(MsgDevicesUpdated); + auto data = messageData(&message); + data->propagate = propagate; + this->m_mainServer.sendMessage(&message); +} + void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties() { + AkLogFunction(); + for (size_t i = 0; i < Preferences::camerasCount(); i++) { auto path = Preferences::cameraPath(i); Message message; @@ -817,6 +933,8 @@ void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties() void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties(const std::string &deviceId, const std::string &owner) { + AkLogFunction(); + if (owner.empty()) { this->m_devices[deviceId] = {SharedMemory(), Mutex()}; } else { @@ -865,6 +983,19 @@ void AkVCam::IpcBridgePrivate::isAlive(Message *message) data->alive = true; } +void AkVCam::IpcBridgePrivate::deviceUpdate(Message *message) +{ + UNUSED(message); + AkLogFunction(); + std::vector devices; + auto nCameras = Preferences::camerasCount(); + + for (size_t i = 0; i < nCameras; i++) + devices.push_back(Preferences::cameraPath(i)); + + AKVCAM_EMIT(this->self, DevicesChanged, devices) +} + void AkVCam::IpcBridgePrivate::frameReady(Message *message) { AkLogFunction(); @@ -899,39 +1030,6 @@ void AkVCam::IpcBridgePrivate::pictureUpdated(Message *message) AKVCAM_EMIT(this->self, PictureChanged, std::string(data->picture)) } -void AkVCam::IpcBridgePrivate::deviceUpdate(Message *message) -{ - UNUSED(message); - AkLogFunction(); - std::vector devices; - auto nCameras = Preferences::camerasCount(); - - for (size_t i = 0; i < nCameras; i++) - devices.push_back(Preferences::cameraPath(i)); - - AKVCAM_EMIT(this->self, DevicesChanged, devices) -} - -void AkVCam::IpcBridgePrivate::listenerAdd(Message *message) -{ - AkLogFunction(); - auto data = messageData(message); - AKVCAM_EMIT(this->self, - ListenerAdded, - std::string(data->device), - std::string(data->listener)) -} - -void AkVCam::IpcBridgePrivate::listenerRemove(Message *message) -{ - AkLogFunction(); - auto data = messageData(message); - AKVCAM_EMIT(this->self, - ListenerRemoved, - std::string(data->device), - std::string(data->listener)) -} - void AkVCam::IpcBridgePrivate::setBroadcasting(Message *message) { AkLogFunction(); @@ -954,9 +1052,181 @@ void AkVCam::IpcBridgePrivate::controlsUpdated(Message *message) std::map controls; - for (auto &control: this->controls()) + for (auto &control: this->controls()) { controls[control.id] = Preferences::cameraControlValue(size_t(cameraIndex), control.id); + AkLogDebug() << control.id << ": " << controls[control.id] << std::endl; + } AKVCAM_EMIT(this->self, ControlsChanged, deviceId, controls) } + +void AkVCam::IpcBridgePrivate::listenerAdd(Message *message) +{ + AkLogFunction(); + auto data = messageData(message); + AKVCAM_EMIT(this->self, + ListenerAdded, + std::string(data->device), + std::string(data->listener)) +} + +void AkVCam::IpcBridgePrivate::listenerRemove(Message *message) +{ + AkLogFunction(); + auto data = messageData(message); + AKVCAM_EMIT(this->self, + ListenerRemoved, + std::string(data->device), + std::string(data->listener)) +} + +bool AkVCam::IpcBridgePrivate::isRoot() const +{ + AkLogFunction(); + HANDLE token = nullptr; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) + return false; + + TOKEN_ELEVATION elevationInfo; + memset(&elevationInfo, 0, sizeof(TOKEN_ELEVATION)); + DWORD len = 0; + + if (!GetTokenInformation(token, + TokenElevation, + &elevationInfo, + sizeof(TOKEN_ELEVATION), + &len)) { + CloseHandle(token); + + return false; + } + + CloseHandle(token); + + return elevationInfo.TokenIsElevated; +} + +int AkVCam::IpcBridgePrivate::sudo(const std::vector ¶meters, + const std::string &directory, + bool show) +{ + AkLogFunction(); + + if (parameters.size() < 1) + return E_FAIL; + + auto command = parameters[0]; + std::string params; + size_t i = 0; + + for (auto ¶m: parameters) { + if (i < 1) { + i++; + + continue; + } + + if (!params.empty()) + params += ' '; + + auto param_ = replace(param, "\"", "\"\"\""); + + if (param_.find(" ") == std::string::npos) + params += param_; + else + params += "\"" + param_ + "\""; + } + + AkLogDebug() << "Command: " << command << std::endl; + AkLogDebug() << "Arguments: " << params << std::endl; + + SHELLEXECUTEINFOA execInfo; + memset(&execInfo, 0, sizeof(SHELLEXECUTEINFO)); + execInfo.cbSize = sizeof(SHELLEXECUTEINFO); + execInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + execInfo.hwnd = nullptr; + execInfo.lpVerb = "runas"; + execInfo.lpFile = command.data(); + execInfo.lpParameters = params.data(); + execInfo.lpDirectory = directory.data(); + execInfo.nShow = show? SW_SHOWNORMAL: SW_HIDE; + execInfo.hInstApp = nullptr; + ShellExecuteExA(&execInfo); + + if (!execInfo.hProcess) { + AkLogError() << "Failed executing command" << std::endl; + + return E_FAIL; + } + + WaitForSingleObject(execInfo.hProcess, INFINITE); + + DWORD exitCode; + GetExitCodeProcess(execInfo.hProcess, &exitCode); + CloseHandle(execInfo.hProcess); + + if (FAILED(exitCode)) + AkLogError() << "Command failed with code " + << exitCode + << " (" + << stringFromError(exitCode) + << ")" + << std::endl; + + AkLogError() << "Command exited with code " << exitCode << std::endl; + + return int(exitCode); +} + +std::string AkVCam::IpcBridgePrivate::manager() const +{ + AkLogFunction(); + auto pluginPath = locatePluginPath(); + auto path = realPath(pluginPath + "\\AkVCamManager.dll"); + + return fileExists(path)? path: std::string(); +} + +std::string AkVCam::IpcBridgePrivate::alternativeManager() const +{ + AkLogFunction(); + auto pluginPath = locatePluginPath(); + +#ifdef _WIN64 + auto path = realPath(pluginPath + "\\..\\x86\\AkVCamManager.dll"); +#else + SYSTEM_INFO info; + memset(&info, 0, sizeof(SYSTEM_INFO)); + GetNativeSystemInfo(&info); + + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + return {}; + + auto path = realPath(pluginPath + "\\..\\x64\\AkVCamManager.dll"); +#endif + + return fileExists(path)? path: std::string(); +} + +std::string AkVCam::IpcBridgePrivate::alternativePlugin() const +{ + AkLogFunction(); + auto pluginPath = locatePluginPath(); + +#ifdef _WIN64 + auto path = realPath(pluginPath + "\\..\\x86\\" DSHOW_PLUGIN_NAME ".dll"); +#else + SYSTEM_INFO info; + memset(&info, 0, sizeof(SYSTEM_INFO)); + GetNativeSystemInfo(&info); + + if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + return {}; + + auto path = realPath(pluginPath + "\\..\\x64\\" DSHOW_PLUGIN_NAME ".dll"); +#endif + + return fileExists(path)? path: std::string(); +} diff --git a/dshow/VirtualCamera/src/basefilter.cpp b/dshow/VirtualCamera/src/basefilter.cpp index 5b41c1f..20d726f 100644 --- a/dshow/VirtualCamera/src/basefilter.cpp +++ b/dshow/VirtualCamera/src/basefilter.cpp @@ -150,6 +150,28 @@ IReferenceClock *AkVCam::BaseFilter::referenceClock() const return this->d->m_referenceClock; } +std::string AkVCam::BaseFilter::deviceId() +{ + CLSID clsid; + this->GetClassID(&clsid); + auto cameraIndex = Preferences::cameraFromCLSID(clsid); + + if (cameraIndex < 0) + return {}; + + return Preferences::cameraPath(size_t(cameraIndex)); +} + +std::string AkVCam::BaseFilter::broadcaster() +{ + auto deviceId = this->deviceId(); + + if (deviceId.empty()) + return {}; + + return this->d->m_ipcBridge.broadcaster(deviceId); +} + HRESULT AkVCam::BaseFilter::QueryInterface(const IID &riid, void **ppvObject) { AkLogFunction(); diff --git a/dshow/VirtualCamera/src/basefilter.h b/dshow/VirtualCamera/src/basefilter.h index 1ac8f69..d53207e 100644 --- a/dshow/VirtualCamera/src/basefilter.h +++ b/dshow/VirtualCamera/src/basefilter.h @@ -47,6 +47,8 @@ namespace AkVCam static BaseFilter *create(const GUID &clsid); IFilterGraph *filterGraph() const; IReferenceClock *referenceClock() const; + std::string deviceId(); + std::string broadcaster(); DECLARE_IMEDIAFILTER_NQ diff --git a/dshow/VirtualCamera/src/pin.cpp b/dshow/VirtualCamera/src/pin.cpp index ccc3eeb..7640061 100644 --- a/dshow/VirtualCamera/src/pin.cpp +++ b/dshow/VirtualCamera/src/pin.cpp @@ -111,6 +111,20 @@ AkVCam::Pin::Pin(BaseFilter *baseFilter, this->d->m_pinId = ss.str(); this->d->m_mediaTypes = new AkVCam::EnumMediaTypes(formats); this->d->m_mediaTypes->AddRef(); + + auto cameraIndex = Preferences::cameraFromPath(baseFilter->deviceId()); + this->d->m_broadcaster = baseFilter->broadcaster(); + this->d->m_controls["hflip"] = + Preferences::cameraControlValue(cameraIndex, "hflip"); + this->d->m_controls["vflip"] = + Preferences::cameraControlValue(cameraIndex, "vflip"); + this->d->m_controls["scaling"] = + Preferences::cameraControlValue(cameraIndex, "scaling"); + this->d->m_controls["aspect_ratio"] = + Preferences::cameraControlValue(cameraIndex, "aspect_ratio"); + this->d->m_controls["swap_rgb"] = + Preferences::cameraControlValue(cameraIndex, "swap_rgb"); + auto picture = Preferences::picture(); if (!picture.empty()) @@ -316,9 +330,18 @@ void AkVCam::Pin::setControls(const std::map &controls) return; } + for (auto &control: controls) + AkLogDebug() << control.first << ": " << control.second << std::endl; + this->d->m_controls = controls; this->d->m_controlsMutex.unlock(); this->d->updateTestFrame(); + this->d->m_mutex.lock(); + + if (this->d->m_broadcaster.empty()) + this->d->m_currentFrame = this->d->m_testFrameAdapted; + + this->d->m_mutex.unlock(); } bool AkVCam::Pin::horizontalFlip() const @@ -907,6 +930,7 @@ HRESULT AkVCam::PinPrivate::sendFrame() void AkVCam::PinPrivate::updateTestFrame() { + AkLogFunction(); auto frame = this->applyAdjusts(this->m_testFrame); if (frame.format().size() < 1) diff --git a/dshow/dshow.pri b/dshow/dshow.pri index c5facc1..870580e 100644 --- a/dshow/dshow.pri +++ b/dshow/dshow.pri @@ -25,7 +25,7 @@ isEmpty(DSHOW_PLUGIN_ASSISTANT_DESCRIPTION): isEmpty(DSHOW_PLUGIN_DESCRIPTION): DSHOW_PLUGIN_DESCRIPTION = "Webcamoid Virtual Camera" isEmpty(DSHOW_PLUGIN_DESCRIPTION_EXT): - DSHOW_PLUGIN_DESCRIPTION_EXT = "Central service for communicating between virtual cameras clients and servers" + DSHOW_PLUGIN_DESCRIPTION_EXT = "Central service for communicating between virtual cameras and clients" isEmpty(DSHOW_PLUGIN_DEVICE_PREFIX): DSHOW_PLUGIN_DEVICE_PREFIX = AkVCamVideoDevice isEmpty(DSHOW_PLUGIN_VENDOR): diff --git a/ports/deploy/installscript.mac.qs b/ports/deploy/installscript.mac.qs index 7150acf..2892a59 100644 --- a/ports/deploy/installscript.mac.qs +++ b/ports/deploy/installscript.mac.qs @@ -56,14 +56,15 @@ Component.prototype.createOperations = function() "/Library/CoreMediaIO/Plug-Ins/DAL/@Name@.plugin"); // Set assistant daemon. - let daemonPlist = "/Library/LaunchAgents/org.webcamoid.cmio.AkVCam.Assistant.plist" + let service = "org.webcamoid.cmio.AkVCam.Assistant" + let daemonPlist = "/Library/LaunchDaemons/" + service + ".plist" let plistContents = "\n" + "\n" + "\n" + " \n" + " Label\n" - + " org.webcamoid.cmio.AkVCam.Assistant\n" + + " " + service + "\n" + " ProgramArguments\n" + " \n" + " " @@ -75,7 +76,7 @@ Component.prototype.createOperations = function() + " \n" + " MachServices\n" + " \n" - + " org.webcamoid.cmio.AkVCam.Assistant\n" + + " " + service + "\n" + " \n" + " \n" + " StandardOutPath\n" @@ -88,10 +89,14 @@ Component.prototype.createOperations = function() component.addElevatedOperation("AppendFile", daemonPlist, plistContents); // Load assistant daemon. + if (installer.isUninstaller()) + component.addElevatedOperation("Execute", + "launchctl", "enable", "system/" + service); + component.addElevatedOperation("Execute", - "launchctl", "load", "-w", daemonPlist, + "launchctl", "bootstrap", "system", daemonPlist, "UNDOEXECUTE", - "launchctl", "unload", "-w", daemonPlist); + "launchctl", "bootout", "system", daemonPlist); if (installer.isUninstaller()) component.addElevatedOperation("Delete", daemonPlist); diff --git a/ports/deploy/installscript.windows.qs b/ports/deploy/installscript.windows.qs index 81a3f1d..8a11b05 100644 --- a/ports/deploy/installscript.windows.qs +++ b/ports/deploy/installscript.windows.qs @@ -20,10 +20,12 @@ Component.prototype.createOperations = function() + "/" + archs[i] + "/AkVCamManager.exe"; - component.addOperation("Execute", - managerPath, "remove-devices"); + component.addElevateOperation("Execute", + managerPath, + "remove-devices"); component.addElevatedOperation("Execute", - managerPath, "update"); + managerPath, + "update"); } let assistantPath =