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 <chrono>
#include <codecvt>
#include <csignal>
#include <cstring>
#include <iostream>
#include <functional>
#include <locale>
#include <sstream>
#include <thread>
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#endif
#include "cmdparser.h"
#include "VCamUtils/src/ipcbridge.h"
@ -99,6 +107,8 @@ namespace AkVCam {
const StringVector &args);
int showSupportedFormats(const StringMap &flags,
const StringVector &args);
int showDefaultFormat(const StringMap &flags,
const StringVector &args);
int showFormats(const StringMap &flags, const StringVector &args);
int addFormat(const StringMap &flags, const StringVector &args);
int removeFormat(const StringMap &flags, const StringVector &args);
@ -106,6 +116,7 @@ namespace AkVCam {
int update(const StringMap &flags, const StringVector &args);
int loadSettings(const StringMap &flags, const StringVector &args);
int stream(const StringMap &flags, const StringVector &args);
int listenEvents(const StringMap &flags, const StringVector &args);
int showControls(const StringMap &flags, const StringVector &args);
int readControl(const StringMap &flags, const StringVector &args);
int writeControls(const StringMap &flags, const StringVector &args);
@ -114,6 +125,7 @@ namespace AkVCam {
int logLevel(const StringMap &flags, const StringVector &args);
int setLogLevel(const StringMap &flags, const StringVector &args);
int showClients(const StringMap &flags, const StringVector &args);
int dumpInfo(const StringMap &flags, const StringVector &args);
void loadGenerals(Settings &settings);
VideoFormatMatrix readFormats(Settings &settings);
std::vector<VideoFormat> readFormat(Settings &settings);
@ -131,6 +143,7 @@ namespace AkVCam {
};
std::string operator *(const std::string &str, size_t n);
std::string operator *(size_t n, const std::string &str);
}
AkVCam::CmdParser::CmdParser()
@ -141,6 +154,9 @@ AkVCam::CmdParser::CmdParser()
this->addFlags("",
{"-h", "--help"},
"Show help.");
this->addFlags("",
{"-v", "--version"},
"Show program version.");
this->addFlags("",
{"-p", "--parseable"},
"Show parseable output.");
@ -178,6 +194,16 @@ AkVCam::CmdParser::CmdParser()
this->addFlags("supported-formats",
{"-o", "--output"},
"Show supported output formats.");
this->addCommand("default-format",
"",
"Default device format.",
AKVCAM_BIND_FUNC(CmdParserPrivate::showDefaultFormat));
this->addFlags("default-format",
{"-i", "--input"},
"Default input format.");
this->addFlags("default-format",
{"-o", "--output"},
"Default output format.");
this->addCommand("formats",
"DEVICE",
"Show device formats.",
@ -210,6 +236,10 @@ AkVCam::CmdParser::CmdParser()
"DEVICE FORMAT WIDTH HEIGHT",
"Read frames from stdin and send them to the device.",
AKVCAM_BIND_FUNC(CmdParserPrivate::stream));
this->addCommand("listen-events",
"",
"Keep the manager running and listening to global events.",
AKVCAM_BIND_FUNC(CmdParserPrivate::listenEvents));
this->addCommand("controls",
"DEVICE",
"Show device controls.",
@ -263,6 +293,10 @@ AkVCam::CmdParser::CmdParser()
"",
"Show clients using the camera.",
AKVCAM_BIND_FUNC(CmdParserPrivate::showClients));
this->addCommand("dump",
"",
"Show all information in a parseable XML format.",
AKVCAM_BIND_FUNC(CmdParserPrivate::dumpInfo));
}
AkVCam::CmdParser::~CmdParser()
@ -279,8 +313,10 @@ int AkVCam::CmdParser::parse(int argc, char **argv)
for (int i = 1; i < argc; i++) {
std::string arg = argv[i];
char *p = nullptr;
strtod(arg.c_str(), &p);
if (arg[0] == '-') {
if (arg[0] == '-' && strlen(p) != 0) {
auto flag = this->d->parserFlag(command->flags, arg);
if (!flag) {
@ -338,6 +374,9 @@ int AkVCam::CmdParser::parse(int argc, char **argv)
}
}
if (this->d->m_ipcBridge.needsRoot(command->command))
return this->d->m_ipcBridge.sudo(argc, argv);
return command->func(flags, arguments);
}
@ -638,6 +677,12 @@ int AkVCam::CmdParserPrivate::defaultHandler(const StringMap &flags,
if (flags.empty() || this->containsFlag(flags, "", "-h"))
return this->showHelp(flags, args);
if (this->containsFlag(flags, "", "-v")) {
std::cout << COMMONS_VERSION << std::endl;
return 0;
}
if (this->containsFlag(flags, "", "-p"))
this->m_parseable = true;
@ -864,6 +909,21 @@ int AkVCam::CmdParserPrivate::showSupportedFormats(const StringMap &flags,
return 0;
}
int AkVCam::CmdParserPrivate::showDefaultFormat(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(args);
auto type =
this->containsFlag(flags, "default-format", "-i")?
IpcBridge::StreamTypeInput:
IpcBridge::StreamTypeOutput;
auto format = this->m_ipcBridge.defaultPixelFormat(type);
std::cout << VideoFormat::stringFromFourcc(format) << std::endl;
return 0;
}
int AkVCam::CmdParserPrivate::showFormats(const StringMap &flags,
const StringVector &args)
{
@ -948,7 +1008,8 @@ int AkVCam::CmdParserPrivate::addFormat(const StringMap &flags,
return -1;
}
auto formats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
auto formats =
this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
auto fit = std::find(formats.begin(), formats.end(), format);
if (fit == formats.end()) {
@ -1141,7 +1202,8 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
return -1;
}
auto formats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
auto formats =
this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
auto fit = std::find(formats.begin(), formats.end(), format);
if (fit == formats.end()) {
@ -1186,6 +1248,11 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
AkVCam::VideoFrame frame(fmt);
size_t bufferSize = 0;
#ifdef _WIN32
// Set std::cin in binary mode.
_setmode(_fileno(stdin), _O_BINARY);
#endif
do {
std::cin.read(reinterpret_cast<char *>(frame.data().data()
+ bufferSize),
@ -1203,6 +1270,42 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
return 0;
}
int AkVCam::CmdParserPrivate::listenEvents(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
UNUSED(args);
auto serverStateChanged = [] (void *, IpcBridge::ServerState state) {
if (state == IpcBridge::ServerStateAvailable)
std::cout << "ServerAvailable" << std::endl;
else
std::cout << "ServerGone" << std::endl;
};
auto devicesChanged = [] (void *, const std::vector<std::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,
const StringVector &args)
{
@ -1315,7 +1418,10 @@ int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
if (this->m_parseable)
std::cout << control.menu[i] << std::endl;
else
std::cout << i << ": " << control.menu[i] << std::endl;
std::cout << i
<< ": "
<< control.menu[i]
<< std::endl;
}
}
@ -1582,6 +1688,189 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
return 0;
}
int AkVCam::CmdParserPrivate::dumpInfo(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
UNUSED(args);
static const auto indent = 4 * std::string(" ");
std::cout << "<?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)
{
settings.beginGroup("General");
@ -1735,7 +2024,8 @@ void AkVCam::CmdParserPrivate::createDevice(Settings &settings,
}
auto deviceId = this->m_ipcBridge.addDevice(description);
auto supportedFormats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
auto supportedFormats =
this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
for (auto &format: formats) {
auto it = std::find(supportedFormats.begin(),
@ -1781,3 +2071,13 @@ std::string AkVCam::operator *(const std::string &str, size_t n)
return ss.str();
}
std::string AkVCam::operator *(size_t n, const std::string &str)
{
std::stringstream ss;
for (size_t i = 0; i < n; i++)
ss << str;
return ss.str();
}

View file

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

View file

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

View file

@ -17,8 +17,9 @@
* Web-Site: http://webcamoid.github.io/
*/
#include <map>
#include <algorithm>
#include <map>
#include <ostream>
#include "videoformat.h"
#include "../utils.h"
@ -421,3 +422,38 @@ size_t AkVCam::VideoFormatGlobals::byplNV(size_t plane, size_t width)
return align32(size_t(width));
}
std::ostream &operator <<(std::ostream &os, const AkVCam::VideoFormat &format)
{
auto formatStr = AkVCam::VideoFormat::stringFromFourcc(format.fourcc());
os << "VideoFormat("
<< formatStr
<< ' '
<< format.width()
<< 'x'
<< format.height()
<< ' '
<< format.minimumFrameRate()
<< ')';
return os;
}
std::ostream &operator <<(std::ostream &os, const AkVCam::VideoFormats &formats)
{
bool writeComma = false;
os << "VideoFormats(";
for (auto &format: formats) {
if (writeComma)
os << ", ";
os << format;
writeComma = true;
}
os << ')';
return os;
}

View file

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

View file

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

View file

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

View file

@ -44,12 +44,20 @@ std::string AkVCam::replace(const std::string &str,
const std::string &from,
const std::string &to)
{
auto newStr = str;
std::string newStr;
for (auto pos = newStr.find(from);
pos != std::string::npos;
pos = newStr.find(from))
newStr.replace(pos, from.size(), to);
for (size_t i = 0; i < str.size(); i++) {
auto pos = str.find(from, i);
if (pos == std::string::npos) {
newStr += str.substr(i);
break;
} else {
newStr += str.substr(i, pos - i) + to;
i = pos + from.size() - 1;
}
}
return newStr;
}

View file

@ -26,7 +26,6 @@
#include "assistant.h"
#include "assistantglobals.h"
#include "PlatformUtils/src/preferences.h"
#include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h"
@ -63,11 +62,11 @@ namespace AkVCam
bool startTimer();
void stopTimer();
static void timerTimeout(CFRunLoopTimerRef timer, void *info);
void releaseDevicesFromPeer(const std::string &portName);
void peerDied();
void removePortByName(const std::string &portName);
void releaseDevicesFromPeer(const std::string &portName);
void requestPort(xpc_connection_t client, xpc_object_t event);
void addPort(xpc_connection_t client, xpc_object_t event);
void removePortByName(const std::string &portName);
void removePort(xpc_connection_t client, xpc_object_t event);
void devicesUpdate(xpc_connection_t client, xpc_object_t event);
void setBroadcasting(xpc_connection_t client, xpc_object_t event);
@ -76,9 +75,9 @@ namespace AkVCam
void listeners(xpc_connection_t client, xpc_object_t event);
void listener(xpc_connection_t client, xpc_object_t event);
void broadcasting(xpc_connection_t client, xpc_object_t event);
void controlsUpdated(xpc_connection_t client, xpc_object_t event);
void listenerAdd(xpc_connection_t client, xpc_object_t event);
void listenerRemove(xpc_connection_t client, xpc_object_t event);
void controlsUpdated(xpc_connection_t client, xpc_object_t event);
};
}
@ -199,40 +198,10 @@ void AkVCam::AssistantPrivate::timerTimeout(CFRunLoopTimerRef timer, void *info)
CFRunLoopStop(CFRunLoopGetMain());
}
void AkVCam::AssistantPrivate::releaseDevicesFromPeer(const std::string &portName)
{
AkLogFunction();
for (auto &config: this->m_deviceConfigs)
if (config.second.broadcaster == portName) {
config.second.broadcaster.clear();
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING);
xpc_dictionary_set_string(dictionary, "device", config.first.c_str());
xpc_dictionary_set_string(dictionary, "broadcaster", "");
for (auto &client: this->m_clients) {
auto reply =
xpc_connection_send_message_with_reply_sync(client.second,
dictionary);
xpc_release(reply);
}
xpc_release(dictionary);
} else {
auto it = std::find(config.second.listeners.begin(),
config.second.listeners.end(),
portName);
if (it != config.second.listeners.end())
config.second.listeners.erase(it);
}
}
void AkVCam::AssistantPrivate::peerDied()
{
AkLogFunction();
std::vector<std::string> deadPeers;
for (auto &client: this->m_clients) {
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
@ -249,7 +218,56 @@ void AkVCam::AssistantPrivate::peerDied()
xpc_release(reply);
if (!alive)
this->removePortByName(client.first);
deadPeers.push_back(client.first);
}
for (auto &peer: deadPeers)
this->removePortByName(peer);
}
void AkVCam::AssistantPrivate::removePortByName(const std::string &portName)
{
AkLogFunction();
AkLogInfo() << "Port: " << portName << std::endl;
for (auto &peer: this->m_clients)
if (peer.first == portName) {
xpc_release(peer.second);
this->m_clients.erase(portName);
break;
}
if (this->m_clients.empty())
this->startTimer();
this->releaseDevicesFromPeer(portName);
}
void AkVCam::AssistantPrivate::releaseDevicesFromPeer(const std::string &portName)
{
AkLogFunction();
for (auto &config: this->m_deviceConfigs)
if (config.second.broadcaster == portName) {
config.second.broadcaster.clear();
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING);
xpc_dictionary_set_string(dictionary, "device", config.first.c_str());
xpc_dictionary_set_string(dictionary, "broadcaster", "");
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, dictionary);
xpc_release(dictionary);
} else {
auto it = std::find(config.second.listeners.begin(),
config.second.listeners.end(),
portName);
if (it != config.second.listeners.end())
config.second.listeners.erase(it);
}
}
@ -299,25 +317,6 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client,
xpc_release(reply);
}
void AkVCam::AssistantPrivate::removePortByName(const std::string &portName)
{
AkLogFunction();
AkLogInfo() << "Port: " << portName << std::endl;
for (auto &peer: this->m_clients)
if (peer.first == portName) {
xpc_release(peer.second);
this->m_clients.erase(portName);
break;
}
if (this->m_clients.empty())
this->startTimer();
this->releaseDevicesFromPeer(portName);
}
void AkVCam::AssistantPrivate::removePort(xpc_connection_t client,
xpc_object_t event)
{
@ -332,6 +331,22 @@ void AkVCam::AssistantPrivate::devicesUpdate(xpc_connection_t client,
{
UNUSED(client);
AkLogFunction();
auto devicesList = xpc_dictionary_get_array(event, "devices");
DeviceConfigs configs;
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)
@ -339,14 +354,15 @@ void AkVCam::AssistantPrivate::devicesUpdate(xpc_connection_t client,
xpc_release(notification);
}
}
void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
xpc_object_t event)
{
UNUSED(client);
AkLogFunction();
std::string deviceId = xpc_dictionary_get_string(event, "device");
std::string broadcaster = xpc_dictionary_get_string(event, "broadcaster");
bool ok = false;
if (this->m_deviceConfigs.count(deviceId) > 0)
if (this->m_deviceConfigs[deviceId].broadcaster != broadcaster) {
@ -359,13 +375,7 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
xpc_connection_send_message(client.second, notification);
xpc_release(notification);
ok = true;
}
auto reply = xpc_dictionary_create_reply(event);
xpc_dictionary_set_bool(reply, "status", ok);
xpc_connection_send_message(client, reply);
xpc_release(reply);
}
void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
@ -378,6 +388,7 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
bool ok = true;
for (auto &client: this->m_clients) {
AkLogDebug() << "Sending frame to " << client.first << std::endl;
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
event);
auto replyType = xpc_get_type(reply);
@ -387,6 +398,10 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
isOk = xpc_dictionary_get_bool(reply, "status");
ok &= isOk;
if (!isOk)
AkLogError() << "Failed sending frame." << std::endl;
xpc_release(reply);
}
@ -400,25 +415,12 @@ void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client,
{
UNUSED(client);
AkLogFunction();
auto reply = xpc_dictionary_create_reply(event);
bool ok = true;
auto notification = xpc_copy(event);
for (auto &client: this->m_clients) {
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
event);
auto replyType = xpc_get_type(reply);
bool isOk = false;
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
if (replyType == XPC_TYPE_DICTIONARY)
isOk = xpc_dictionary_get_bool(reply, "status");
ok &= isOk;
xpc_release(reply);
}
xpc_dictionary_set_bool(reply, "status", ok);
xpc_connection_send_message(client, reply);
xpc_release(reply);
xpc_release(notification);
}
void AkVCam::AssistantPrivate::listeners(xpc_connection_t client,
@ -484,22 +486,6 @@ void AkVCam::AssistantPrivate::broadcasting(xpc_connection_t client,
xpc_release(reply);
}
void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client, xpc_object_t event)
{
UNUSED(client);
AkLogFunction();
std::string deviceId = xpc_dictionary_get_string(event, "device");
if (this->m_deviceConfigs.count(deviceId) > 0) {
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
xpc_release(notification);
}
}
void AkVCam::AssistantPrivate::listenerAdd(xpc_connection_t client,
xpc_object_t event)
{
@ -559,3 +545,27 @@ void AkVCam::AssistantPrivate::listenerRemove(xpc_connection_t client,
xpc_connection_send_message(client, reply);
xpc_release(reply);
}
void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client,
xpc_object_t event)
{
UNUSED(client);
AkLogFunction();
std::string deviceId = xpc_dictionary_get_string(event, "device");
if (this->m_deviceConfigs.count(deviceId) <= 0) {
AkLogError() << "'"
<< deviceId
<< "' device in not in the devices list."
<< std::endl;
return;
}
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
xpc_release(notification);
}

