Added sub-command for showing default streaming format.

Added sub-command for dumping all posssible information about the
devices and configurations in XML format.
Added sub-command for listening global events.
Added sub-command for showing virtual camera version.
Now the manager recognises negative numbers as arguments instead as an
option.
The manager can execute sub-commands as root when required.
Fixed installer.
The virtual camera is working fine in both Mac and Windows.
This commit is contained in:
Gonzalo Exequiel Pedone 2021-02-06 01:45:40 -03:00
parent f2ca25c5c8
commit 09e3b6ad3a
No known key found for this signature in database
GPG key ID: B8B09E63E9B85BAF
31 changed files with 1658 additions and 712 deletions

View file

@ -18,12 +18,20 @@
*/ */
#include <algorithm> #include <algorithm>
#include <chrono>
#include <codecvt> #include <codecvt>
#include <csignal> #include <csignal>
#include <cstring>
#include <iostream> #include <iostream>
#include <functional> #include <functional>
#include <locale> #include <locale>
#include <sstream> #include <sstream>
#include <thread>
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include "cmdparser.h" #include "cmdparser.h"
#include "VCamUtils/src/ipcbridge.h" #include "VCamUtils/src/ipcbridge.h"
@ -99,6 +107,8 @@ namespace AkVCam {
const StringVector &args); const StringVector &args);
int showSupportedFormats(const StringMap &flags, int showSupportedFormats(const StringMap &flags,
const StringVector &args); const StringVector &args);
int showDefaultFormat(const StringMap &flags,
const StringVector &args);
int showFormats(const StringMap &flags, const StringVector &args); int showFormats(const StringMap &flags, const StringVector &args);
int addFormat(const StringMap &flags, const StringVector &args); int addFormat(const StringMap &flags, const StringVector &args);
int removeFormat(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 update(const StringMap &flags, const StringVector &args);
int loadSettings(const StringMap &flags, const StringVector &args); int loadSettings(const StringMap &flags, const StringVector &args);
int stream(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 showControls(const StringMap &flags, const StringVector &args);
int readControl(const StringMap &flags, const StringVector &args); int readControl(const StringMap &flags, const StringVector &args);
int writeControls(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 logLevel(const StringMap &flags, const StringVector &args);
int setLogLevel(const StringMap &flags, const StringVector &args); int setLogLevel(const StringMap &flags, const StringVector &args);
int showClients(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); void loadGenerals(Settings &settings);
VideoFormatMatrix readFormats(Settings &settings); VideoFormatMatrix readFormats(Settings &settings);
std::vector<VideoFormat> readFormat(Settings &settings); std::vector<VideoFormat> readFormat(Settings &settings);
@ -131,6 +143,7 @@ namespace AkVCam {
}; };
std::string operator *(const std::string &str, size_t n); std::string operator *(const std::string &str, size_t n);
std::string operator *(size_t n, const std::string &str);
} }
AkVCam::CmdParser::CmdParser() AkVCam::CmdParser::CmdParser()
@ -141,6 +154,9 @@ AkVCam::CmdParser::CmdParser()
this->addFlags("", this->addFlags("",
{"-h", "--help"}, {"-h", "--help"},
"Show help."); "Show help.");
this->addFlags("",
{"-v", "--version"},
"Show program version.");
this->addFlags("", this->addFlags("",
{"-p", "--parseable"}, {"-p", "--parseable"},
"Show parseable output."); "Show parseable output.");
@ -178,6 +194,16 @@ AkVCam::CmdParser::CmdParser()
this->addFlags("supported-formats", this->addFlags("supported-formats",
{"-o", "--output"}, {"-o", "--output"},
"Show supported output formats."); "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", this->addCommand("formats",
"DEVICE", "DEVICE",
"Show device formats.", "Show device formats.",
@ -210,6 +236,10 @@ AkVCam::CmdParser::CmdParser()
"DEVICE FORMAT WIDTH HEIGHT", "DEVICE FORMAT WIDTH HEIGHT",
"Read frames from stdin and send them to the device.", "Read frames from stdin and send them to the device.",
AKVCAM_BIND_FUNC(CmdParserPrivate::stream)); 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", this->addCommand("controls",
"DEVICE", "DEVICE",
"Show device controls.", "Show device controls.",
@ -263,6 +293,10 @@ AkVCam::CmdParser::CmdParser()
"", "",
"Show clients using the camera.", "Show clients using the camera.",
AKVCAM_BIND_FUNC(CmdParserPrivate::showClients)); AKVCAM_BIND_FUNC(CmdParserPrivate::showClients));
this->addCommand("dump",
"",
"Show all information in a parseable XML format.",
AKVCAM_BIND_FUNC(CmdParserPrivate::dumpInfo));
} }
AkVCam::CmdParser::~CmdParser() AkVCam::CmdParser::~CmdParser()
@ -279,8 +313,10 @@ int AkVCam::CmdParser::parse(int argc, char **argv)
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
std::string arg = argv[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); auto flag = this->d->parserFlag(command->flags, arg);
if (!flag) { 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); return command->func(flags, arguments);
} }
@ -638,6 +677,12 @@ int AkVCam::CmdParserPrivate::defaultHandler(const StringMap &flags,
if (flags.empty() || this->containsFlag(flags, "", "-h")) if (flags.empty() || this->containsFlag(flags, "", "-h"))
return this->showHelp(flags, args); return this->showHelp(flags, args);
if (this->containsFlag(flags, "", "-v")) {
std::cout << COMMONS_VERSION << std::endl;
return 0;
}
if (this->containsFlag(flags, "", "-p")) if (this->containsFlag(flags, "", "-p"))
this->m_parseable = true; this->m_parseable = true;
@ -864,6 +909,21 @@ int AkVCam::CmdParserPrivate::showSupportedFormats(const StringMap &flags,
return 0; 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, int AkVCam::CmdParserPrivate::showFormats(const StringMap &flags,
const StringVector &args) const StringVector &args)
{ {
@ -948,7 +1008,8 @@ int AkVCam::CmdParserPrivate::addFormat(const StringMap &flags,
return -1; 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); auto fit = std::find(formats.begin(), formats.end(), format);
if (fit == formats.end()) { if (fit == formats.end()) {
@ -1141,7 +1202,8 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
return -1; 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); auto fit = std::find(formats.begin(), formats.end(), format);
if (fit == formats.end()) { if (fit == formats.end()) {
@ -1186,6 +1248,11 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
AkVCam::VideoFrame frame(fmt); AkVCam::VideoFrame frame(fmt);
size_t bufferSize = 0; size_t bufferSize = 0;
#ifdef _WIN32
// Set std::cin in binary mode.
_setmode(_fileno(stdin), _O_BINARY);
#endif
do { do {
std::cin.read(reinterpret_cast<char *>(frame.data().data() std::cin.read(reinterpret_cast<char *>(frame.data().data()
+ bufferSize), + bufferSize),
@ -1203,6 +1270,42 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
return 0; 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::string> &) {
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, int AkVCam::CmdParserPrivate::showControls(const StringMap &flags,
const StringVector &args) const StringVector &args)
{ {
@ -1311,11 +1414,14 @@ int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
} }
if (this->containsFlag(flags, "get-control", "-l")) { 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) if (this->m_parseable)
std::cout << control.menu[i] << std::endl; std::cout << control.menu[i] << std::endl;
else 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; 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 << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << std::endl;
std::cout << "<info>" << std::endl;
std::cout << indent << "<devices>" << std::endl;
auto devices = this->m_ipcBridge.devices();
for (auto &device: devices) {
std::cout << 2 * indent << "<device>" << std::endl;
std::cout << 3 * indent << "<id>" << device << "</id>" << std::endl;
std::cout << 3 * indent
<< "<description>"
<< this->m_ipcBridge.description(device)
<< "</description>"
<< std::endl;
std::cout << 3 * indent << "<formats>" << std::endl;
for (auto &format: this->m_ipcBridge.formats(device)) {
std::cout << 4 * indent << "<format>" << std::endl;
std::cout << 5 * indent
<< "<pixel-format>"
<< VideoFormat::stringFromFourcc(format.fourcc())
<< "</pixel-format>"
<< std::endl;
std::cout << 5 * indent
<< "<width>"
<< format.width()
<< "</width>"
<< std::endl;
std::cout << 5 * indent
<< "<height>"
<< format.height()
<< "</height>"
<< std::endl;
std::cout << 5 * indent
<< "<fps>"
<< format.minimumFrameRate().toString()
<< "</fps>"
<< std::endl;
std::cout << 4 * indent << "</format>" << std::endl;
}
std::cout << 3 * indent << "</formats>" << std::endl;
std::cout << 3 * indent << "<controls>" << std::endl;
for (auto &control: this->m_ipcBridge.controls(device)) {
std::cout << 4 * indent << "<control>" << std::endl;
std::cout << 5 * indent
<< "<id>"
<< control.id
<< "</id>"
<< std::endl;
std::cout << 5 * indent
<< "<description>"
<< control.description
<< "</description>"
<< std::endl;
auto typeStr = typeStrMap();
std::cout << 5 * indent
<< "<type>"
<< typeStr[control.type]
<< "</type>"
<< std::endl;
std::cout << 5 * indent
<< "<minimum>"
<< control.minimum
<< "</minimum>"
<< std::endl;
std::cout << 5 * indent
<< "<maximum>"
<< control.maximum
<< "</maximum>"
<< std::endl;
std::cout << 5 * indent
<< "<step>"
<< control.step
<< "</step>"
<< std::endl;
std::cout << 5 * indent
<< "<default-value>"
<< control.defaultValue
<< "</default-value>"
<< std::endl;
std::cout << 5 * indent
<< "<value>"
<< control.value
<< "</value>"
<< std::endl;
if (!control.menu.empty() && control.type == ControlTypeMenu) {
std::cout << 5 * indent << "<menu>" << std::endl;
for (auto &item: control.menu)
std::cout << 6 * indent
<< "<item>"
<< item
<< "</item>"
<< std::endl;
std::cout << 5 * indent << "</menu>" << std::endl;
}
std::cout << 4 * indent << "</control>" << std::endl;
}
std::cout << 3 * indent << "</controls>" << std::endl;
std::cout << 2 * indent << "</device>" << std::endl;
}
std::cout << indent << "</devices>" << std::endl;
std::cout << indent << "<input-formats>" << std::endl;
for (auto &format: this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeInput))
std::cout << 2 * indent
<< "<pixel-format>"
<< VideoFormat::stringFromFourcc(format)
<< "</pixel-format>"
<< std::endl;
std::cout << indent << "</input-formats>" << std::endl;
auto defInputFormat =
this->m_ipcBridge.defaultPixelFormat(IpcBridge::StreamTypeInput);
std::cout << indent
<< "<default-input-format>"
<< VideoFormat::stringFromFourcc(defInputFormat)
<< "</default-input-format>"
<< std::endl;
std::cout << indent << "<output-formats>" << std::endl;
for (auto &format: this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput))
std::cout << 2 * indent
<< "<pixel-format>"
<< VideoFormat::stringFromFourcc(format)
<< "</pixel-format>"
<< std::endl;
std::cout << indent << "</output-formats>" << std::endl;
auto defOutputFormat =
this->m_ipcBridge.defaultPixelFormat(IpcBridge::StreamTypeOutput);
std::cout << indent
<< "<default-output-format>"
<< VideoFormat::stringFromFourcc(defOutputFormat)
<< "</default-output-format>"
<< std::endl;
std::cout << indent << "<clients>" << std::endl;
for (auto &pid: this->m_ipcBridge.clientsPids()) {
std::cout << 2 * indent << "<client>" << std::endl;
std::cout << 3 * indent << "<pid>" << pid << "</pid>" << std::endl;
std::cout << 3 * indent
<< "<exe>"
<< this->m_ipcBridge.clientExe(pid)
<< "</exe>"
<< std::endl;
std::cout << 2 * indent << "</client>" << std::endl;
}
std::cout << indent << "</clients>" << std::endl;
std::cout << indent
<< "<picture>"
<< this->m_ipcBridge.picture()
<< "</picture>"
<< std::endl;
std::cout << indent
<< "<loglevel>"
<< this->m_ipcBridge.logLevel()
<< "</loglevel>"
<< std::endl;
std::cout << "</info>" << std::endl;
return 0;
}
void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings) void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings)
{ {
settings.beginGroup("General"); settings.beginGroup("General");
@ -1654,7 +1943,7 @@ std::vector<AkVCam::VideoFormat> AkVCam::CmdParserPrivate::readFormat(Settings &
VideoFormat format(pixFormat, VideoFormat format(pixFormat,
width, width,
height, height,
{frame_rate}); {frame_rate});
if (format.isValid()) if (format.isValid())
formats.push_back(format); formats.push_back(format);
@ -1735,7 +2024,8 @@ void AkVCam::CmdParserPrivate::createDevice(Settings &settings,
} }
auto deviceId = this->m_ipcBridge.addDevice(description); 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) { for (auto &format: formats) {
auto it = std::find(supportedFormats.begin(), auto it = std::find(supportedFormats.begin(),
@ -1781,3 +2071,13 @@ std::string AkVCam::operator *(const std::string &str, size_t n)
return ss.str(); 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();
}

View file

@ -142,3 +142,10 @@ std::string AkVCam::Fraction::toString() const
return ss.str(); return ss.str();
} }
std::ostream &operator <<(std::ostream &os, const AkVCam::Fraction &fraction)
{
os << fraction.toString();
return os;
}

View file

@ -53,4 +53,6 @@ namespace AkVCam
}; };
} }
std::ostream &operator <<(std::ostream &os, const AkVCam::Fraction &fraction);
#endif // FRACTION_H #endif // FRACTION_H

View file

@ -17,8 +17,9 @@
* Web-Site: http://webcamoid.github.io/ * Web-Site: http://webcamoid.github.io/
*/ */
#include <map>
#include <algorithm> #include <algorithm>
#include <map>
#include <ostream>
#include "videoformat.h" #include "videoformat.h"
#include "../utils.h" #include "../utils.h"
@ -421,3 +422,38 @@ size_t AkVCam::VideoFormatGlobals::byplNV(size_t plane, size_t width)
return align32(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;
}

View file

@ -28,7 +28,9 @@
namespace AkVCam namespace AkVCam
{ {
class VideoFormat;
class VideoFormatPrivate; class VideoFormatPrivate;
using VideoFormats = std::vector<VideoFormat>;
class VideoFormat 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 #endif // AKVCAMUTILS_VIDEOFORMAT_H

View file

@ -115,10 +115,10 @@ namespace AkVCam
void setDescription(const std::string &deviceId, void setDescription(const std::string &deviceId,
const std::string &description); const std::string &description);
// Output pixel formats supported by the driver. // Pixel formats supported by the driver.
std::vector<PixelFormat> supportedPixelFormats(StreamType type) const; std::vector<PixelFormat> supportedPixelFormats(StreamType type) const;
// Default output pixel format of the driver. // Default pixel format of the driver.
PixelFormat defaultPixelFormat(StreamType type) const; PixelFormat defaultPixelFormat(StreamType type) const;
// Return supported formats for the device. // Return supported formats for the device.
@ -171,6 +171,9 @@ namespace AkVCam
// Decrement the count of device listeners // Decrement the count of device listeners
bool removeListener(const std::string &deviceId); bool removeListener(const std::string &deviceId);
bool needsRoot(const std::string &operation) const;
int sudo(int argc, char **argv) const;
private: private:
IpcBridgePrivate *d; IpcBridgePrivate *d;

View file

@ -108,10 +108,11 @@ bool AkVCam::Settings::load(const std::string &fileName)
if (this->d->m_configs.count(currentGroup) < 1) if (this->d->m_configs.count(currentGroup) < 1)
this->d->m_configs[currentGroup] = {}; this->d->m_configs[currentGroup] = {};
} else if (!element.key.empty() && !element.value.empty()) { } else if (!element.key.empty() && !element.value.empty()) {
if (currentGroup.empty()) { if (currentGroup.empty())
currentGroup = "General"; currentGroup = "General";
if (this->d->m_configs.count(currentGroup) < 1)
this->d->m_configs[currentGroup] = {}; this->d->m_configs[currentGroup] = {};
}
this->d->m_configs[currentGroup][element.key] = element.value; 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()) { if (this->d->m_currentArray.empty()) {
contains = groupConfigs.count(key) > 0; contains = groupConfigs.count(key) > 0;
} else { } else {
std::string arrayKey; std::stringstream arrayKey;
std::stringstream ss(arrayKey); arrayKey << this->d->m_currentArray
ss << this->d->m_currentArray << '/'
<< '/' << this->d->m_arrayIndex + 1
<< this->d->m_arrayIndex + 1 << '/'
<< '/' << key;
<< key; contains = groupConfigs.count(arrayKey.str()) > 0;
contains = groupConfigs.count(arrayKey) > 0;
} }
return contains; return contains;
@ -237,14 +237,13 @@ std::string AkVCam::Settings::value(const std::string &key) const
if (this->d->m_currentArray.empty()) { if (this->d->m_currentArray.empty()) {
value = groupConfigs[key]; value = groupConfigs[key];
} else { } else {
std::string arrayKey; std::stringstream arrayKey;
std::stringstream ss(arrayKey); arrayKey << this->d->m_currentArray
ss << this->d->m_currentArray << '/'
<< '/' << this->d->m_arrayIndex + 1
<< this->d->m_arrayIndex + 1 << '/'
<< '/' << key;
<< key; value = groupConfigs[arrayKey.str()];
value = groupConfigs[arrayKey];
} }
return value; return value;
@ -358,7 +357,7 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line,
if (line.empty() || line[0] == '#' || line[0] == ';') { if (line.empty() || line[0] == '#' || line[0] == ';') {
if (ok) if (ok)
*ok = false; *ok = true;
return {}; return {};
} }

View file

@ -44,12 +44,20 @@ std::string AkVCam::replace(const std::string &str,
const std::string &from, const std::string &from,
const std::string &to) const std::string &to)
{ {
auto newStr = str; std::string newStr;
for (auto pos = newStr.find(from); for (size_t i = 0; i < str.size(); i++) {
pos != std::string::npos; auto pos = str.find(from, i);
pos = newStr.find(from))
newStr.replace(pos, from.size(), to); 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; return newStr;
} }

View file

@ -26,7 +26,6 @@
#include "assistant.h" #include "assistant.h"
#include "assistantglobals.h" #include "assistantglobals.h"
#include "PlatformUtils/src/preferences.h"
#include "PlatformUtils/src/utils.h" #include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/image/videoframe.h"
@ -63,11 +62,11 @@ namespace AkVCam
bool startTimer(); bool startTimer();
void stopTimer(); void stopTimer();
static void timerTimeout(CFRunLoopTimerRef timer, void *info); static void timerTimeout(CFRunLoopTimerRef timer, void *info);
void releaseDevicesFromPeer(const std::string &portName);
void peerDied(); 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 requestPort(xpc_connection_t client, xpc_object_t event);
void addPort(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 removePort(xpc_connection_t client, xpc_object_t event);
void devicesUpdate(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); 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 listeners(xpc_connection_t client, xpc_object_t event);
void listener(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 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 listenerAdd(xpc_connection_t client, xpc_object_t event);
void listenerRemove(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()); 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() void AkVCam::AssistantPrivate::peerDied()
{ {
AkLogFunction(); AkLogFunction();
std::vector<std::string> deadPeers;
for (auto &client: this->m_clients) { for (auto &client: this->m_clients) {
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
@ -249,8 +218,57 @@ void AkVCam::AssistantPrivate::peerDied()
xpc_release(reply); xpc_release(reply);
if (!alive) 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, void AkVCam::AssistantPrivate::requestPort(xpc_connection_t client,
@ -299,25 +317,6 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client,
xpc_release(reply); 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, void AkVCam::AssistantPrivate::removePort(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {
@ -332,21 +331,38 @@ void AkVCam::AssistantPrivate::devicesUpdate(xpc_connection_t client,
{ {
UNUSED(client); UNUSED(client);
AkLogFunction(); AkLogFunction();
auto notification = xpc_copy(event);
for (auto &client: this->m_clients) auto devicesList = xpc_dictionary_get_array(event, "devices");
xpc_connection_send_message(client.second, notification); 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, void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {
UNUSED(client);
AkLogFunction(); AkLogFunction();
std::string deviceId = xpc_dictionary_get_string(event, "device"); std::string deviceId = xpc_dictionary_get_string(event, "device");
std::string broadcaster = xpc_dictionary_get_string(event, "broadcaster"); std::string broadcaster = xpc_dictionary_get_string(event, "broadcaster");
bool ok = false;
if (this->m_deviceConfigs.count(deviceId) > 0) if (this->m_deviceConfigs.count(deviceId) > 0)
if (this->m_deviceConfigs[deviceId].broadcaster != broadcaster) { 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_connection_send_message(client.second, notification);
xpc_release(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, void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
@ -378,6 +388,7 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
bool ok = true; bool ok = true;
for (auto &client: this->m_clients) { 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, auto reply = xpc_connection_send_message_with_reply_sync(client.second,
event); event);
auto replyType = xpc_get_type(reply); 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"); isOk = xpc_dictionary_get_bool(reply, "status");
ok &= isOk; ok &= isOk;
if (!isOk)
AkLogError() << "Failed sending frame." << std::endl;
xpc_release(reply); xpc_release(reply);
} }
@ -400,25 +415,12 @@ void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client,
{ {
UNUSED(client); UNUSED(client);
AkLogFunction(); AkLogFunction();
auto reply = xpc_dictionary_create_reply(event); auto notification = xpc_copy(event);
bool ok = true;
for (auto &client: this->m_clients) { for (auto &client: this->m_clients)
auto reply = xpc_connection_send_message_with_reply_sync(client.second, xpc_connection_send_message(client.second, notification);
event);
auto replyType = xpc_get_type(reply);
bool isOk = false;
if (replyType == XPC_TYPE_DICTIONARY) xpc_release(notification);
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);
} }
void AkVCam::AssistantPrivate::listeners(xpc_connection_t client, void AkVCam::AssistantPrivate::listeners(xpc_connection_t client,
@ -484,22 +486,6 @@ void AkVCam::AssistantPrivate::broadcasting(xpc_connection_t client,
xpc_release(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) {
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, void AkVCam::AssistantPrivate::listenerAdd(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {
@ -559,3 +545,27 @@ void AkVCam::AssistantPrivate::listenerRemove(xpc_connection_t client,
xpc_connection_send_message(client, reply); xpc_connection_send_message(client, reply);
xpc_release(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);
}

View file

@ -47,8 +47,10 @@ int main(int argc, char **argv)
auto timeout = strtod(argv[i + 1], nullptr); auto timeout = strtod(argv[i + 1], nullptr);
AkLogInfo() << "Set timeout: " << timeout << std::endl; AkLogInfo() << "Set timeout: " << timeout << std::endl;
assistant()->setTimeout(timeout); assistant()->setTimeout(timeout);
} else if (strcmp(argv[i], "--loglevel") == 0 && i + 1 < argc) {
break; auto loglevel = strtoul(argv[i + 1], nullptr, 10);
AkVCam::Logger::setLogLevel(loglevel);
AkLogInfo() << "Set loglevel: " << loglevel << std::endl;
} }
AkLogDebug() << "Setting up handler" << std::endl; AkLogDebug() << "Setting up handler" << std::endl;

View file

@ -61,7 +61,11 @@ void AkVCam::Preferences::write(const std::string &key,
AkLogFunction(); AkLogFunction();
AkLogInfo() << "Writing: " << key << " = " << *value << std::endl; AkLogInfo() << "Writing: " << key << " = " << *value << std::endl;
auto cfKey = cfTypeFromStd(key); 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, 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; AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = cfTypeFromStd(value); 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) 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; AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = cfTypeFromStd(value); 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) 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; AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = cfTypeFromStd(value); 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, void AkVCam::Preferences::write(const std::string &key,
@ -103,8 +119,10 @@ std::shared_ptr<CFTypeRef> AkVCam::Preferences::read(const std::string &key)
{ {
AkLogFunction(); AkLogFunction();
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = CFTypeRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), auto cfValue = CFTypeRef(CFPreferencesCopyValue(CFStringRef(*cfKey),
PREFERENCES_ID)); PREFERENCES_ID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost));
if (!cfValue) if (!cfValue)
return {}; return {};
@ -122,8 +140,10 @@ std::string AkVCam::Preferences::readString(const std::string &key,
AkLogFunction(); AkLogFunction();
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = auto cfValue =
CFStringRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), CFStringRef(CFPreferencesCopyValue(CFStringRef(*cfKey),
PREFERENCES_ID)); PREFERENCES_ID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost));
auto value = defaultValue; auto value = defaultValue;
if (cfValue) { if (cfValue) {
@ -139,8 +159,10 @@ int AkVCam::Preferences::readInt(const std::string &key, int defaultValue)
AkLogFunction(); AkLogFunction();
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = auto cfValue =
CFNumberRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), CFNumberRef(CFPreferencesCopyValue(CFStringRef(*cfKey),
PREFERENCES_ID)); PREFERENCES_ID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost));
auto value = defaultValue; auto value = defaultValue;
if (cfValue) { if (cfValue) {
@ -157,8 +179,10 @@ double AkVCam::Preferences::readDouble(const std::string &key,
AkLogFunction(); AkLogFunction();
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = auto cfValue =
CFNumberRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), CFNumberRef(CFPreferencesCopyValue(CFStringRef(*cfKey),
PREFERENCES_ID)); PREFERENCES_ID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost));
auto value = defaultValue; auto value = defaultValue;
if (cfValue) { if (cfValue) {
@ -174,8 +198,10 @@ bool AkVCam::Preferences::readBool(const std::string &key, bool defaultValue)
AkLogFunction(); AkLogFunction();
auto cfKey = cfTypeFromStd(key); auto cfKey = cfTypeFromStd(key);
auto cfValue = auto cfValue =
CFBooleanRef(CFPreferencesCopyAppValue(CFStringRef(*cfKey), CFBooleanRef(CFPreferencesCopyValue(CFStringRef(*cfKey),
PREFERENCES_ID)); PREFERENCES_ID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost));
auto value = defaultValue; auto value = defaultValue;
if (cfValue) { if (cfValue) {
@ -202,7 +228,11 @@ void AkVCam::Preferences::deleteKey(const std::string &key)
AkLogFunction(); AkLogFunction();
AkLogInfo() << "Deleting " << key << std::endl; AkLogInfo() << "Deleting " << key << std::endl;
auto cfKey = cfTypeFromStd(key); 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) void AkVCam::Preferences::deleteAllKeys(const std::string &key)
@ -250,7 +280,9 @@ void AkVCam::Preferences::moveAll(const std::string &keyFrom,
void AkVCam::Preferences::sync() void AkVCam::Preferences::sync()
{ {
AkLogFunction(); AkLogFunction();
CFPreferencesAppSynchronize(PREFERENCES_ID); CFPreferencesSynchronize(PREFERENCES_ID,
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost);
} }
std::string AkVCam::Preferences::addDevice(const std::string &description) std::string AkVCam::Preferences::addDevice(const std::string &description)

View file

@ -64,6 +64,7 @@ namespace AkVCam
void remove(IpcBridge *bridge); void remove(IpcBridge *bridge);
inline std::vector<IpcBridge *> &bridges(); inline std::vector<IpcBridge *> &bridges();
inline const std::vector<DeviceControl> &controls() const; inline const std::vector<DeviceControl> &controls() const;
void updateDevices(xpc_connection_t port, bool propagate);
// Message handling methods // Message handling methods
void isAlive(xpc_connection_t client, xpc_object_t event); void isAlive(xpc_connection_t client, xpc_object_t event);
@ -155,6 +156,8 @@ bool AkVCam::IpcBridge::registerPeer()
xpc_type_t replyType; xpc_type_t replyType;
bool status = false; bool status = false;
AkLogDebug() << "Requesting service." << std::endl;
auto serverMessagePort = auto serverMessagePort =
xpc_connection_create_mach_service(CMIO_ASSISTANT_NAME, xpc_connection_create_mach_service(CMIO_ASSISTANT_NAME,
nullptr, nullptr,
@ -163,6 +166,8 @@ bool AkVCam::IpcBridge::registerPeer()
if (!serverMessagePort) if (!serverMessagePort)
goto registerEndPoint_failed; goto registerEndPoint_failed;
AkLogDebug() << "Setting event handler." << std::endl;
xpc_connection_set_event_handler(serverMessagePort, ^(xpc_object_t event) { xpc_connection_set_event_handler(serverMessagePort, ^(xpc_object_t event) {
if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
ipcBridgePrivate().connectionInterrupted(); ipcBridgePrivate().connectionInterrupted();
@ -170,6 +175,8 @@ bool AkVCam::IpcBridge::registerPeer()
}); });
xpc_connection_resume(serverMessagePort); xpc_connection_resume(serverMessagePort);
AkLogDebug() << "Requesting port." << std::endl;
dictionary = xpc_dictionary_create(nullptr, nullptr, 0); dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_REQUEST_PORT); xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_REQUEST_PORT);
reply = xpc_connection_send_message_with_reply_sync(serverMessagePort, reply = xpc_connection_send_message_with_reply_sync(serverMessagePort,
@ -185,6 +192,8 @@ bool AkVCam::IpcBridge::registerPeer()
if (replyType != XPC_TYPE_DICTIONARY) if (replyType != XPC_TYPE_DICTIONARY)
goto registerEndPoint_failed; goto registerEndPoint_failed;
AkLogDebug() << "Creating message port." << std::endl;
messagePort = xpc_connection_create(nullptr, nullptr); messagePort = xpc_connection_create(nullptr, nullptr);
if (!messagePort) if (!messagePort)
@ -207,6 +216,8 @@ bool AkVCam::IpcBridge::registerPeer()
xpc_connection_resume(messagePort); xpc_connection_resume(messagePort);
AkLogDebug() << "Adding port to the server." << std::endl;
dictionary = xpc_dictionary_create(nullptr, nullptr, 0); dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_ADD_PORT); xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_ADD_PORT);
xpc_dictionary_set_string(dictionary, "port", portName.c_str()); xpc_dictionary_set_string(dictionary, "port", portName.c_str());
@ -229,6 +240,7 @@ bool AkVCam::IpcBridge::registerPeer()
this->d->m_serverMessagePort = serverMessagePort; this->d->m_serverMessagePort = serverMessagePort;
AkLogInfo() << "SUCCESSFUL" << std::endl; AkLogInfo() << "SUCCESSFUL" << std::endl;
this->d->updateDevices(serverMessagePort, false);
return true; return true;
@ -273,9 +285,9 @@ void AkVCam::IpcBridge::unregisterPeer()
std::vector<std::string> AkVCam::IpcBridge::devices() const std::vector<std::string> AkVCam::IpcBridge::devices() const
{ {
AkLogFunction(); AkLogFunction();
AkLogInfo() << "Devices:" << std::endl;
std::vector<std::string> devices; std::vector<std::string> devices;
auto nCameras = Preferences::camerasCount(); auto nCameras = Preferences::camerasCount();
AkLogInfo() << "Devices:" << std::endl;
for (size_t i = 0; i < nCameras; i++) { for (size_t i = 0; i < nCameras; i++) {
auto deviceId = Preferences::cameraPath(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) std::string AkVCam::IpcBridge::addDevice(const std::string &description)
{ {
AkLogFunction();
return Preferences::addDevice(description); return Preferences::addDevice(description);
} }
void AkVCam::IpcBridge::removeDevice(const std::string &deviceId) void AkVCam::IpcBridge::removeDevice(const std::string &deviceId)
{ {
AkLogFunction();
Preferences::removeCamera(deviceId); Preferences::removeCamera(deviceId);
} }
void AkVCam::IpcBridge::addFormat(const std::string &deviceId, void AkVCam::IpcBridge::addFormat(const std::string &deviceId,
const AkVCam::VideoFormat &format, const VideoFormat &format,
int index) int index)
{ {
AkLogFunction(); AkLogFunction();
@ -556,16 +572,7 @@ void AkVCam::IpcBridge::removeFormat(const std::string &deviceId, int index)
void AkVCam::IpcBridge::updateDevices() void AkVCam::IpcBridge::updateDevices()
{ {
AkLogFunction(); AkLogFunction();
this->d->updateDevices(this->d->m_serverMessagePort, true);
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);
} }
bool AkVCam::IpcBridge::deviceStart(const std::string &deviceId, 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_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING);
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
xpc_dictionary_set_string(dictionary, "broadcaster", this->d->m_portName.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, xpc_connection_send_message(this->d->m_serverMessagePort, dictionary);
dictionary);
xpc_release(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); this->d->m_broadcasting.push_back(deviceId);
return status; return true;
} }
void AkVCam::IpcBridge::deviceStop(const std::string &deviceId) 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_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING);
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
xpc_dictionary_set_string(dictionary, "broadcaster", ""); xpc_dictionary_set_string(dictionary, "broadcaster", "");
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, xpc_connection_send_message(this->d->m_serverMessagePort, dictionary);
dictionary);
xpc_release(dictionary); xpc_release(dictionary);
xpc_release(reply);
this->d->m_broadcasting.erase(it); this->d->m_broadcasting.erase(it);
} }
@ -766,6 +761,21 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
return status; 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): AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
self(self), self(self),
m_messagePort(nullptr), m_messagePort(nullptr),
@ -833,6 +843,32 @@ const std::vector<AkVCam::DeviceControl> &AkVCam::IpcBridgePrivate::controls() c
return controls; 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, void AkVCam::IpcBridgePrivate::isAlive(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {

View file

@ -68,6 +68,7 @@ OSStatus AkVCam::Device::createObject()
OSStatus AkVCam::Device::registerObject(bool regist) OSStatus AkVCam::Device::registerObject(bool regist)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Register: " << regist << std::endl;
OSStatus status = kCMIOHardwareUnspecifiedError; OSStatus status = kCMIOHardwareUnspecifiedError;
if (!this->m_isCreated if (!this->m_isCreated
@ -91,6 +92,11 @@ OSStatus AkVCam::Device::registerObject(bool regist)
&this->m_objectID); &this->m_objectID);
} }
if (status == kCMIOHardwareNoError)
AkLogDebug() << "Ok";
else
AkLogDebug() << "Error registering device" << std::endl;
return status; return status;
} }
@ -134,6 +140,7 @@ std::list<AkVCam::StreamPtr> AkVCam::Device::addStreams(int n)
OSStatus AkVCam::Device::registerStreams(bool regist) OSStatus AkVCam::Device::registerStreams(bool regist)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Register: " << regist << std::endl;
OSStatus status = kCMIOHardwareUnspecifiedError; OSStatus status = kCMIOHardwareUnspecifiedError;
if (!this->m_isCreated if (!this->m_isCreated
@ -163,6 +170,11 @@ OSStatus AkVCam::Device::registerStreams(bool regist)
streams.data()); streams.data());
} }
if (status == kCMIOHardwareNoError)
AkLogDebug() << "Ok";
else
AkLogDebug() << "Error registering streams" << std::endl;
return status; return status;
} }
@ -338,6 +350,7 @@ OSStatus AkVCam::Device::processRS422Command(CMIODeviceRS422Command *ioRS422Comm
void AkVCam::Device::updateStreamsProperty() void AkVCam::Device::updateStreamsProperty()
{ {
AkLogFunction();
std::vector<ObjectPtr> streams; std::vector<ObjectPtr> streams;
for (auto &stream: this->m_streams) for (auto &stream: this->m_streams)

View file

@ -437,6 +437,7 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
stream->properties().setProperty(kCMIOStreamPropertyDirection, 0); stream->properties().setProperty(kCMIOStreamPropertyDirection, 0);
if (device->registerStreams() != kCMIOHardwareNoError) { if (device->registerStreams() != kCMIOHardwareNoError) {
AkLogDebug() << "Failed registering streams" << std::endl;
device->registerStreams(false); device->registerStreams(false);
goto createDevice_failed; goto createDevice_failed;
@ -444,6 +445,7 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
// Register the device. // Register the device.
if (device->registerObject() != kCMIOHardwareNoError) { if (device->registerObject() != kCMIOHardwareNoError) {
AkLogDebug() << "Failed registering device" << std::endl;
device->registerObject(false); device->registerObject(false);
device->registerStreams(false); device->registerStreams(false);

View file

@ -309,6 +309,7 @@ void AkVCam::Stream::frameReady(const AkVCam::VideoFrame &frame)
void AkVCam::Stream::setBroadcasting(const std::string &broadcaster) void AkVCam::Stream::setBroadcasting(const std::string &broadcaster)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Broadcaster: " << broadcaster << std::endl;
if (this->d->m_broadcaster == broadcaster) if (this->d->m_broadcaster == broadcaster)
return; return;
@ -325,6 +326,7 @@ void AkVCam::Stream::setBroadcasting(const std::string &broadcaster)
void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror) void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Mirror: " << horizontalMirror << std::endl;
if (this->d->m_horizontalMirror == horizontalMirror) if (this->d->m_horizontalMirror == horizontalMirror)
return; return;
@ -342,6 +344,7 @@ void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror)
void AkVCam::Stream::setVerticalMirror(bool verticalMirror) void AkVCam::Stream::setVerticalMirror(bool verticalMirror)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Mirror: " << verticalMirror << std::endl;
if (this->d->m_verticalMirror == verticalMirror) if (this->d->m_verticalMirror == verticalMirror)
return; return;
@ -359,6 +362,7 @@ void AkVCam::Stream::setVerticalMirror(bool verticalMirror)
void AkVCam::Stream::setScaling(Scaling scaling) void AkVCam::Stream::setScaling(Scaling scaling)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Scaling: " << scaling << std::endl;
if (this->d->m_scaling == scaling) if (this->d->m_scaling == scaling)
return; return;
@ -376,6 +380,7 @@ void AkVCam::Stream::setScaling(Scaling scaling)
void AkVCam::Stream::setAspectRatio(AspectRatio aspectRatio) void AkVCam::Stream::setAspectRatio(AspectRatio aspectRatio)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Aspect ratio: " << aspectRatio << std::endl;
if (this->d->m_aspectRatio == aspectRatio) if (this->d->m_aspectRatio == aspectRatio)
return; return;
@ -393,6 +398,7 @@ void AkVCam::Stream::setAspectRatio(AspectRatio aspectRatio)
void AkVCam::Stream::setSwapRgb(bool swap) void AkVCam::Stream::setSwapRgb(bool swap)
{ {
AkLogFunction(); AkLogFunction();
AkLogDebug() << "Swap: " << swap << std::endl;
if (this->d->m_swapRgb == swap) if (this->d->m_swapRgb == swap)
return; return;
@ -625,11 +631,14 @@ void AkVCam::StreamPrivate::sendFrame(const VideoFrame &frame)
void AkVCam::StreamPrivate::updateTestFrame() void AkVCam::StreamPrivate::updateTestFrame()
{ {
AkLogFunction();
this->m_testFrameAdapted = this->applyAdjusts(this->m_testFrame); this->m_testFrameAdapted = this->applyAdjusts(this->m_testFrame);
} }
AkVCam::VideoFrame AkVCam::StreamPrivate::applyAdjusts(const VideoFrame &frame) AkVCam::VideoFrame AkVCam::StreamPrivate::applyAdjusts(const VideoFrame &frame)
{ {
AkLogFunction();
VideoFormat format; VideoFormat format;
this->self->m_properties.getProperty(kCMIOStreamPropertyFormatDescription, this->self->m_properties.getProperty(kCMIOStreamPropertyFormatDescription,
&format); &format);

View file

@ -21,7 +21,7 @@ isEmpty(CMIO_PLUGIN_NAME):
isEmpty(CMIO_PLUGIN_ASSISTANT_NAME): isEmpty(CMIO_PLUGIN_ASSISTANT_NAME):
CMIO_PLUGIN_ASSISTANT_NAME = AkVCamAssistant CMIO_PLUGIN_ASSISTANT_NAME = AkVCamAssistant
isEmpty(CMIO_PLUGIN_DEVICE_PREFIX): isEmpty(CMIO_PLUGIN_DEVICE_PREFIX):
CMIO_PLUGIN_DEVICE_PREFIX = /akvcam/video CMIO_PLUGIN_DEVICE_PREFIX = AkVCamVideoDevice
isEmpty(CMIO_PLUGIN_VENDOR): isEmpty(CMIO_PLUGIN_VENDOR):
CMIO_PLUGIN_VENDOR = "Webcamoid Project" CMIO_PLUGIN_VENDOR = "Webcamoid Project"
isEmpty(CMIO_PLUGIN_PRODUCT): isEmpty(CMIO_PLUGIN_PRODUCT):

View file

@ -31,6 +31,7 @@
#include "service.h" #include "service.h"
#include "PlatformUtils/src/messageserver.h" #include "PlatformUtils/src/messageserver.h"
#include "PlatformUtils/src/preferences.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/image/videoframe.h"
#include "VCamUtils/src/timer.h" #include "VCamUtils/src/timer.h"
@ -53,27 +54,26 @@ namespace AkVCam
SERVICE_STATUS m_status; SERVICE_STATUS m_status;
SERVICE_STATUS_HANDLE m_statusHandler; SERVICE_STATUS_HANDLE m_statusHandler;
MessageServer m_messageServer; MessageServer m_messageServer;
AssistantPeers m_servers;
AssistantPeers m_clients; AssistantPeers m_clients;
DeviceConfigs m_deviceConfigs; DeviceConfigs m_deviceConfigs;
Timer m_timer; Timer m_timer;
std::mutex m_peerMutex; std::mutex m_peerMutex;
ServicePrivate(); ServicePrivate();
inline static uint64_t id();
static void stateChanged(void *userData, static void stateChanged(void *userData,
MessageServer::State state); MessageServer::State state);
static void checkPeers(void *userData);
void sendStatus(DWORD currentState, DWORD exitCode, DWORD wait); 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 removePortByName(const std::string &portName);
void releaseDevicesFromPeer(const std::string &portName); void releaseDevicesFromPeer(const std::string &portName);
void requestPort(Message *message); void requestPort(Message *message);
void addPort(Message *message); void addPort(Message *message);
void removePort(Message *message); void removePort(Message *message);
void devicesUpdate(Message *message);
void setBroadCasting(Message *message); void setBroadCasting(Message *message);
void frameReady(Message *message); void frameReady(Message *message);
void pictureUpdated(Message *message); void pictureUpdated(Message *message);
void deviceUpdate(Message *message);
void listeners(Message *message); void listeners(Message *message);
void listener(Message *message); void listener(Message *message);
void broadcasting(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_REQUEST_PORT , AKVCAM_BIND_FUNC(ServicePrivate::requestPort) },
{AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(ServicePrivate::addPort) }, {AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(ServicePrivate::addPort) },
{AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(ServicePrivate::removePort) }, {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_ADD , AKVCAM_BIND_FUNC(ServicePrivate::listenerAdd) },
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , AKVCAM_BIND_FUNC(ServicePrivate::listenerRemove) }, {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , AKVCAM_BIND_FUNC(ServicePrivate::listenerRemove) },
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS , AKVCAM_BIND_FUNC(ServicePrivate::listeners) }, {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.setInterval(60000);
this->m_timer.connectTimeout(this, &ServicePrivate::checkPeers); 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, 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<ServicePrivate *>(userData);
std::vector<std::string> removePorts;
self->m_peerMutex.lock();
std::vector<AssistantPeers *> 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<MsgIsAlive>(&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, void AkVCam::ServicePrivate::sendStatus(DWORD currentState,
DWORD exitCode, DWORD exitCode,
DWORD wait) DWORD wait)
@ -358,51 +335,55 @@ void AkVCam::ServicePrivate::sendStatus(DWORD currentState,
SetServiceStatus(this->m_statusHandler, &this->m_status); 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<ServicePrivate *>(userData);
std::vector<std::string> 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<MsgIsAlive>(&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) void AkVCam::ServicePrivate::removePortByName(const std::string &portName)
{ {
AkLogFunction(); AkLogFunction();
AkLogInfo() << "Port: " << portName << std::endl; AkLogDebug() << "Port: " << portName << std::endl;
this->m_peerMutex.lock(); this->m_peerMutex.lock();
std::vector<AssistantPeers *> allPeers { for (auto &peer: this->m_clients)
&this->m_clients, if (peer.first == portName) {
&this->m_servers 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; break;
} }
bool peersEmpty = this->m_servers.empty() && this->m_clients.empty();
this->m_peerMutex.unlock(); this->m_peerMutex.unlock();
if (peersEmpty)
this->m_timer.stop();
this->releaseDevicesFromPeer(portName); this->releaseDevicesFromPeer(portName);
} }
void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName) void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName)
{ {
AkLogFunction();
for (auto &config: this->m_deviceConfigs) for (auto &config: this->m_deviceConfigs)
if (config.second.broadcaster == portName) { if (config.second.broadcaster == portName) {
config.second.broadcaster.clear(); config.second.broadcaster.clear();
@ -428,6 +409,8 @@ void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName)
if (it != config.second.listeners.end()) if (it != config.second.listeners.end())
config.second.listeners.erase(it); config.second.listeners.erase(it);
} }
AkLogInfo() << portName << " released." << std::endl;
} }
void AkVCam::ServicePrivate::requestPort(AkVCam::Message *message) void AkVCam::ServicePrivate::requestPort(AkVCam::Message *message)
@ -453,15 +436,9 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
bool ok = true; bool ok = true;
this->m_peerMutex.lock(); this->m_peerMutex.lock();
AssistantPeers *peers;
if (portName.find(AKVCAM_ASSISTANT_CLIENT_NAME) != std::string::npos) for (auto &client: this->m_clients)
peers = &this->m_clients; if (client.first == portName) {
else
peers = &this->m_servers;
for (auto &peer: *peers)
if (peer.first == portName) {
ok = false; ok = false;
break; break;
@ -469,16 +446,10 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
if (ok) { if (ok) {
AkLogInfo() << "Adding Peer: " << portName << std::endl; 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(); this->m_peerMutex.unlock();
if (ok && nPeers == 1)
this->m_timer.start();
data->status = ok; data->status = ok;
} }
@ -490,6 +461,31 @@ void AkVCam::ServicePrivate::removePort(AkVCam::Message *message)
this->removePortByName(data->port); this->removePortByName(data->port);
} }
void AkVCam::ServicePrivate::devicesUpdate(AkVCam::Message *message)
{
AkLogFunction();
auto data = messageData<MsgDevicesUpdated>(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) void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message)
{ {
AkLogFunction(); AkLogFunction();
@ -498,25 +494,22 @@ void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message)
std::string broadcaster(data->broadcaster); std::string broadcaster(data->broadcaster);
data->status = false; data->status = false;
if (this->m_deviceConfigs.count(deviceId) < 1) if (this->m_deviceConfigs.count(deviceId) > 0)
this->m_deviceConfigs[deviceId] = {}; 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) this->m_peerMutex.lock();
return;
AkLogInfo() << "Device: " << deviceId << std::endl; for (auto &client: this->m_clients) {
AkLogInfo() << "Broadcaster: " << broadcaster << std::endl; Message msg(message);
this->m_deviceConfigs[deviceId].broadcaster = broadcaster; MessageServer::sendMessage(client.second, &msg);
data->status = true; }
this->m_peerMutex.lock(); this->m_peerMutex.unlock();
}
for (auto &client: this->m_clients) {
Message msg(message);
MessageServer::sendMessage(client.second, &msg);
}
this->m_peerMutex.unlock();
} }
void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message) void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message)
@ -541,17 +534,6 @@ void AkVCam::ServicePrivate::pictureUpdated(AkVCam::Message *message)
this->m_peerMutex.unlock(); 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) void AkVCam::ServicePrivate::listeners(AkVCam::Message *message)
{ {
AkLogFunction(); AkLogFunction();
@ -707,7 +689,7 @@ DWORD WINAPI controlHandler(DWORD control,
AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING, AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING,
NO_ERROR, NO_ERROR,
0); 0);
AkVCam::servicePrivate()->m_messageServer.stop(); AkVCam::servicePrivate()->m_messageServer.stop(true);
result = NO_ERROR; result = NO_ERROR;
break; break;
@ -732,7 +714,7 @@ BOOL WINAPI controlDebugHandler(DWORD control)
AkLogFunction(); AkLogFunction();
if (control == CTRL_BREAK_EVENT || control == CTRL_C_EVENT) { if (control == CTRL_BREAK_EVENT || control == CTRL_C_EVENT) {
AkVCam::servicePrivate()->m_messageServer.stop(); AkVCam::servicePrivate()->m_messageServer.stop(true);
return TRUE; return TRUE;
} }

View file

@ -141,14 +141,9 @@ namespace AkVCam
char port[MAX_STRING]; char port[MAX_STRING];
}; };
struct MsgDeviceAdded struct MsgDevicesUpdated
{ {
char device[MAX_STRING]; bool propagate;
};
struct MsgDeviceRemoved
{
char device[MAX_STRING];
}; };
struct MsgBroadcasting struct MsgBroadcasting

View file

@ -17,6 +17,8 @@
* Web-Site: http://webcamoid.github.io/ * Web-Site: http://webcamoid.github.io/
*/ */
#include <algorithm>
#include <atomic>
#include <chrono> #include <chrono>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
@ -30,6 +32,14 @@
namespace AkVCam namespace AkVCam
{ {
struct PipeThread
{
std::shared_ptr<std::thread> thread;
std::atomic<bool> finished {false};
};
using PipeThreadPtr = std::shared_ptr<PipeThread>;
class MessageServerPrivate class MessageServerPrivate
{ {
public: public:
@ -38,9 +48,8 @@ namespace AkVCam
std::map<uint32_t, MessageHandler> m_handlers; std::map<uint32_t, MessageHandler> m_handlers;
MessageServer::ServerMode m_mode {MessageServer::ServerModeReceive}; MessageServer::ServerMode m_mode {MessageServer::ServerModeReceive};
MessageServer::PipeState m_pipeState {MessageServer::PipeStateGone}; MessageServer::PipeState m_pipeState {MessageServer::PipeStateGone};
HANDLE m_pipe {INVALID_HANDLE_VALUE}; std::thread m_mainThread;
OVERLAPPED m_overlapped; std::vector<PipeThreadPtr> m_clientsThreads;
std::thread m_thread;
std::mutex m_mutex; std::mutex m_mutex;
std::condition_variable_any m_exitCheckLoop; std::condition_variable_any m_exitCheckLoop;
int m_checkInterval {5000}; int m_checkInterval {5000};
@ -52,10 +61,8 @@ namespace AkVCam
bool startSend(); bool startSend();
void stopSend(); void stopSend();
void messagesLoop(); void messagesLoop();
void processPipe(PipeThreadPtr pipeThread, HANDLE pipe);
void checkLoop(); 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() AkVCam::MessageServer::~MessageServer()
{ {
this->stop(true); this->stop();
delete this->d; delete this->d;
} }
@ -177,27 +184,116 @@ BOOL AkVCam::MessageServer::sendMessage(const std::string &pipeName,
Message *messageOut, Message *messageOut,
uint32_t timeout) 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(), // CallNamedPie can sometimes return false without ever sending any data to
const_cast<Message *>(&messageIn), // the server, try many times before returning.
DWORD(sizeof(Message)), bool result;
messageOut,
DWORD(sizeof(Message)), for (int i = 0; i < 5; i++) {
&bytesTransferred, DWORD bytesTransferred = 0;
timeout); result = CallNamedPipeA(pipeName.c_str(),
const_cast<Message *>(&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): AkVCam::MessageServerPrivate::MessageServerPrivate(MessageServer *self):
self(self) self(self)
{ {
memset(&this->m_overlapped, 0, sizeof(OVERLAPPED));
} }
bool AkVCam::MessageServerPrivate::startReceive(bool wait) bool AkVCam::MessageServerPrivate::startReceive(bool wait)
{ {
AkLogFunction();
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateAboutToStart) 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. // 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 * 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:") // Discretionary ACL
TEXT("(D;OICI;GA;;;BG)") // Deny access to Built-in Guests TEXT("(D;OICI;GA;;;BG)") // Deny access to Built-in Guests
TEXT("(D;OICI;GA;;;AN)") // Deny access to Anonymous Logon TEXT("(D;OICI;GA;;;AN)") // Deny access to Anonymous Logon
@ -216,144 +312,160 @@ bool AkVCam::MessageServerPrivate::startReceive(bool wait)
PSECURITY_DESCRIPTOR securityDescriptor = PSECURITY_DESCRIPTOR securityDescriptor =
LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!securityDescriptor) if (!securityDescriptor) {
goto startReceive_failed; AkLogError() << "Security descriptor not allocated" << std::endl;
return;
}
if (!InitializeSecurityDescriptor(securityDescriptor, if (!InitializeSecurityDescriptor(securityDescriptor,
SECURITY_DESCRIPTOR_REVISION)) SECURITY_DESCRIPTOR_REVISION)) {
goto startReceive_failed; 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, if (!ConvertStringSecurityDescriptorToSecurityDescriptor(descriptor,
SDDL_REVISION_1, SDDL_REVISION_1,
&securityDescriptor, &securityDescriptor,
nullptr)) nullptr)) {
goto startReceive_failed; LocalFree(securityDescriptor);
AkLogError() << "Can't read security descriptor from string: "
<< stringFromError(GetLastError())
<< " (" << GetLastError() << ")"
<< std::endl;
return;
}
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
securityAttributes.lpSecurityDescriptor = securityDescriptor; securityAttributes.lpSecurityDescriptor = securityDescriptor;
securityAttributes.bInheritHandle = TRUE; securityAttributes.bInheritHandle = TRUE;
AkLogDebug() << "Pipe name: " << this->m_pipeName << std::endl;
// 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;
while (this->m_running) { 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 (it == this->m_clientsThreads.end())
if (!ConnectNamedPipe(this->m_pipe, &this->m_overlapped)) break;
result = this->waitResult(&bytesTransferred);
if (result == S_OK) { (*it)->thread->join();
Message message; this->m_clientsThreads.erase(it);
if (this->readMessage(&message)) {
if (this->m_handlers.count(message.messageId))
this->m_handlers[message.messageId](&message);
this->writeMessage(message);
}
} }
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<PipeThread>();
thread->thread =
std::make_shared<std::thread>(&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) AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
AkLogDebug() << "Server stopped." << std::endl;
}
if (this->m_overlapped.hEvent != INVALID_HANDLE_VALUE) { void AkVCam::MessageServerPrivate::processPipe(PipeThreadPtr pipeThread,
CloseHandle(this->m_overlapped.hEvent); HANDLE pipe)
memset(&this->m_overlapped, 0, sizeof(OVERLAPPED)); {
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) { AkLogDebug() << "Closing pipe." << std::endl;
CloseHandle(this->m_pipe); FlushFileBuffers(pipe);
this->m_pipe = INVALID_HANDLE_VALUE; DisconnectNamedPipe(pipe);
} CloseHandle(pipe);
pipeThread->finished = true;
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped) AkLogDebug() << "Pipe thread finished." << std::endl;
} }
void AkVCam::MessageServerPrivate::checkLoop() void AkVCam::MessageServerPrivate::checkLoop()
{ {
AkLogFunction();
while (this->m_running) { while (this->m_running) {
AkLogDebug() << "Waiting for pipe: " << this->m_pipeName << std::endl;
auto result = WaitNamedPipeA(this->m_pipeName.c_str(), NMPWAIT_NOWAIT); auto result = WaitNamedPipeA(this->m_pipeName.c_str(), NMPWAIT_NOWAIT);
if (result if (result
@ -378,62 +490,3 @@ void AkVCam::MessageServerPrivate::checkLoop()
this->m_mutex.unlock(); 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;
}

View file

@ -30,7 +30,7 @@
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/logger.h" #include "VCamUtils/src/logger.h"
#define REG_PREFIX "SOFTWARE\\Webcamoid\\VirtualCamera\\" #define REG_PREFIX "SOFTWARE\\Webcamoid\\VirtualCamera"
namespace AkVCam namespace AkVCam
{ {
@ -42,128 +42,154 @@ namespace AkVCam
bool readValue(const std::string &key, bool readValue(const std::string &key,
DWORD dataTypeFlags, DWORD dataTypeFlags,
PVOID data, PVOID data,
LPDWORD dataSize); LPDWORD dataSize,
void setValue(const std::string &key, bool global);
bool setValue(const std::string &key,
DWORD dataType, DWORD dataType,
LPCSTR data, LPCSTR data,
DWORD dataSize); DWORD dataSize,
bool global);
} }
} }
void AkVCam::Preferences::write(const std::string &key, bool AkVCam::Preferences::write(const std::string &key,
const std::string &value) const std::string &value,
bool global)
{ {
AkLogFunction(); AkLogFunction();
AkLogInfo() << "Writing: " << key << " = " << value << std::endl; 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(); AkLogFunction();
AkLogInfo() << "Writing: " << key << " = " << value << std::endl; AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
setValue(key,
REG_DWORD, return setValue(key,
reinterpret_cast<const char *>(&value), REG_DWORD,
DWORD(sizeof(int))); reinterpret_cast<const char *>(&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(); AkLogFunction();
AkLogInfo() << "Writing: " << key << " = " << value << std::endl; AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
auto val = std::to_string(value); auto val = std::to_string(value);
setValue(key,
REG_SZ, return setValue(key,
val.c_str(), REG_SZ,
DWORD(val.size())); val.c_str(),
DWORD(val.size()),
global);
} }
void AkVCam::Preferences::write(const std::string &key, bool AkVCam::Preferences::write(const std::string &key,
std::vector<std::string> &value) std::vector<std::string> &value,
bool global)
{ {
AkLogFunction(); AkLogFunction();
write(key, join(value, ","));
return write(key, join(value, ","), global);
} }
std::string AkVCam::Preferences::readString(const std::string &key, std::string AkVCam::Preferences::readString(const std::string &key,
const std::string &defaultValue) const std::string &defaultValue,
bool global)
{ {
AkLogFunction(); AkLogFunction();
char value[MAX_PATH]; char value[MAX_PATH];
memset(value, 0, MAX_PATH * sizeof(char)); memset(value, 0, MAX_PATH * sizeof(char));
DWORD valueSize = MAX_PATH; 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 defaultValue;
return {value}; 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(); AkLogFunction();
DWORD value = 0; DWORD value = 0;
DWORD valueSize = sizeof(DWORD); 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 defaultValue;
return int(value); return int(value);
} }
double AkVCam::Preferences::readDouble(const std::string &key, double AkVCam::Preferences::readDouble(const std::string &key,
double defaultValue) double defaultValue,
bool global)
{ {
AkLogFunction(); AkLogFunction();
auto value = readString(key, std::to_string(defaultValue)); auto value = readString(key, std::to_string(defaultValue), global);
std::string::size_type sz; std::string::size_type sz;
return std::stod(value, &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(); 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(); AkLogFunction();
AkLogInfo() << "Deleting " << key << std::endl; AkLogInfo() << "Deleting " << key << std::endl;
HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER;
std::string subKey; std::string subKey;
std::string val; std::string val;
splitSubKey(key, subKey, val); splitSubKey(key, subKey, val);
bool ok = false;
if (val.empty()) { 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 { } else {
HKEY hkey = nullptr; HKEY hkey = nullptr;
auto result = RegOpenKeyExA(HKEY_CURRENT_USER, auto result = RegOpenKeyExA(rootKey,
subKey.c_str(), subKey.c_str(),
0, 0,
KEY_ALL_ACCESS | KEY_WOW64_64KEY, KEY_ALL_ACCESS | KEY_WOW64_64KEY,
&hkey); &hkey);
if (result == ERROR_SUCCESS) { if (result == ERROR_SUCCESS) {
RegDeleteValueA(hkey, val.c_str()); ok = RegDeleteValueA(hkey, val.c_str()) == ERROR_SUCCESS;
RegCloseKey(hkey); RegCloseKey(hkey);
} }
} }
return ok;
} }
void AkVCam::Preferences::move(const std::string &keyFrom, bool AkVCam::Preferences::move(const std::string &keyFrom,
const std::string &keyTo) const std::string &keyTo,
bool global)
{ {
AkLogFunction(); AkLogFunction();
AkLogInfo() << "From: " << keyFrom << std::endl; AkLogInfo() << "From: " << keyFrom << std::endl;
AkLogInfo() << "To: " << keyTo << 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; std::string subKeyFrom = REG_PREFIX "\\" + keyFrom;
HKEY hkeyFrom = nullptr; HKEY hkeyFrom = nullptr;
auto result = RegOpenKeyExA(HKEY_CURRENT_USER, auto result = RegOpenKeyExA(rootKey,
subKeyFrom.c_str(), subKeyFrom.c_str(),
0, 0,
KEY_READ | KEY_WOW64_64KEY, KEY_READ | KEY_WOW64_64KEY,
@ -172,7 +198,7 @@ void AkVCam::Preferences::move(const std::string &keyFrom,
if (result == ERROR_SUCCESS) { if (result == ERROR_SUCCESS) {
std::string subKeyTo = REG_PREFIX "\\" + keyTo; std::string subKeyTo = REG_PREFIX "\\" + keyTo;
HKEY hkeyTo = nullptr; HKEY hkeyTo = nullptr;
result = RegCreateKeyExA(HKEY_CURRENT_USER, result = RegCreateKeyExA(rootKey,
subKeyTo.c_str(), subKeyTo.c_str(),
0, 0,
nullptr, nullptr,
@ -186,26 +212,34 @@ void AkVCam::Preferences::move(const std::string &keyFrom,
result = copyTree(hkeyFrom, nullptr, hkeyTo, KEY_WOW64_64KEY); result = copyTree(hkeyFrom, nullptr, hkeyTo, KEY_WOW64_64KEY);
if (result == ERROR_SUCCESS) if (result == ERROR_SUCCESS)
deleteKey(keyFrom); ok = deleteKey(keyFrom, global);
RegCloseKey(hkeyTo); RegCloseKey(hkeyTo);
} }
RegCloseKey(hkeyFrom); RegCloseKey(hkeyFrom);
} }
return ok;
} }
std::string AkVCam::Preferences::addDevice(const std::string &description) std::string AkVCam::Preferences::addDevice(const std::string &description)
{ {
AkLogFunction(); AkLogFunction();
auto path = createDevicePath(); 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, std::string AkVCam::Preferences::addCamera(const std::string &description,
@ -224,20 +258,28 @@ std::string AkVCam::Preferences::addCamera(const std::string &path,
return {}; return {};
auto path_ = path.empty()? createDevicePath(): path; auto path_ = path.empty()? createDevicePath(): path;
int cameraIndex = readInt("Cameras\\") + 1;
write("Cameras\\size", cameraIndex); if (path.empty())
write("Cameras\\" return {};
+ std::to_string(cameraIndex)
+ "\\description", bool ok = true;
description); int cameraIndex = readInt("Cameras\\", 0, true) + 1;
write("Cameras\\" ok &= write("Cameras\\size", cameraIndex, true);
+ std::to_string(cameraIndex) ok &= write("Cameras\\"
+ "\\path", + std::to_string(cameraIndex)
path_); + "\\description",
write("Cameras\\" description,
+ std::to_string(cameraIndex) true);
+ "\\Formats\\size", ok &= write("Cameras\\"
int(formats.size())); + 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++) { for (size_t i = 0; i < formats.size(); i++) {
auto &format = formats[i]; auto &format = formats[i];
@ -246,43 +288,49 @@ std::string AkVCam::Preferences::addCamera(const std::string &path,
+ "\\Formats\\" + "\\Formats\\"
+ std::to_string(i + 1); + std::to_string(i + 1);
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
write(prefix + "\\format", formatStr); ok &= write(prefix + "\\format", formatStr, true);
write(prefix + "\\width", format.width()); ok &= write(prefix + "\\width", format.width(), true);
write(prefix + "\\height", format.height()); ok &= write(prefix + "\\height", format.height(), true);
write(prefix + "\\fps", format.minimumFrameRate().toString()); 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(); AkLogFunction();
AkLogInfo() << "Device: " << path << std::endl; AkLogInfo() << "Device: " << path << std::endl;
int cameraIndex = cameraFromPath(path); int cameraIndex = cameraFromPath(path);
if (cameraIndex < 0) if (cameraIndex < 0)
return; return false;
cameraSetFormats(size_t(cameraIndex), {}); bool ok = true;
ok &= cameraSetFormats(size_t(cameraIndex), {});
auto nCameras = camerasCount(); 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++) for (auto i = size_t(cameraIndex + 1); i < nCameras; i++)
move("Cameras\\" + std::to_string(i + 1), ok &= move("Cameras\\" + std::to_string(i + 1),
"Cameras\\" + std::to_string(i)); "Cameras\\" + std::to_string(i),
true);
if (nCameras > 1) if (nCameras > 1)
write("Cameras\\size", int(nCameras - 1)); ok &= write("Cameras\\size", int(nCameras - 1), true);
else else
deleteKey("Cameras\\"); ok &= deleteKey("Cameras\\", true);
return ok;
} }
size_t AkVCam::Preferences::camerasCount() size_t AkVCam::Preferences::camerasCount()
{ {
AkLogFunction(); AkLogFunction();
int nCameras = readInt("Cameras\\size"); int nCameras = readInt("Cameras\\size", 0, true);
AkLogInfo() << "Cameras: " << nCameras << std::endl; AkLogInfo() << "Cameras: " << nCameras << std::endl;
return size_t(nCameras); return size_t(nCameras);
@ -359,31 +407,38 @@ std::string AkVCam::Preferences::cameraDescription(size_t cameraIndex)
return readString("Cameras\\" return readString("Cameras\\"
+ std::to_string(cameraIndex + 1) + 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) const std::string &description)
{ {
if (cameraIndex >= camerasCount()) if (cameraIndex >= camerasCount())
return; return false;
write("Cameras\\" + std::to_string(cameraIndex + 1) + "\\description", return write("Cameras\\" + std::to_string(cameraIndex + 1) + "\\description",
description); description,
true);
} }
std::string AkVCam::Preferences::cameraPath(size_t cameraIndex) std::string AkVCam::Preferences::cameraPath(size_t cameraIndex)
{ {
return readString("Cameras\\" return readString("Cameras\\"
+ std::to_string(cameraIndex + 1) + std::to_string(cameraIndex + 1)
+ "\\path"); + "\\path",
{},
true);
} }
size_t AkVCam::Preferences::formatsCount(size_t cameraIndex) size_t AkVCam::Preferences::formatsCount(size_t cameraIndex)
{ {
return size_t(readInt("Cameras\\" return size_t(readInt("Cameras\\"
+ std::to_string(cameraIndex + 1) + std::to_string(cameraIndex + 1)
+ "\\Formats\\size")); + "\\Formats\\size",
0,
true));
} }
AkVCam::VideoFormat AkVCam::Preferences::cameraFormat(size_t cameraIndex, 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) + std::to_string(cameraIndex + 1)
+ "\\Formats\\" + "\\Formats\\"
+ std::to_string(formatIndex + 1); + std::to_string(formatIndex + 1);
auto format = readString(prefix + "\\format"); auto format = readString(prefix + "\\format", {}, true);
auto fourcc = VideoFormat::fourccFromString(format); auto fourcc = VideoFormat::fourccFromString(format);
int width = readInt(prefix + "\\width"); int width = readInt(prefix + "\\width", 0, true);
int height = readInt(prefix + "\\height"); int height = readInt(prefix + "\\height", 0, true);
auto fps = Fraction(readString(prefix + "\\fps")); auto fps = Fraction(readString(prefix + "\\fps", {}, true));
return VideoFormat(fourcc, width, height, {fps}); return VideoFormat(fourcc, width, height, {fps});
} }
@ -418,19 +473,22 @@ std::vector<AkVCam::VideoFormat> AkVCam::Preferences::cameraFormats(size_t camer
return formats; return formats;
} }
void AkVCam::Preferences::cameraSetFormats(size_t cameraIndex, bool AkVCam::Preferences::cameraSetFormats(size_t cameraIndex,
const std::vector<AkVCam::VideoFormat> &formats) const std::vector<AkVCam::VideoFormat> &formats)
{ {
AkLogFunction(); AkLogFunction();
if (cameraIndex >= camerasCount()) if (cameraIndex >= camerasCount())
return; return false;
deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + "\\Formats\\"); bool ok = true;
write("Cameras\\" ok &= deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + "\\Formats\\",
+ std::to_string(cameraIndex + 1) true);
+ "\\Formats\\size", ok &= write("Cameras\\"
int(formats.size())); + std::to_string(cameraIndex + 1)
+ "\\Formats\\size",
int(formats.size()),
true);
for (size_t i = 0; i < formats.size(); i++) { for (size_t i = 0; i < formats.size(); i++) {
auto &format = formats[i]; auto &format = formats[i];
@ -439,14 +497,16 @@ void AkVCam::Preferences::cameraSetFormats(size_t cameraIndex,
+ "\\Formats\\" + "\\Formats\\"
+ std::to_string(i + 1); + std::to_string(i + 1);
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
write(prefix + "\\format", formatStr); ok &= write(prefix + "\\format", formatStr, true);
write(prefix + "\\width", format.width()); ok &= write(prefix + "\\width", format.width(), true);
write(prefix + "\\height", format.height()); ok &= write(prefix + "\\height", format.height(), true);
write(prefix + "\\fps", format.minimumFrameRate().toString()); 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, const AkVCam::VideoFormat &format,
int index) int index)
{ {
@ -456,11 +516,13 @@ void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex,
if (index < 0 || index > int(formats.size())) if (index < 0 || index > int(formats.size()))
index = int(formats.size()); index = int(formats.size());
bool ok = true;
formats.insert(formats.begin() + index, format); formats.insert(formats.begin() + index, format);
write("Cameras\\" ok &= write("Cameras\\"
+ std::to_string(cameraIndex + 1) + std::to_string(cameraIndex + 1)
+ "\\Formats\\size", + "\\Formats\\size",
int(formats.size())); int(formats.size()),
true);
for (size_t i = 0; i < formats.size(); i++) { for (size_t i = 0; i < formats.size(); i++) {
auto &format = formats[i]; auto &format = formats[i];
@ -469,26 +531,30 @@ void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex,
+ "\\Formats\\" + "\\Formats\\"
+ std::to_string(i + 1); + std::to_string(i + 1);
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
write(prefix + "\\format", formatStr); ok &= write(prefix + "\\format", formatStr, true);
write(prefix + "\\width", format.width()); ok &= write(prefix + "\\width", format.width(), true);
write(prefix + "\\height", format.height()); ok &= write(prefix + "\\height", format.height(), true);
write(prefix + "\\fps", format.minimumFrameRate().toString()); 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(); AkLogFunction();
auto formats = cameraFormats(cameraIndex); auto formats = cameraFormats(cameraIndex);
if (index < 0 || index >= int(formats.size())) if (index < 0 || index >= int(formats.size()))
return; return false;
bool ok = true;
formats.erase(formats.begin() + index); formats.erase(formats.begin() + index);
write("Cameras\\" ok &= write("Cameras\\"
+ std::to_string(cameraIndex + 1) + std::to_string(cameraIndex + 1)
+ "\\Formats\\size", + "\\Formats\\size",
int(formats.size())); int(formats.size()),
true);
for (size_t i = 0; i < formats.size(); i++) { for (size_t i = 0; i < formats.size(); i++) {
auto &format = formats[i]; auto &format = formats[i];
@ -497,11 +563,15 @@ void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index)
+ "\\Formats\\" + "\\Formats\\"
+ std::to_string(i + 1); + std::to_string(i + 1);
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
write(prefix + "\\format", formatStr); ok &= write(prefix + "\\format", formatStr, true);
write(prefix + "\\width", format.width()); ok &= write(prefix + "\\width", format.width(), true);
write(prefix + "\\height", format.height()); ok &= write(prefix + "\\height", format.height(), true);
write(prefix + "\\fps", format.minimumFrameRate().toString()); ok &= write(prefix + "\\fps",
format.minimumFrameRate().toString(),
true);
} }
return ok;
} }
int AkVCam::Preferences::cameraControlValue(size_t cameraIndex, int AkVCam::Preferences::cameraControlValue(size_t cameraIndex,
@ -513,15 +583,15 @@ int AkVCam::Preferences::cameraControlValue(size_t cameraIndex,
+ key); + key);
} }
void AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex, bool AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex,
const std::string &key, const std::string &key,
int value) int value)
{ {
write("Cameras\\" return write("Cameras\\"
+ std::to_string(cameraIndex + 1) + std::to_string(cameraIndex + 1)
+ "\\Controls\\" + "\\Controls\\"
+ key, + key,
value); value);
} }
std::string AkVCam::Preferences::picture() std::string AkVCam::Preferences::picture()
@ -529,19 +599,19 @@ std::string AkVCam::Preferences::picture()
return readString("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() 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, 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, bool AkVCam::Preferences::readValue(const std::string &key,
DWORD dataTypeFlags, DWORD dataTypeFlags,
PVOID data, PVOID data,
LPDWORD dataSize) LPDWORD dataSize,
bool global)
{ {
AkLogFunction(); AkLogFunction();
HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER;
std::string subKey; std::string subKey;
std::string val; std::string val;
splitSubKey(key, subKey, val); splitSubKey(key, subKey, val);
AkLogDebug() << "SubKey: " << subKey << std::endl; AkLogDebug() << "SubKey: " << subKey << std::endl;
AkLogDebug() << "Value: " << val << std::endl; AkLogDebug() << "Value: " << val << std::endl;
HKEY hkey = nullptr; HKEY hkey = nullptr;
auto result = RegOpenKeyExA(HKEY_CURRENT_USER, auto result = RegOpenKeyExA(rootKey,
subKey.c_str(), subKey.c_str(),
0, 0,
KEY_READ | KEY_WOW64_64KEY, KEY_READ | KEY_WOW64_64KEY,
@ -594,19 +666,21 @@ bool AkVCam::Preferences::readValue(const std::string &key,
return result == ERROR_SUCCESS; return result == ERROR_SUCCESS;
} }
void AkVCam::Preferences::setValue(const std::string &key, bool AkVCam::Preferences::setValue(const std::string &key,
DWORD dataType, DWORD dataType,
LPCSTR data, LPCSTR data,
DWORD dataSize) DWORD dataSize,
bool global)
{ {
AkLogFunction(); AkLogFunction();
HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER;
std::string subKey; std::string subKey;
std::string val; std::string val;
splitSubKey(key, subKey, val); splitSubKey(key, subKey, val);
AkLogDebug() << "SubKey: " << subKey << std::endl; AkLogDebug() << "SubKey: " << subKey << std::endl;
AkLogDebug() << "Value: " << val << std::endl; AkLogDebug() << "Value: " << val << std::endl;
HKEY hkey = nullptr; HKEY hkey = nullptr;
LONG result = RegCreateKeyExA(HKEY_CURRENT_USER, LONG result = RegCreateKeyExA(rootKey,
subKey.c_str(), subKey.c_str(),
0, 0,
nullptr, nullptr,
@ -617,13 +691,15 @@ void AkVCam::Preferences::setValue(const std::string &key,
nullptr); nullptr);
if (result != ERROR_SUCCESS) if (result != ERROR_SUCCESS)
return; return false;
RegSetValueExA(hkey, result = RegSetValueExA(hkey,
val.c_str(), val.c_str(),
0, 0,
dataType, dataType,
reinterpret_cast<CONST BYTE *>(data), reinterpret_cast<CONST BYTE *>(data),
dataSize); dataSize);
RegCloseKey(hkey); RegCloseKey(hkey);
return result == ERROR_SUCCESS;
} }

View file

@ -31,51 +31,64 @@ namespace AkVCam
namespace Preferences namespace Preferences
{ {
void write(const std::string &key, const std::string &value); bool write(const std::string &key,
void write(const std::string &key, int value); const std::string &value,
void write(const std::string &key, double value); bool global=false);
void write(const std::string &key, std::vector<std::string> &value); 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<std::string> &value,
bool global=false);
std::string readString(const std::string &key, std::string readString(const std::string &key,
const std::string &defaultValue={}); const std::string &defaultValue={},
int readInt(const std::string &key, int defaultValue=0); bool global=false);
double readDouble(const std::string &key, double defaultValue=0.0); int readInt(const std::string &key,
bool readBool(const std::string &key, bool defaultValue=false); int defaultValue=0,
void deleteKey(const std::string &key); bool global=false);
void move(const std::string &keyFrom, const std::string &keyTo); 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 addDevice(const std::string &description);
std::string addCamera(const std::string &description, std::string addCamera(const std::string &description,
const std::vector<VideoFormat> &formats); const std::vector<VideoFormat> &formats);
std::string addCamera(const std::string &path, std::string addCamera(const std::string &path,
const std::string &description, const std::string &description,
const std::vector<VideoFormat> &formats); const std::vector<VideoFormat> &formats);
void removeCamera(const std::string &path); bool removeCamera(const std::string &path);
size_t camerasCount(); size_t camerasCount();
std::string createDevicePath(); std::string createDevicePath();
int cameraFromCLSID(const CLSID &clsid); int cameraFromCLSID(const CLSID &clsid);
int cameraFromPath(const std::string &path); int cameraFromPath(const std::string &path);
bool cameraExists(const std::string &path); bool cameraExists(const std::string &path);
std::string cameraDescription(size_t cameraIndex); std::string cameraDescription(size_t cameraIndex);
void cameraSetDescription(size_t cameraIndex, bool cameraSetDescription(size_t cameraIndex,
const std::string &description); const std::string &description);
std::string cameraPath(size_t cameraIndex); std::string cameraPath(size_t cameraIndex);
size_t formatsCount(size_t cameraIndex); size_t formatsCount(size_t cameraIndex);
VideoFormat cameraFormat(size_t cameraIndex, size_t formatIndex); VideoFormat cameraFormat(size_t cameraIndex, size_t formatIndex);
std::vector<VideoFormat> cameraFormats(size_t cameraIndex); std::vector<VideoFormat> cameraFormats(size_t cameraIndex);
void cameraSetFormats(size_t cameraIndex, bool cameraSetFormats(size_t cameraIndex,
const std::vector<VideoFormat> &formats); const std::vector<VideoFormat> &formats);
void cameraAddFormat(size_t cameraIndex, bool cameraAddFormat(size_t cameraIndex,
const VideoFormat &format, const VideoFormat &format,
int index); int index);
void cameraRemoveFormat(size_t cameraIndex, int index); bool cameraRemoveFormat(size_t cameraIndex, int index);
int cameraControlValue(size_t cameraIndex, int cameraControlValue(size_t cameraIndex,
const std::string &key); const std::string &key);
void cameraSetControlValue(size_t cameraIndex, bool cameraSetControlValue(size_t cameraIndex,
const std::string &key, const std::string &key,
int value); int value);
std::string picture(); std::string picture();
void setPicture(const std::string &picture); bool setPicture(const std::string &picture);
int logLevel(); int logLevel();
void setLogLevel(int logLevel); bool setLogLevel(int logLevel);
} }
} }

View file

@ -130,7 +130,7 @@ bool AkVCam::SharedMemory::open(size_t pageSize, OpenMode mode)
AkLogError() << "Error opening shared memory (" AkLogError() << "Error opening shared memory ("
<< this->d->m_name << this->d->m_name
<< "): " << "): "
<< errorToString(GetLastError()) << stringFromError(GetLastError())
<< " (" << GetLastError() << ")" << " (" << GetLastError() << ")"
<< std::endl; << std::endl;

View file

@ -27,6 +27,7 @@
#include <wincodec.h> #include <wincodec.h>
#include "utils.h" #include "utils.h"
#include "messagecommons.h"
#include "VCamUtils/src/utils.h" #include "VCamUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.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; 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; CHAR *errorStr = nullptr;
auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
@ -170,6 +180,32 @@ std::string AkVCam::createClsidStrFromStr(const std::string &str)
return stringFromIid(createClsidFromStr(str)); return stringFromIid(createClsidFromStr(str));
} }
std::string AkVCam::stringFromMessageId(uint32_t messageId)
{
static const std::map<uint32_t, std::string> 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) std::string AkVCam::stringFromIid(const IID &iid)
{ {
LPWSTR strIID = nullptr; LPWSTR strIID = nullptr;

View file

@ -39,9 +39,11 @@ namespace AkVCam
std::string moduleFileName(HINSTANCE hinstDLL); std::string moduleFileName(HINSTANCE hinstDLL);
std::string dirname(const std::string &path); std::string dirname(const std::string &path);
bool fileExists(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); CLSID createClsidFromStr(const std::string &str);
std::string createClsidStrFromStr(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 stringFromIid(const IID &iid);
std::string stringFromResult(HRESULT result); std::string stringFromResult(HRESULT result);
std::string stringFromClsid(const CLSID &clsid); std::string stringFromClsid(const CLSID &clsid);

View file

@ -66,6 +66,7 @@ namespace AkVCam
~IpcBridgePrivate(); ~IpcBridgePrivate();
inline const std::vector<DeviceControl> &controls() const; inline const std::vector<DeviceControl> &controls() const;
void updateDevices(bool propagate);
void updateDeviceSharedProperties(); void updateDeviceSharedProperties();
void updateDeviceSharedProperties(const std::string &deviceId, void updateDeviceSharedProperties(const std::string &deviceId,
const std::string &owner); const std::string &owner);
@ -74,13 +75,21 @@ namespace AkVCam
// Message handling methods // Message handling methods
void isAlive(Message *message); void isAlive(Message *message);
void deviceUpdate(Message *message);
void frameReady(Message *message); void frameReady(Message *message);
void pictureUpdated(Message *message); void pictureUpdated(Message *message);
void deviceUpdate(Message *message);
void listenerAdd(Message *message);
void listenerRemove (Message *message);
void setBroadcasting(Message *message); void setBroadcasting(Message *message);
void controlsUpdated(Message *message); void controlsUpdated(Message *message);
void listenerAdd(Message *message);
void listenerRemove (Message *message);
bool isRoot() const;
int sudo(const std::vector<std::string> &parameters,
const std::string &directory={},
bool show=false);
std::string manager() const;
std::string alternativeManager() const;
std::string alternativePlugin() const;
}; };
static const int maxFrameWidth = 1920; static const int maxFrameWidth = 1920;
@ -97,12 +106,17 @@ AkVCam::IpcBridge::IpcBridge()
AkVCam::Logger::setLogLevel(loglevel); AkVCam::Logger::setLogLevel(loglevel);
this->d->m_mainServer.start(); this->d->m_mainServer.start();
this->registerPeer(); this->registerPeer();
this->d->updateDeviceSharedProperties();
this->d->m_mainServer.connectPipeStateChanged(this,
&IpcBridgePrivate::pipeStateChanged);
} }
AkVCam::IpcBridge::~IpcBridge() AkVCam::IpcBridge::~IpcBridge()
{ {
this->d->m_mainServer.disconnectPipeStateChanged(this,
&IpcBridgePrivate::pipeStateChanged);
this->unregisterPeer(); this->unregisterPeer();
this->d->m_mainServer.stop(true); this->d->m_mainServer.stop();
delete this->d; delete this->d;
} }
@ -113,6 +127,7 @@ std::string AkVCam::IpcBridge::picture() const
void AkVCam::IpcBridge::setPicture(const std::string &picture) void AkVCam::IpcBridge::setPicture(const std::string &picture)
{ {
AkLogFunction();
Preferences::setPicture(picture); Preferences::setPicture(picture);
Message message; Message message;
message.messageId = AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED; message.messageId = AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED;
@ -131,6 +146,7 @@ int AkVCam::IpcBridge::logLevel() const
void AkVCam::IpcBridge::setLogLevel(int logLevel) void AkVCam::IpcBridge::setLogLevel(int logLevel)
{ {
AkLogFunction();
Preferences::setLogLevel(logLevel); Preferences::setLogLevel(logLevel);
Logger::setLogLevel(logLevel); Logger::setLogLevel(logLevel);
} }
@ -139,6 +155,11 @@ bool AkVCam::IpcBridge::registerPeer()
{ {
AkLogFunction(); AkLogFunction();
if (!this->d->m_portName.empty())
return true;
AkLogDebug() << "Requesting port." << std::endl;
Message message; Message message;
message.messageId = AKVCAM_ASSISTANT_MSG_REQUEST_PORT; message.messageId = AKVCAM_ASSISTANT_MSG_REQUEST_PORT;
message.dataSize = sizeof(MsgRequestPort); message.dataSize = sizeof(MsgRequestPort);
@ -149,10 +170,18 @@ bool AkVCam::IpcBridge::registerPeer()
return false; return false;
std::string portName(requestData->port); 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; auto pipeName = "\\\\.\\pipe\\" + portName;
this->d->m_messageServer.setPipeName(pipeName); this->d->m_messageServer.setPipeName(pipeName);
this->d->m_messageServer.setHandlers(this->d->m_messageHandlers); this->d->m_messageServer.setHandlers(this->d->m_messageHandlers);
AkLogInfo() << "Recommended port name: " << portName << std::endl;
if (!this->d->m_messageServer.start()) { if (!this->d->m_messageServer.start()) {
AkLogError() << "Can't start message server" << std::endl; AkLogError() << "Can't start message server" << std::endl;
@ -160,6 +189,8 @@ bool AkVCam::IpcBridge::registerPeer()
return false; return false;
} }
AkLogInfo() << "Registering port: " << portName << std::endl;
message.clear(); message.clear();
message.messageId = AKVCAM_ASSISTANT_MSG_ADD_PORT; message.messageId = AKVCAM_ASSISTANT_MSG_ADD_PORT;
message.dataSize = sizeof(MsgAddPort); message.dataSize = sizeof(MsgAddPort);
@ -171,17 +202,17 @@ bool AkVCam::IpcBridge::registerPeer()
pipeName.c_str(), pipeName.c_str(),
(std::min<size_t>)(pipeName.size(), MAX_STRING)); (std::min<size_t>)(pipeName.size(), MAX_STRING));
AkLogInfo() << "Registering port name: " << portName << std::endl;
if (!MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME, if (!MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME,
&message)) { &message)) {
this->d->m_messageServer.stop(true); AkLogError() << "Failed registering port." << std::endl;
this->d->m_messageServer.stop();
return false; return false;
} }
if (!addData->status) { if (!addData->status) {
this->d->m_messageServer.stop(true); AkLogError() << "Failed registering port." << std::endl;
this->d->m_messageServer.stop();
return false; return false;
} }
@ -191,6 +222,9 @@ bool AkVCam::IpcBridge::registerPeer()
this->d->m_portName = portName; this->d->m_portName = portName;
AkLogInfo() << "Peer registered as " << portName << std::endl; AkLogInfo() << "Peer registered as " << portName << std::endl;
AkLogInfo() << "SUCCESSFUL" << std::endl;
this->d->updateDevices(false);
return true; return true;
} }
@ -201,7 +235,6 @@ void AkVCam::IpcBridge::unregisterPeer()
if (this->d->m_portName.empty()) if (this->d->m_portName.empty())
return; return;
this->d->m_sharedMemory.setName({});
Message message; Message message;
message.messageId = AKVCAM_ASSISTANT_MSG_REMOVE_PORT; message.messageId = AKVCAM_ASSISTANT_MSG_REMOVE_PORT;
message.dataSize = sizeof(MsgRemovePort); message.dataSize = sizeof(MsgRemovePort);
@ -211,15 +244,17 @@ void AkVCam::IpcBridge::unregisterPeer()
(std::min<size_t>)(this->d->m_portName.size(), MAX_STRING)); (std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME, MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME,
&message); &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(); this->d->m_portName.clear();
} }
std::vector<std::string> AkVCam::IpcBridge::devices() const std::vector<std::string> AkVCam::IpcBridge::devices() const
{ {
AkLogFunction(); AkLogFunction();
auto nCameras = Preferences::camerasCount();
std::vector<std::string> devices; std::vector<std::string> devices;
auto nCameras = Preferences::camerasCount();
AkLogInfo() << "Devices:" << std::endl; AkLogInfo() << "Devices:" << std::endl;
for (size_t i = 0; i < nCameras; i++) { for (size_t i = 0; i < nCameras; i++) {
@ -285,9 +320,11 @@ std::vector<AkVCam::VideoFormat> AkVCam::IpcBridge::formats(const std::string &d
return Preferences::cameraFormats(size_t(cameraIndex)); return Preferences::cameraFormats(size_t(cameraIndex));
} }
void AkVCam::IpcBridge::setFormats(const std::string &deviceId, void AkVCam::IpcBridge::setFormats(const std::string &deviceId,
const std::vector<VideoFormat> &formats) const std::vector<VideoFormat> &formats)
{ {
AkLogFunction();
auto cameraIndex = Preferences::cameraFromPath(deviceId); auto cameraIndex = Preferences::cameraFromPath(deviceId);
if (cameraIndex >= 0) if (cameraIndex >= 0)
@ -425,10 +462,22 @@ std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
if (pluginPath.empty()) if (pluginPath.empty())
return {}; return {};
std::vector<std::string> plugins;
// First check for the existence of the main plugin binary.
auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll"; auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll";
AkLogDebug() << "Plugin binary: " << path << std::endl; 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 {}; return {};
std::vector<uint64_t> pids; std::vector<uint64_t> pids;
@ -449,8 +498,18 @@ std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
| PROCESS_VM_READ, | PROCESS_VM_READ,
FALSE, FALSE,
process[i]); 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]; HMODULE modules[nElements];
memset(modules, 0, nElements * sizeof(HMODULE)); memset(modules, 0, nElements * sizeof(HMODULE));
@ -470,7 +529,11 @@ std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
modules[j], modules[j],
moduleName, moduleName,
MAX_PATH)) { MAX_PATH)) {
if (moduleName == path) { auto it = std::find(plugins.begin(),
plugins.end(),
moduleName);
if (it != plugins.end()) {
auto pidsIt = std::find(pids.begin(), auto pidsIt = std::find(pids.begin(),
pids.end(), pids.end(),
process[i]); process[i]);
@ -513,11 +576,15 @@ std::string AkVCam::IpcBridge::clientExe(uint64_t pid) const
std::string AkVCam::IpcBridge::addDevice(const std::string &description) std::string AkVCam::IpcBridge::addDevice(const std::string &description)
{ {
AkLogFunction();
return Preferences::addDevice(description); return Preferences::addDevice(description);
} }
void AkVCam::IpcBridge::removeDevice(const std::string &deviceId) void AkVCam::IpcBridge::removeDevice(const std::string &deviceId)
{ {
AkLogFunction();
Preferences::removeCamera(deviceId); Preferences::removeCamera(deviceId);
} }
@ -567,12 +634,24 @@ void AkVCam::IpcBridge::updateDevices()
RegisterServerFunc(GetProcAddress(hmodule, "DllRegisterServer")); RegisterServerFunc(GetProcAddress(hmodule, "DllRegisterServer"));
if (registerServer) { 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; if (!fileExists(lockFileName)) {
message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE; std::ofstream lockFile;
message.dataSize = 0; lockFile.open(lockFileName);
this->d->m_mainServer.sendMessage(&message); lockFile << std::endl;
lockFile.close();
auto altManager = this->d->alternativeManager();
if (!altManager.empty())
this->d->sudo({altManager, "update"});
DeleteFileA(lockFileName.c_str());
}
} else { } else {
AkLogError() << "Can't locate DllRegisterServer function." << std::endl; AkLogError() << "Can't locate DllRegisterServer function." << std::endl;
} }
@ -626,9 +705,6 @@ bool AkVCam::IpcBridge::deviceStart(const std::string &deviceId,
return false; return false;
} }
if (!data->status)
return false;
this->d->m_broadcasting.push_back(deviceId); this->d->m_broadcasting.push_back(deviceId);
return true; return true;
@ -746,15 +822,42 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
return data->status; return data->status;
} }
bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
{
static const std::vector<std::string> 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<std::string> arguments;
for (int i = 0; i < argc; i++)
arguments.push_back(argv[i]);
return this->d->sudo(arguments);
}
AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self): AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
self(self) self(self)
{ {
this->m_mainServer.setPipeName("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME); this->m_mainServer.setPipeName("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME);
this->m_mainServer.setMode(MessageServer::ServerModeSend); this->m_mainServer.setMode(MessageServer::ServerModeSend);
this->m_mainServer.connectPipeStateChanged(this,
&IpcBridgePrivate::pipeStateChanged);
this->updateDeviceSharedProperties();
this->m_messageHandlers = std::map<uint32_t, MessageHandler> { this->m_messageHandlers = std::map<uint32_t, MessageHandler> {
{AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) }, {AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) },
{AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) }, {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) },
@ -769,7 +872,6 @@ AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
AkVCam::IpcBridgePrivate::~IpcBridgePrivate() AkVCam::IpcBridgePrivate::~IpcBridgePrivate()
{ {
this->m_mainServer.stop(true);
} }
const std::vector<AkVCam::DeviceControl> &AkVCam::IpcBridgePrivate::controls() const const std::vector<AkVCam::DeviceControl> &AkVCam::IpcBridgePrivate::controls() const
@ -797,8 +899,22 @@ const std::vector<AkVCam::DeviceControl> &AkVCam::IpcBridgePrivate::controls() c
return controls; 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<MsgDevicesUpdated>(&message);
data->propagate = propagate;
this->m_mainServer.sendMessage(&message);
}
void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties() void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties()
{ {
AkLogFunction();
for (size_t i = 0; i < Preferences::camerasCount(); i++) { for (size_t i = 0; i < Preferences::camerasCount(); i++) {
auto path = Preferences::cameraPath(i); auto path = Preferences::cameraPath(i);
Message message; Message message;
@ -817,6 +933,8 @@ void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties()
void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties(const std::string &deviceId, void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties(const std::string &deviceId,
const std::string &owner) const std::string &owner)
{ {
AkLogFunction();
if (owner.empty()) { if (owner.empty()) {
this->m_devices[deviceId] = {SharedMemory(), Mutex()}; this->m_devices[deviceId] = {SharedMemory(), Mutex()};
} else { } else {
@ -865,6 +983,19 @@ void AkVCam::IpcBridgePrivate::isAlive(Message *message)
data->alive = true; data->alive = true;
} }
void AkVCam::IpcBridgePrivate::deviceUpdate(Message *message)
{
UNUSED(message);
AkLogFunction();
std::vector<std::string> 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) void AkVCam::IpcBridgePrivate::frameReady(Message *message)
{ {
AkLogFunction(); AkLogFunction();
@ -899,39 +1030,6 @@ void AkVCam::IpcBridgePrivate::pictureUpdated(Message *message)
AKVCAM_EMIT(this->self, PictureChanged, std::string(data->picture)) AKVCAM_EMIT(this->self, PictureChanged, std::string(data->picture))
} }
void AkVCam::IpcBridgePrivate::deviceUpdate(Message *message)
{
UNUSED(message);
AkLogFunction();
std::vector<std::string> 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<MsgListeners>(message);
AKVCAM_EMIT(this->self,
ListenerAdded,
std::string(data->device),
std::string(data->listener))
}
void AkVCam::IpcBridgePrivate::listenerRemove(Message *message)
{
AkLogFunction();
auto data = messageData<MsgListeners>(message);
AKVCAM_EMIT(this->self,
ListenerRemoved,
std::string(data->device),
std::string(data->listener))
}
void AkVCam::IpcBridgePrivate::setBroadcasting(Message *message) void AkVCam::IpcBridgePrivate::setBroadcasting(Message *message)
{ {
AkLogFunction(); AkLogFunction();
@ -954,9 +1052,181 @@ void AkVCam::IpcBridgePrivate::controlsUpdated(Message *message)
std::map<std::string, int> controls; std::map<std::string, int> controls;
for (auto &control: this->controls()) for (auto &control: this->controls()) {
controls[control.id] = controls[control.id] =
Preferences::cameraControlValue(size_t(cameraIndex), control.id); Preferences::cameraControlValue(size_t(cameraIndex), control.id);
AkLogDebug() << control.id << ": " << controls[control.id] << std::endl;
}
AKVCAM_EMIT(this->self, ControlsChanged, deviceId, controls) AKVCAM_EMIT(this->self, ControlsChanged, deviceId, controls)
} }
void AkVCam::IpcBridgePrivate::listenerAdd(Message *message)
{
AkLogFunction();
auto data = messageData<MsgListeners>(message);
AKVCAM_EMIT(this->self,
ListenerAdded,
std::string(data->device),
std::string(data->listener))
}
void AkVCam::IpcBridgePrivate::listenerRemove(Message *message)
{
AkLogFunction();
auto data = messageData<MsgListeners>(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<std::string> &parameters,
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 &param: 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();
}

View file

@ -150,6 +150,28 @@ IReferenceClock *AkVCam::BaseFilter::referenceClock() const
return this->d->m_referenceClock; 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) HRESULT AkVCam::BaseFilter::QueryInterface(const IID &riid, void **ppvObject)
{ {
AkLogFunction(); AkLogFunction();

View file

@ -47,6 +47,8 @@ namespace AkVCam
static BaseFilter *create(const GUID &clsid); static BaseFilter *create(const GUID &clsid);
IFilterGraph *filterGraph() const; IFilterGraph *filterGraph() const;
IReferenceClock *referenceClock() const; IReferenceClock *referenceClock() const;
std::string deviceId();
std::string broadcaster();
DECLARE_IMEDIAFILTER_NQ DECLARE_IMEDIAFILTER_NQ

View file

@ -111,6 +111,20 @@ AkVCam::Pin::Pin(BaseFilter *baseFilter,
this->d->m_pinId = ss.str(); this->d->m_pinId = ss.str();
this->d->m_mediaTypes = new AkVCam::EnumMediaTypes(formats); this->d->m_mediaTypes = new AkVCam::EnumMediaTypes(formats);
this->d->m_mediaTypes->AddRef(); 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(); auto picture = Preferences::picture();
if (!picture.empty()) if (!picture.empty())
@ -316,9 +330,18 @@ void AkVCam::Pin::setControls(const std::map<std::string, int> &controls)
return; return;
} }
for (auto &control: controls)
AkLogDebug() << control.first << ": " << control.second << std::endl;
this->d->m_controls = controls; this->d->m_controls = controls;
this->d->m_controlsMutex.unlock(); this->d->m_controlsMutex.unlock();
this->d->updateTestFrame(); 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 bool AkVCam::Pin::horizontalFlip() const
@ -907,6 +930,7 @@ HRESULT AkVCam::PinPrivate::sendFrame()
void AkVCam::PinPrivate::updateTestFrame() void AkVCam::PinPrivate::updateTestFrame()
{ {
AkLogFunction();
auto frame = this->applyAdjusts(this->m_testFrame); auto frame = this->applyAdjusts(this->m_testFrame);
if (frame.format().size() < 1) if (frame.format().size() < 1)

View file

@ -25,7 +25,7 @@ isEmpty(DSHOW_PLUGIN_ASSISTANT_DESCRIPTION):
isEmpty(DSHOW_PLUGIN_DESCRIPTION): isEmpty(DSHOW_PLUGIN_DESCRIPTION):
DSHOW_PLUGIN_DESCRIPTION = "Webcamoid Virtual Camera" DSHOW_PLUGIN_DESCRIPTION = "Webcamoid Virtual Camera"
isEmpty(DSHOW_PLUGIN_DESCRIPTION_EXT): 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): isEmpty(DSHOW_PLUGIN_DEVICE_PREFIX):
DSHOW_PLUGIN_DEVICE_PREFIX = AkVCamVideoDevice DSHOW_PLUGIN_DEVICE_PREFIX = AkVCamVideoDevice
isEmpty(DSHOW_PLUGIN_VENDOR): isEmpty(DSHOW_PLUGIN_VENDOR):

View file

@ -56,14 +56,15 @@ Component.prototype.createOperations = function()
"/Library/CoreMediaIO/Plug-Ins/DAL/@Name@.plugin"); "/Library/CoreMediaIO/Plug-Ins/DAL/@Name@.plugin");
// Set assistant daemon. // 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" let plistContents = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
+ "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
+ "<plist version=\"1.0\">\n" + "<plist version=\"1.0\">\n"
+ " <dict>\n" + " <dict>\n"
+ " <key>Label</key>\n" + " <key>Label</key>\n"
+ " <string>org.webcamoid.cmio.AkVCam.Assistant</string>\n" + " <string>" + service + "</string>\n"
+ " <key>ProgramArguments</key>\n" + " <key>ProgramArguments</key>\n"
+ " <array>\n" + " <array>\n"
+ " <string>" + " <string>"
@ -75,7 +76,7 @@ Component.prototype.createOperations = function()
+ " </array>\n" + " </array>\n"
+ " <key>MachServices</key>\n" + " <key>MachServices</key>\n"
+ " <dict>\n" + " <dict>\n"
+ " <key>org.webcamoid.cmio.AkVCam.Assistant</key>\n" + " <key>" + service + "</key>\n"
+ " <true/>\n" + " <true/>\n"
+ " </dict>\n" + " </dict>\n"
+ " <key>StandardOutPath</key>\n" + " <key>StandardOutPath</key>\n"
@ -88,10 +89,14 @@ Component.prototype.createOperations = function()
component.addElevatedOperation("AppendFile", daemonPlist, plistContents); component.addElevatedOperation("AppendFile", daemonPlist, plistContents);
// Load assistant daemon. // Load assistant daemon.
if (installer.isUninstaller())
component.addElevatedOperation("Execute",
"launchctl", "enable", "system/" + service);
component.addElevatedOperation("Execute", component.addElevatedOperation("Execute",
"launchctl", "load", "-w", daemonPlist, "launchctl", "bootstrap", "system", daemonPlist,
"UNDOEXECUTE", "UNDOEXECUTE",
"launchctl", "unload", "-w", daemonPlist); "launchctl", "bootout", "system", daemonPlist);
if (installer.isUninstaller()) if (installer.isUninstaller())
component.addElevatedOperation("Delete", daemonPlist); component.addElevatedOperation("Delete", daemonPlist);

View file

@ -20,10 +20,12 @@ Component.prototype.createOperations = function()
+ "/" + "/"
+ archs[i] + archs[i]
+ "/AkVCamManager.exe"; + "/AkVCamManager.exe";
component.addOperation("Execute", component.addElevateOperation("Execute",
managerPath, "remove-devices"); managerPath,
"remove-devices");
component.addElevatedOperation("Execute", component.addElevatedOperation("Execute",
managerPath, "update"); managerPath,
"update");
} }
let assistantPath = let assistantPath =