View file

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

View file

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

View file

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

View file

@ -68,6 +68,7 @@ OSStatus AkVCam::Device::createObject()
OSStatus AkVCam::Device::registerObject(bool regist)
{
AkLogFunction();
AkLogDebug() << "Register: " << regist << std::endl;
OSStatus status = kCMIOHardwareUnspecifiedError;
if (!this->m_isCreated
@ -91,6 +92,11 @@ OSStatus AkVCam::Device::registerObject(bool regist)
&this->m_objectID);
}
if (status == kCMIOHardwareNoError)
AkLogDebug() << "Ok";
else
AkLogDebug() << "Error registering device" << std::endl;
return status;
}
@ -134,6 +140,7 @@ std::list<AkVCam::StreamPtr> AkVCam::Device::addStreams(int n)
OSStatus AkVCam::Device::registerStreams(bool regist)
{
AkLogFunction();
AkLogDebug() << "Register: " << regist << std::endl;
OSStatus status = kCMIOHardwareUnspecifiedError;
if (!this->m_isCreated
@ -163,6 +170,11 @@ OSStatus AkVCam::Device::registerStreams(bool regist)
streams.data());
}
if (status == kCMIOHardwareNoError)
AkLogDebug() << "Ok";
else
AkLogDebug() << "Error registering streams" << std::endl;
return status;
}
@ -338,6 +350,7 @@ OSStatus AkVCam::Device::processRS422Command(CMIODeviceRS422Command *ioRS422Comm
void AkVCam::Device::updateStreamsProperty()
{
AkLogFunction();
std::vector<ObjectPtr> 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);
if (device->registerStreams() != kCMIOHardwareNoError) {
AkLogDebug() << "Failed registering streams" << std::endl;
device->registerStreams(false);
goto createDevice_failed;
@ -444,6 +445,7 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
// Register the device.
if (device->registerObject() != kCMIOHardwareNoError) {
AkLogDebug() << "Failed registering device" << std::endl;
device->registerObject(false);
device->registerStreams(false);

View file

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

View file

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

View file

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

View file

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

View file

@ -17,6 +17,8 @@
* Web-Site: http://webcamoid.github.io/
*/
#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
@ -30,6 +32,14 @@
namespace AkVCam
{
struct PipeThread
{
std::shared_ptr<std::thread> thread;
std::atomic<bool> finished {false};
};
using PipeThreadPtr = std::shared_ptr<PipeThread>;
class MessageServerPrivate
{
public:
@ -38,9 +48,8 @@ namespace AkVCam
std::map<uint32_t, MessageHandler> m_handlers;
MessageServer::ServerMode m_mode {MessageServer::ServerModeReceive};
MessageServer::PipeState m_pipeState {MessageServer::PipeStateGone};
HANDLE m_pipe {INVALID_HANDLE_VALUE};
OVERLAPPED m_overlapped;
std::thread m_thread;
std::thread m_mainThread;
std::vector<PipeThreadPtr> m_clientsThreads;
std::mutex m_mutex;
std::condition_variable_any m_exitCheckLoop;
int m_checkInterval {5000};
@ -52,10 +61,8 @@ namespace AkVCam
bool startSend();
void stopSend();
void messagesLoop();
void processPipe(PipeThreadPtr pipeThread, HANDLE pipe);
void checkLoop();
HRESULT waitResult(DWORD *bytesTransferred);
bool readMessage(Message *message);
bool writeMessage(const Message &message);
};
}
@ -66,7 +73,7 @@ AkVCam::MessageServer::MessageServer()
AkVCam::MessageServer::~MessageServer()
{
this->stop(true);
this->stop();
delete this->d;
}
@ -177,27 +184,116 @@ BOOL AkVCam::MessageServer::sendMessage(const std::string &pipeName,
Message *messageOut,
uint32_t timeout)
{
DWORD bytesTransferred = 0;
AkLogFunction();
AkLogDebug() << "Pipe: " << pipeName << std::endl;
AkLogDebug() << "Message ID: " << stringFromMessageId(messageIn.messageId) << std::endl;
return CallNamedPipeA(pipeName.c_str(),
// CallNamedPie can sometimes return false without ever sending any data to
// the server, try many times before returning.
bool result;
for (int i = 0; i < 5; i++) {
DWORD bytesTransferred = 0;
result = CallNamedPipeA(pipeName.c_str(),
const_cast<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):
self(self)
{
memset(&this->m_overlapped, 0, sizeof(OVERLAPPED));
}
bool AkVCam::MessageServerPrivate::startReceive(bool wait)
{
AkLogFunction();
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateAboutToStart)
bool ok = false;
this->m_running = true;
if (wait) {
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStarted)
AkLogDebug() << "Server ready." << std::endl;
this->messagesLoop();
} else {
this->m_mainThread = std::thread(&MessageServerPrivate::messagesLoop, this);
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStarted)
AkLogDebug() << "Server ready." << std::endl;
}
return true;
}
void AkVCam::MessageServerPrivate::stopReceive(bool wait)
{
AkLogFunction();
if (!this->m_running)
return;
AkLogDebug() << "Stopping clients threads." << std::endl;
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateAboutToStop)
this->m_running = false;
// wake up current pipe thread.
Message message;
message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE;
message.dataSize = sizeof(MsgIsAlive);
MessageServer::sendMessage(this->m_pipeName, &message);
if (!wait)
this->m_mainThread.join();
}
bool AkVCam::MessageServerPrivate::startSend()
{
AkLogFunction();
AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl;
this->m_running = true;
this->m_mainThread = std::thread(&MessageServerPrivate::checkLoop, this);
return true;
}
void AkVCam::MessageServerPrivate::stopSend()
{
AkLogFunction();
if (!this->m_running)
return;
AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl;
this->m_running = false;
this->m_mutex.lock();
this->m_exitCheckLoop.notify_all();
this->m_mutex.unlock();
this->m_mainThread.join();
this->m_pipeState = MessageServer::PipeStateGone;
}
void AkVCam::MessageServerPrivate::messagesLoop()
{
AkLogFunction();
AkLogDebug() << "Initializing security descriptor." << std::endl;
// Define who can read and write from pipe.
@ -205,7 +301,7 @@ bool AkVCam::MessageServerPrivate::startReceive(bool wait)
*
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa379570(v=vs.85).aspx
*/
TCHAR descriptor[] =
static const TCHAR descriptor[] =
TEXT("D:") // Discretionary ACL
TEXT("(D;OICI;GA;;;BG)") // Deny access to Built-in Guests
TEXT("(D;OICI;GA;;;AN)") // Deny access to Anonymous Logon
@ -216,144 +312,160 @@ bool AkVCam::MessageServerPrivate::startReceive(bool wait)
PSECURITY_DESCRIPTOR securityDescriptor =
LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (!securityDescriptor)
goto startReceive_failed;
if (!securityDescriptor) {
AkLogError() << "Security descriptor not allocated" << std::endl;
return;
}
if (!InitializeSecurityDescriptor(securityDescriptor,
SECURITY_DESCRIPTOR_REVISION))
goto startReceive_failed;
SECURITY_DESCRIPTOR_REVISION)) {
LocalFree(securityDescriptor);
AkLogError() << "Can't initialize security descriptor: "
<< stringFromError(GetLastError())
<< " (" << GetLastError() << ")"
<< std::endl;
return;
}
AkLogDebug() << "Getting security descriptor from string." << std::endl;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(descriptor,
SDDL_REVISION_1,
&securityDescriptor,
nullptr))
goto startReceive_failed;
nullptr)) {
LocalFree(securityDescriptor);
AkLogError() << "Can't read security descriptor from string: "
<< stringFromError(GetLastError())
<< " (" << GetLastError() << ")"
<< std::endl;
return;
}
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
securityAttributes.lpSecurityDescriptor = securityDescriptor;
securityAttributes.bInheritHandle = TRUE;
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,
while (this->m_running) {
// Clean death threads.
for (;;) {
auto it = std::find_if(this->m_clientsThreads.begin(),
this->m_clientsThreads.end(),
[] (const PipeThreadPtr &thread) -> bool {
return thread->finished;
});
if (it == this->m_clientsThreads.end())
break;
(*it)->thread->join();
this->m_clientsThreads.erase(it);
}
AkLogDebug() << "Creating pipe." << std::endl;
auto pipe = CreateNamedPipeA(this->m_pipeName.c_str(),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE
| PIPE_READMODE_BYTE
| PIPE_READMODE_MESSAGE
| PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
sizeof(Message),
sizeof(Message),
NMPWAIT_USE_DEFAULT_WAIT,
0,
&securityAttributes);
if (this->m_pipe == INVALID_HANDLE_VALUE)
goto startReceive_failed;
if (pipe == INVALID_HANDLE_VALUE)
continue;
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;
AkLogDebug() << "Connecting pipe." << std::endl;
auto connected = ConnectNamedPipe(pipe, nullptr);
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 (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);
}
}
if (securityDescriptor)
for (auto &thread: this->m_clientsThreads)
thread->thread->join();
LocalFree(securityDescriptor);
return ok;
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
AkLogDebug() << "Server stopped." << std::endl;
}
void AkVCam::MessageServerPrivate::stopReceive(bool wait)
void AkVCam::MessageServerPrivate::processPipe(PipeThreadPtr pipeThread,
HANDLE pipe)
{
if (!this->m_running)
return;
for (;;) {
AkLogDebug() << "Reading message." << std::endl;
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) {
HRESULT result = S_OK;
// Wait for a connection.
if (!ConnectNamedPipe(this->m_pipe, &this->m_overlapped))
result = this->waitResult(&bytesTransferred);
if (result == S_OK) {
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->readMessage(&message)) {
if (this->m_handlers.count(message.messageId))
this->m_handlers[message.messageId](&message);
this->writeMessage(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;
}
}
DisconnectNamedPipe(this->m_pipe);
}
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
if (this->m_overlapped.hEvent != INVALID_HANDLE_VALUE) {
CloseHandle(this->m_overlapped.hEvent);
memset(&this->m_overlapped, 0, sizeof(OVERLAPPED));
}
if (this->m_pipe != INVALID_HANDLE_VALUE) {
CloseHandle(this->m_pipe);
this->m_pipe = INVALID_HANDLE_VALUE;
}
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
AkLogDebug() << "Closing pipe." << std::endl;
FlushFileBuffers(pipe);
DisconnectNamedPipe(pipe);
CloseHandle(pipe);
pipeThread->finished = true;
AkLogDebug() << "Pipe thread finished." << std::endl;
}
void AkVCam::MessageServerPrivate::checkLoop()
{
AkLogFunction();
while (this->m_running) {
AkLogDebug() << "Waiting for pipe: " << this->m_pipeName << std::endl;
auto result = WaitNamedPipeA(this->m_pipeName.c_str(), NMPWAIT_NOWAIT);
if (result
@ -378,62 +490,3 @@ void AkVCam::MessageServerPrivate::checkLoop()
this->m_mutex.unlock();
}
}
HRESULT AkVCam::MessageServerPrivate::waitResult(DWORD *bytesTransferred)
{
auto lastError = GetLastError();
if (lastError == ERROR_IO_PENDING) {
if (WaitForSingleObject(this->m_overlapped.hEvent,
INFINITE) == WAIT_OBJECT_0) {
if (!GetOverlappedResult(this->m_pipe,
&this->m_overlapped,
bytesTransferred,
FALSE))
return S_FALSE;
} else {
CancelIo(this->m_pipe);
return S_FALSE;
}
} else {
AkLogWarning() << "Wait result failed: "
<< errorToString(lastError)
<< " (" << lastError << ")"
<< std::endl;
return E_FAIL;
}
return S_OK;
}
bool AkVCam::MessageServerPrivate::readMessage(Message *message)
{
DWORD bytesTransferred = 0;
HRESULT result = S_OK;
if (!ReadFile(this->m_pipe,
message,
DWORD(sizeof(Message)),
&bytesTransferred,
&this->m_overlapped))
result = this->waitResult(&bytesTransferred);
return result == S_OK;
}
bool AkVCam::MessageServerPrivate::writeMessage(const Message &message)
{
DWORD bytesTransferred = 0;
HRESULT result = S_OK;
if (!WriteFile(this->m_pipe,
&message,
DWORD(sizeof(Message)),
&bytesTransferred,
&this->m_overlapped))
result = this->waitResult(&bytesTransferred);
return result == S_OK;
}

View file

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

View file

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

View file

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

View file

@ -27,6 +27,7 @@
#include <wincodec.h>
#include "utils.h"
#include "messagecommons.h"
#include "VCamUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h"
@ -102,7 +103,16 @@ bool AkVCam::fileExists(const std::string &path)
return GetFileAttributesA(path.c_str()) & FILE_ATTRIBUTE_ARCHIVE;
}
std::string AkVCam::errorToString(DWORD errorCode)
std::string AkVCam::realPath(const std::string &path)
{
char rpath[MAX_PATH];
memset(rpath, 0, MAX_PATH);
GetFullPathNameA(path.c_str(), MAX_PATH, rpath, nullptr);
return std::string(rpath);
}
std::string AkVCam::stringFromError(DWORD errorCode)
{
CHAR *errorStr = nullptr;
auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
@ -170,6 +180,32 @@ std::string AkVCam::createClsidStrFromStr(const std::string &str)
return stringFromIid(createClsidFromStr(str));
}
std::string AkVCam::stringFromMessageId(uint32_t messageId)
{
static const std::map<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)
{
LPWSTR strIID = nullptr;

View file

@ -39,9 +39,11 @@ namespace AkVCam
std::string moduleFileName(HINSTANCE hinstDLL);
std::string dirname(const std::string &path);
bool fileExists(const std::string &path);
std::string errorToString(DWORD errorCode);
std::string realPath(const std::string &path);
std::string stringFromError(DWORD errorCode);
CLSID createClsidFromStr(const std::string &str);
std::string createClsidStrFromStr(const std::string &str);
std::string stringFromMessageId(uint32_t messageId);
std::string stringFromIid(const IID &iid);
std::string stringFromResult(HRESULT result);
std::string stringFromClsid(const CLSID &clsid);

View file

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

View file

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

View file

@ -111,6 +111,20 @@ AkVCam::Pin::Pin(BaseFilter *baseFilter,
this->d->m_pinId = ss.str();
this->d->m_mediaTypes = new AkVCam::EnumMediaTypes(formats);
this->d->m_mediaTypes->AddRef();
auto cameraIndex = Preferences::cameraFromPath(baseFilter->deviceId());
this->d->m_broadcaster = baseFilter->broadcaster();
this->d->m_controls["hflip"] =
Preferences::cameraControlValue(cameraIndex, "hflip");
this->d->m_controls["vflip"] =
Preferences::cameraControlValue(cameraIndex, "vflip");
this->d->m_controls["scaling"] =
Preferences::cameraControlValue(cameraIndex, "scaling");
this->d->m_controls["aspect_ratio"] =
Preferences::cameraControlValue(cameraIndex, "aspect_ratio");
this->d->m_controls["swap_rgb"] =
Preferences::cameraControlValue(cameraIndex, "swap_rgb");
auto picture = Preferences::picture();
if (!picture.empty())
@ -316,9 +330,18 @@ void AkVCam::Pin::setControls(const std::map<std::string, int> &controls)
return;
}
for (auto &control: controls)
AkLogDebug() << control.first << ": " << control.second << std::endl;
this->d->m_controls = controls;
this->d->m_controlsMutex.unlock();
this->d->updateTestFrame();
this->d->m_mutex.lock();
if (this->d->m_broadcaster.empty())
this->d->m_currentFrame = this->d->m_testFrameAdapted;
this->d->m_mutex.unlock();
}
bool AkVCam::Pin::horizontalFlip() const
@ -907,6 +930,7 @@ HRESULT AkVCam::PinPrivate::sendFrame()
void AkVCam::PinPrivate::updateTestFrame()
{
AkLogFunction();
auto frame = this->applyAdjusts(this->m_testFrame);
if (frame.format().size() < 1)

View file

@ -25,7 +25,7 @@ isEmpty(DSHOW_PLUGIN_ASSISTANT_DESCRIPTION):
isEmpty(DSHOW_PLUGIN_DESCRIPTION):
DSHOW_PLUGIN_DESCRIPTION = "Webcamoid Virtual Camera"
isEmpty(DSHOW_PLUGIN_DESCRIPTION_EXT):
DSHOW_PLUGIN_DESCRIPTION_EXT = "Central service for communicating between virtual cameras clients and servers"
DSHOW_PLUGIN_DESCRIPTION_EXT = "Central service for communicating between virtual cameras and clients"
isEmpty(DSHOW_PLUGIN_DEVICE_PREFIX):
DSHOW_PLUGIN_DEVICE_PREFIX = AkVCamVideoDevice
isEmpty(DSHOW_PLUGIN_VENDOR):

View file

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

View file

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