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:
parent
f2ca25c5c8
commit
09e3b6ad3a
31 changed files with 1658 additions and 712 deletions
|
@ -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)
|
||||
{
|
||||
|
@ -1311,11 +1414,14 @@ int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
|
|||
}
|
||||
|
||||
if (this->containsFlag(flags, "get-control", "-l")) {
|
||||
for (size_t i = 0; i< control.menu.size(); i++)
|
||||
for (size_t i = 0; i < control.menu.size(); i++)
|
||||
if (this->m_parseable)
|
||||
std::cout << control.menu[i] << std::endl;
|
||||
else
|
||||
std::cout << i << ": " << control.menu[i] << std::endl;
|
||||
std::cout << i
|
||||
<< ": "
|
||||
<< control.menu[i]
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1582,6 +1688,189 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int AkVCam::CmdParserPrivate::dumpInfo(const AkVCam::StringMap &flags,
|
||||
const AkVCam::StringVector &args)
|
||||
{
|
||||
UNUSED(flags);
|
||||
UNUSED(args);
|
||||
static const auto indent = 4 * std::string(" ");
|
||||
|
||||
std::cout << "<?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");
|
||||
|
@ -1654,7 +1943,7 @@ std::vector<AkVCam::VideoFormat> AkVCam::CmdParserPrivate::readFormat(Settings &
|
|||
VideoFormat format(pixFormat,
|
||||
width,
|
||||
height,
|
||||
{frame_rate});
|
||||
{frame_rate});
|
||||
|
||||
if (format.isValid())
|
||||
formats.push_back(format);
|
||||
|
@ -1735,7 +2024,8 @@ void AkVCam::CmdParserPrivate::createDevice(Settings &settings,
|
|||
}
|
||||
|
||||
auto deviceId = this->m_ipcBridge.addDevice(description);
|
||||
auto supportedFormats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
|
||||
auto supportedFormats =
|
||||
this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
|
||||
|
||||
for (auto &format: formats) {
|
||||
auto it = std::find(supportedFormats.begin(),
|
||||
|
@ -1781,3 +2071,13 @@ std::string AkVCam::operator *(const std::string &str, size_t n)
|
|||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string AkVCam::operator *(size_t n, const std::string &str)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
ss << str;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -53,4 +53,6 @@ namespace AkVCam
|
|||
};
|
||||
}
|
||||
|
||||
std::ostream &operator <<(std::ostream &os, const AkVCam::Fraction &fraction);
|
||||
|
||||
#endif // FRACTION_H
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -108,10 +108,11 @@ bool AkVCam::Settings::load(const std::string &fileName)
|
|||
if (this->d->m_configs.count(currentGroup) < 1)
|
||||
this->d->m_configs[currentGroup] = {};
|
||||
} else if (!element.key.empty() && !element.value.empty()) {
|
||||
if (currentGroup.empty()) {
|
||||
if (currentGroup.empty())
|
||||
currentGroup = "General";
|
||||
|
||||
if (this->d->m_configs.count(currentGroup) < 1)
|
||||
this->d->m_configs[currentGroup] = {};
|
||||
}
|
||||
|
||||
this->d->m_configs[currentGroup][element.key] = element.value;
|
||||
}
|
||||
|
@ -210,14 +211,13 @@ bool AkVCam::Settings::contains(const std::string &key) const
|
|||
if (this->d->m_currentArray.empty()) {
|
||||
contains = groupConfigs.count(key) > 0;
|
||||
} else {
|
||||
std::string arrayKey;
|
||||
std::stringstream ss(arrayKey);
|
||||
ss << this->d->m_currentArray
|
||||
<< '/'
|
||||
<< this->d->m_arrayIndex + 1
|
||||
<< '/'
|
||||
<< key;
|
||||
contains = groupConfigs.count(arrayKey) > 0;
|
||||
std::stringstream arrayKey;
|
||||
arrayKey << this->d->m_currentArray
|
||||
<< '/'
|
||||
<< this->d->m_arrayIndex + 1
|
||||
<< '/'
|
||||
<< key;
|
||||
contains = groupConfigs.count(arrayKey.str()) > 0;
|
||||
}
|
||||
|
||||
return contains;
|
||||
|
@ -237,14 +237,13 @@ std::string AkVCam::Settings::value(const std::string &key) const
|
|||
if (this->d->m_currentArray.empty()) {
|
||||
value = groupConfigs[key];
|
||||
} else {
|
||||
std::string arrayKey;
|
||||
std::stringstream ss(arrayKey);
|
||||
ss << this->d->m_currentArray
|
||||
<< '/'
|
||||
<< this->d->m_arrayIndex + 1
|
||||
<< '/'
|
||||
<< key;
|
||||
value = groupConfigs[arrayKey];
|
||||
std::stringstream arrayKey;
|
||||
arrayKey << this->d->m_currentArray
|
||||
<< '/'
|
||||
<< this->d->m_arrayIndex + 1
|
||||
<< '/'
|
||||
<< key;
|
||||
value = groupConfigs[arrayKey.str()];
|
||||
}
|
||||
|
||||
return value;
|
||||
|
@ -358,7 +357,7 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line,
|
|||
|
||||
if (line.empty() || line[0] == '#' || line[0] == ';') {
|
||||
if (ok)
|
||||
*ok = false;
|
||||
*ok = true;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,8 +218,57 @@ void AkVCam::AssistantPrivate::peerDied()
|
|||
xpc_release(reply);
|
||||
|
||||
if (!alive)
|
||||
this->removePortByName(client.first);
|
||||
deadPeers.push_back(client.first);
|
||||
}
|
||||
|
||||
for (auto &peer: deadPeers)
|
||||
this->removePortByName(peer);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::removePortByName(const std::string &portName)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogInfo() << "Port: " << portName << std::endl;
|
||||
|
||||
for (auto &peer: this->m_clients)
|
||||
if (peer.first == portName) {
|
||||
xpc_release(peer.second);
|
||||
this->m_clients.erase(portName);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->m_clients.empty())
|
||||
this->startTimer();
|
||||
|
||||
this->releaseDevicesFromPeer(portName);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::releaseDevicesFromPeer(const std::string &portName)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
for (auto &config: this->m_deviceConfigs)
|
||||
if (config.second.broadcaster == portName) {
|
||||
config.second.broadcaster.clear();
|
||||
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING);
|
||||
xpc_dictionary_set_string(dictionary, "device", config.first.c_str());
|
||||
xpc_dictionary_set_string(dictionary, "broadcaster", "");
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, dictionary);
|
||||
|
||||
xpc_release(dictionary);
|
||||
} else {
|
||||
auto it = std::find(config.second.listeners.begin(),
|
||||
config.second.listeners.end(),
|
||||
portName);
|
||||
|
||||
if (it != config.second.listeners.end())
|
||||
config.second.listeners.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::requestPort(xpc_connection_t client,
|
||||
|
@ -299,25 +317,6 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client,
|
|||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::removePortByName(const std::string &portName)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogInfo() << "Port: " << portName << std::endl;
|
||||
|
||||
for (auto &peer: this->m_clients)
|
||||
if (peer.first == portName) {
|
||||
xpc_release(peer.second);
|
||||
this->m_clients.erase(portName);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->m_clients.empty())
|
||||
this->startTimer();
|
||||
|
||||
this->releaseDevicesFromPeer(portName);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::removePort(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
|
@ -332,21 +331,38 @@ void AkVCam::AssistantPrivate::devicesUpdate(xpc_connection_t client,
|
|||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
auto devicesList = xpc_dictionary_get_array(event, "devices");
|
||||
DeviceConfigs configs;
|
||||
|
||||
xpc_release(notification);
|
||||
for (size_t i = 0; i < xpc_array_get_count(devicesList); i++) {
|
||||
std::string device = xpc_array_get_string(devicesList, i);
|
||||
|
||||
if (this->m_deviceConfigs.count(device) > 0)
|
||||
configs[device] = this->m_deviceConfigs[device];
|
||||
else
|
||||
configs[device] = {};
|
||||
}
|
||||
|
||||
this->m_deviceConfigs = configs;
|
||||
|
||||
if (xpc_dictionary_get_bool(event, "propagate")) {
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
}
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
std::string broadcaster = xpc_dictionary_get_string(event, "broadcaster");
|
||||
bool ok = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
if (this->m_deviceConfigs[deviceId].broadcaster != broadcaster) {
|
||||
|
@ -359,13 +375,7 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
|
|||
xpc_connection_send_message(client.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
xpc_dictionary_set_bool(reply, "status", ok);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
|
||||
|
@ -378,6 +388,7 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
|
|||
bool ok = true;
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
AkLogDebug() << "Sending frame to " << client.first << std::endl;
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
|
||||
event);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
|
@ -387,6 +398,10 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
|
|||
isOk = xpc_dictionary_get_bool(reply, "status");
|
||||
|
||||
ok &= isOk;
|
||||
|
||||
if (!isOk)
|
||||
AkLogError() << "Failed sending frame." << std::endl;
|
||||
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
|
@ -400,25 +415,12 @@ void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client,
|
|||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
bool ok = true;
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
|
||||
event);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
bool isOk = false;
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
|
||||
if (replyType == XPC_TYPE_DICTIONARY)
|
||||
isOk = xpc_dictionary_get_bool(reply, "status");
|
||||
|
||||
ok &= isOk;
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
xpc_dictionary_set_bool(reply, "status", ok);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
xpc_release(notification);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::listeners(xpc_connection_t client,
|
||||
|
@ -484,22 +486,6 @@ void AkVCam::AssistantPrivate::broadcasting(xpc_connection_t client,
|
|||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client, xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0) {
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
}
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::listenerAdd(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
|
@ -559,3 +545,27 @@ void AkVCam::AssistantPrivate::listenerRemove(xpc_connection_t client,
|
|||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) <= 0) {
|
||||
AkLogError() << "'"
|
||||
<< deviceId
|
||||
<< "' device in not in the devices list."
|
||||
<< std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
};
|
||||
for (auto &peer: this->m_clients)
|
||||
if (peer.first == portName) {
|
||||
this->m_clients.erase(portName);
|
||||
|
||||
bool breakLoop = false;
|
||||
|
||||
for (auto peers: allPeers) {
|
||||
for (auto &peer: *peers)
|
||||
if (peer.first == portName) {
|
||||
peers->erase(portName);
|
||||
breakLoop = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (breakLoop)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool peersEmpty = this->m_servers.empty() && this->m_clients.empty();
|
||||
this->m_peerMutex.unlock();
|
||||
|
||||
if (peersEmpty)
|
||||
this->m_timer.stop();
|
||||
|
||||
this->releaseDevicesFromPeer(portName);
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
for (auto &config: this->m_deviceConfigs)
|
||||
if (config.second.broadcaster == portName) {
|
||||
config.second.broadcaster.clear();
|
||||
|
@ -428,6 +409,8 @@ void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName)
|
|||
if (it != config.second.listeners.end())
|
||||
config.second.listeners.erase(it);
|
||||
}
|
||||
|
||||
AkLogInfo() << portName << " released." << std::endl;
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::requestPort(AkVCam::Message *message)
|
||||
|
@ -453,15 +436,9 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
|
|||
bool ok = true;
|
||||
|
||||
this->m_peerMutex.lock();
|
||||
AssistantPeers *peers;
|
||||
|
||||
if (portName.find(AKVCAM_ASSISTANT_CLIENT_NAME) != std::string::npos)
|
||||
peers = &this->m_clients;
|
||||
else
|
||||
peers = &this->m_servers;
|
||||
|
||||
for (auto &peer: *peers)
|
||||
if (peer.first == portName) {
|
||||
for (auto &client: this->m_clients)
|
||||
if (client.first == portName) {
|
||||
ok = false;
|
||||
|
||||
break;
|
||||
|
@ -469,16 +446,10 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
|
|||
|
||||
if (ok) {
|
||||
AkLogInfo() << "Adding Peer: " << portName << std::endl;
|
||||
(*peers)[portName] = pipeName;
|
||||
this->m_clients[portName] = pipeName;
|
||||
}
|
||||
|
||||
size_t nPeers = this->m_servers.size() + this->m_clients.size();
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
|
||||
if (ok && nPeers == 1)
|
||||
this->m_timer.start();
|
||||
|
||||
data->status = ok;
|
||||
}
|
||||
|
||||
|
@ -490,6 +461,31 @@ void AkVCam::ServicePrivate::removePort(AkVCam::Message *message)
|
|||
this->removePortByName(data->port);
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::devicesUpdate(AkVCam::Message *message)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto data = messageData<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,25 +494,22 @@ void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message)
|
|||
std::string broadcaster(data->broadcaster);
|
||||
data->status = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) < 1)
|
||||
this->m_deviceConfigs[deviceId] = {};
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
if (this->m_deviceConfigs[deviceId].broadcaster != broadcaster) {
|
||||
AkLogInfo() << "Device: " << deviceId << std::endl;
|
||||
AkLogInfo() << "Broadcaster: " << broadcaster << std::endl;
|
||||
this->m_deviceConfigs[deviceId].broadcaster = broadcaster;
|
||||
data->status = true;
|
||||
|
||||
if (this->m_deviceConfigs[deviceId].broadcaster == broadcaster)
|
||||
return;
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
AkLogInfo() << "Device: " << deviceId << std::endl;
|
||||
AkLogInfo() << "Broadcaster: " << broadcaster << std::endl;
|
||||
this->m_deviceConfigs[deviceId].broadcaster = broadcaster;
|
||||
data->status = true;
|
||||
for (auto &client: this->m_clients) {
|
||||
Message msg(message);
|
||||
MessageServer::sendMessage(client.second, &msg);
|
||||
}
|
||||
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
Message msg(message);
|
||||
MessageServer::sendMessage(client.second, &msg);
|
||||
}
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
this->m_peerMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message)
|
||||
|
@ -541,17 +534,6 @@ void AkVCam::ServicePrivate::pictureUpdated(AkVCam::Message *message)
|
|||
this->m_peerMutex.unlock();
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::deviceUpdate(AkVCam::Message *message)
|
||||
{
|
||||
AkLogFunction();
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
MessageServer::sendMessage(client.second, message);
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::listeners(AkVCam::Message *message)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
@ -707,7 +689,7 @@ DWORD WINAPI controlHandler(DWORD control,
|
|||
AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING,
|
||||
NO_ERROR,
|
||||
0);
|
||||
AkVCam::servicePrivate()->m_messageServer.stop();
|
||||
AkVCam::servicePrivate()->m_messageServer.stop(true);
|
||||
result = NO_ERROR;
|
||||
|
||||
break;
|
||||
|
@ -732,7 +714,7 @@ BOOL WINAPI controlDebugHandler(DWORD control)
|
|||
AkLogFunction();
|
||||
|
||||
if (control == CTRL_BREAK_EVENT || control == CTRL_C_EVENT) {
|
||||
AkVCam::servicePrivate()->m_messageServer.stop();
|
||||
AkVCam::servicePrivate()->m_messageServer.stop(true);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
const_cast<Message *>(&messageIn),
|
||||
DWORD(sizeof(Message)),
|
||||
messageOut,
|
||||
DWORD(sizeof(Message)),
|
||||
&bytesTransferred,
|
||||
timeout);
|
||||
// CallNamedPie can sometimes return false without ever sending any data to
|
||||
// the server, try many times before returning.
|
||||
bool result;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
DWORD bytesTransferred = 0;
|
||||
result = CallNamedPipeA(pipeName.c_str(),
|
||||
const_cast<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;
|
||||
|
||||
// Create a read/write message type pipe.
|
||||
this->m_pipe = CreateNamedPipeA(this->m_pipeName.c_str(),
|
||||
PIPE_ACCESS_DUPLEX
|
||||
| FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE
|
||||
| PIPE_READMODE_BYTE
|
||||
| PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
sizeof(Message),
|
||||
sizeof(Message),
|
||||
NMPWAIT_USE_DEFAULT_WAIT,
|
||||
&securityAttributes);
|
||||
|
||||
if (this->m_pipe == INVALID_HANDLE_VALUE)
|
||||
goto startReceive_failed;
|
||||
|
||||
memset(&this->m_overlapped, 0, sizeof(OVERLAPPED));
|
||||
this->m_overlapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStarted)
|
||||
this->m_running = true;
|
||||
|
||||
if (wait)
|
||||
this->messagesLoop();
|
||||
else
|
||||
this->m_thread =
|
||||
std::thread(&MessageServerPrivate::messagesLoop, this);
|
||||
|
||||
ok = true;
|
||||
|
||||
startReceive_failed:
|
||||
|
||||
if (!ok) {
|
||||
AkLogError() << "Error starting server: "
|
||||
<< errorToString(GetLastError())
|
||||
<< " (" << GetLastError() << ")"
|
||||
<< std::endl;
|
||||
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
|
||||
}
|
||||
|
||||
if (securityDescriptor)
|
||||
LocalFree(securityDescriptor);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServerPrivate::stopReceive(bool wait)
|
||||
{
|
||||
if (!this->m_running)
|
||||
return;
|
||||
|
||||
this->m_running = false;
|
||||
SetEvent(this->m_overlapped.hEvent);
|
||||
|
||||
if (wait)
|
||||
this->m_thread.join();
|
||||
}
|
||||
|
||||
bool AkVCam::MessageServerPrivate::startSend()
|
||||
{
|
||||
this->m_running = true;
|
||||
this->m_thread = std::thread(&MessageServerPrivate::checkLoop, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServerPrivate::stopSend()
|
||||
{
|
||||
if (!this->m_running)
|
||||
return;
|
||||
|
||||
this->m_running = false;
|
||||
this->m_mutex.lock();
|
||||
this->m_exitCheckLoop.notify_all();
|
||||
this->m_mutex.unlock();
|
||||
this->m_thread.join();
|
||||
this->m_pipeState = MessageServer::PipeStateGone;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServerPrivate::messagesLoop()
|
||||
{
|
||||
DWORD bytesTransferred = 0;
|
||||
AkLogDebug() << "Pipe name: " << this->m_pipeName << std::endl;
|
||||
|
||||
while (this->m_running) {
|
||||
HRESULT result = S_OK;
|
||||
// Clean death threads.
|
||||
for (;;) {
|
||||
auto it = std::find_if(this->m_clientsThreads.begin(),
|
||||
this->m_clientsThreads.end(),
|
||||
[] (const PipeThreadPtr &thread) -> bool {
|
||||
return thread->finished;
|
||||
});
|
||||
|
||||
// Wait for a connection.
|
||||
if (!ConnectNamedPipe(this->m_pipe, &this->m_overlapped))
|
||||
result = this->waitResult(&bytesTransferred);
|
||||
if (it == this->m_clientsThreads.end())
|
||||
break;
|
||||
|
||||
if (result == S_OK) {
|
||||
Message message;
|
||||
|
||||
if (this->readMessage(&message)) {
|
||||
if (this->m_handlers.count(message.messageId))
|
||||
this->m_handlers[message.messageId](&message);
|
||||
|
||||
this->writeMessage(message);
|
||||
}
|
||||
(*it)->thread->join();
|
||||
this->m_clientsThreads.erase(it);
|
||||
}
|
||||
|
||||
DisconnectNamedPipe(this->m_pipe);
|
||||
AkLogDebug() << "Creating pipe." << std::endl;
|
||||
auto pipe = CreateNamedPipeA(this->m_pipeName.c_str(),
|
||||
PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_MESSAGE
|
||||
| PIPE_READMODE_MESSAGE
|
||||
| PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES,
|
||||
sizeof(Message),
|
||||
sizeof(Message),
|
||||
0,
|
||||
&securityAttributes);
|
||||
|
||||
if (pipe == INVALID_HANDLE_VALUE)
|
||||
continue;
|
||||
|
||||
AkLogDebug() << "Connecting pipe." << std::endl;
|
||||
auto connected = ConnectNamedPipe(pipe, nullptr);
|
||||
|
||||
if (connected || GetLastError() == ERROR_PIPE_CONNECTED) {
|
||||
auto thread = std::make_shared<PipeThread>();
|
||||
thread->thread =
|
||||
std::make_shared<std::thread>(&MessageServerPrivate::processPipe,
|
||||
this,
|
||||
thread,
|
||||
pipe);
|
||||
this->m_clientsThreads.push_back(thread);
|
||||
} else {
|
||||
AkLogError() << "Failed connecting pipe." << std::endl;
|
||||
CloseHandle(pipe);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &thread: this->m_clientsThreads)
|
||||
thread->thread->join();
|
||||
|
||||
LocalFree(securityDescriptor);
|
||||
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
|
||||
AkLogDebug() << "Server stopped." << std::endl;
|
||||
}
|
||||
|
||||
if (this->m_overlapped.hEvent != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(this->m_overlapped.hEvent);
|
||||
memset(&this->m_overlapped, 0, sizeof(OVERLAPPED));
|
||||
void AkVCam::MessageServerPrivate::processPipe(PipeThreadPtr pipeThread,
|
||||
HANDLE pipe)
|
||||
{
|
||||
for (;;) {
|
||||
AkLogDebug() << "Reading message." << std::endl;
|
||||
|
||||
Message message;
|
||||
DWORD bytesTransferred = 0;
|
||||
auto result = ReadFile(pipe,
|
||||
&message,
|
||||
DWORD(sizeof(Message)),
|
||||
&bytesTransferred,
|
||||
nullptr);
|
||||
|
||||
if (!result || bytesTransferred == 0) {
|
||||
AkLogError() << "Failed reading from pipe." << std::endl;
|
||||
auto lastError = GetLastError();
|
||||
|
||||
if (lastError)
|
||||
AkLogError() << stringFromError(lastError) << std::endl;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
AkLogDebug() << "Message ID: " << stringFromMessageId(message.messageId) << std::endl;
|
||||
|
||||
if (this->m_handlers.count(message.messageId))
|
||||
this->m_handlers[message.messageId](&message);
|
||||
|
||||
AkLogDebug() << "Writing message." << std::endl;
|
||||
result = WriteFile(pipe,
|
||||
&message,
|
||||
DWORD(sizeof(Message)),
|
||||
&bytesTransferred,
|
||||
nullptr);
|
||||
|
||||
if (!result || bytesTransferred != DWORD(sizeof(Message))) {
|
||||
AkLogError() << "Failed writing to pipe." << std::endl;
|
||||
auto lastError = GetLastError();
|
||||
|
||||
if (lastError)
|
||||
AkLogError() << stringFromError(lastError) << std::endl;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->m_pipe != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(this->m_pipe);
|
||||
this->m_pipe = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
|
||||
AkLogDebug() << "Closing pipe." << std::endl;
|
||||
FlushFileBuffers(pipe);
|
||||
DisconnectNamedPipe(pipe);
|
||||
CloseHandle(pipe);
|
||||
pipeThread->finished = true;
|
||||
AkLogDebug() << "Pipe thread finished." << std::endl;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServerPrivate::checkLoop()
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
while (this->m_running) {
|
||||
AkLogDebug() << "Waiting for pipe: " << this->m_pipeName << std::endl;
|
||||
auto result = WaitNamedPipeA(this->m_pipeName.c_str(), NMPWAIT_NOWAIT);
|
||||
|
||||
if (result
|
||||
|
@ -378,62 +490,3 @@ void AkVCam::MessageServerPrivate::checkLoop()
|
|||
this->m_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT AkVCam::MessageServerPrivate::waitResult(DWORD *bytesTransferred)
|
||||
{
|
||||
auto lastError = GetLastError();
|
||||
|
||||
if (lastError == ERROR_IO_PENDING) {
|
||||
if (WaitForSingleObject(this->m_overlapped.hEvent,
|
||||
INFINITE) == WAIT_OBJECT_0) {
|
||||
if (!GetOverlappedResult(this->m_pipe,
|
||||
&this->m_overlapped,
|
||||
bytesTransferred,
|
||||
FALSE))
|
||||
return S_FALSE;
|
||||
} else {
|
||||
CancelIo(this->m_pipe);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
} else {
|
||||
AkLogWarning() << "Wait result failed: "
|
||||
<< errorToString(lastError)
|
||||
<< " (" << lastError << ")"
|
||||
<< std::endl;
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool AkVCam::MessageServerPrivate::readMessage(Message *message)
|
||||
{
|
||||
DWORD bytesTransferred = 0;
|
||||
HRESULT result = S_OK;
|
||||
|
||||
if (!ReadFile(this->m_pipe,
|
||||
message,
|
||||
DWORD(sizeof(Message)),
|
||||
&bytesTransferred,
|
||||
&this->m_overlapped))
|
||||
result = this->waitResult(&bytesTransferred);
|
||||
|
||||
return result == S_OK;
|
||||
}
|
||||
|
||||
bool AkVCam::MessageServerPrivate::writeMessage(const Message &message)
|
||||
{
|
||||
DWORD bytesTransferred = 0;
|
||||
HRESULT result = S_OK;
|
||||
|
||||
if (!WriteFile(this->m_pipe,
|
||||
&message,
|
||||
DWORD(sizeof(Message)),
|
||||
&bytesTransferred,
|
||||
&this->m_overlapped))
|
||||
result = this->waitResult(&bytesTransferred);
|
||||
|
||||
return result == S_OK;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include "VCamUtils/src/image/videoformat.h"
|
||||
#include "VCamUtils/src/logger.h"
|
||||
|
||||
#define REG_PREFIX "SOFTWARE\\Webcamoid\\VirtualCamera\\"
|
||||
#define REG_PREFIX "SOFTWARE\\Webcamoid\\VirtualCamera"
|
||||
|
||||
namespace AkVCam
|
||||
{
|
||||
|
@ -42,128 +42,154 @@ namespace AkVCam
|
|||
bool readValue(const std::string &key,
|
||||
DWORD dataTypeFlags,
|
||||
PVOID data,
|
||||
LPDWORD dataSize);
|
||||
void setValue(const std::string &key,
|
||||
LPDWORD dataSize,
|
||||
bool global);
|
||||
bool setValue(const std::string &key,
|
||||
DWORD dataType,
|
||||
LPCSTR data,
|
||||
DWORD dataSize);
|
||||
DWORD dataSize,
|
||||
bool global);
|
||||
}
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::write(const std::string &key,
|
||||
const std::string &value)
|
||||
bool AkVCam::Preferences::write(const std::string &key,
|
||||
const std::string &value,
|
||||
bool global)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
|
||||
setValue(key, REG_SZ, value.c_str(), DWORD(value.size()));
|
||||
|
||||
return setValue(key, REG_SZ, value.c_str(), DWORD(value.size()), global);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::write(const std::string &key, int value)
|
||||
bool AkVCam::Preferences::write(const std::string &key, int value, bool global)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
|
||||
setValue(key,
|
||||
REG_DWORD,
|
||||
reinterpret_cast<const char *>(&value),
|
||||
DWORD(sizeof(int)));
|
||||
|
||||
return setValue(key,
|
||||
REG_DWORD,
|
||||
reinterpret_cast<const char *>(&value),
|
||||
DWORD(sizeof(int)),
|
||||
global);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::write(const std::string &key, double value)
|
||||
bool AkVCam::Preferences::write(const std::string &key,
|
||||
double value,
|
||||
bool global)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogInfo() << "Writing: " << key << " = " << value << std::endl;
|
||||
auto val = std::to_string(value);
|
||||
setValue(key,
|
||||
REG_SZ,
|
||||
val.c_str(),
|
||||
DWORD(val.size()));
|
||||
|
||||
return setValue(key,
|
||||
REG_SZ,
|
||||
val.c_str(),
|
||||
DWORD(val.size()),
|
||||
global);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::write(const std::string &key,
|
||||
std::vector<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\\"
|
||||
+ std::to_string(cameraIndex)
|
||||
+ "\\description",
|
||||
description);
|
||||
write("Cameras\\"
|
||||
+ std::to_string(cameraIndex)
|
||||
+ "\\path",
|
||||
path_);
|
||||
write("Cameras\\"
|
||||
+ std::to_string(cameraIndex)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()));
|
||||
|
||||
if (path.empty())
|
||||
return {};
|
||||
|
||||
bool ok = true;
|
||||
int cameraIndex = readInt("Cameras\\", 0, true) + 1;
|
||||
ok &= write("Cameras\\size", cameraIndex, true);
|
||||
ok &= write("Cameras\\"
|
||||
+ std::to_string(cameraIndex)
|
||||
+ "\\description",
|
||||
description,
|
||||
true);
|
||||
ok &= write("Cameras\\"
|
||||
+ std::to_string(cameraIndex)
|
||||
+ "\\path",
|
||||
path_,
|
||||
true);
|
||||
ok &= write("Cameras\\"
|
||||
+ std::to_string(cameraIndex)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()),
|
||||
true);
|
||||
|
||||
for (size_t i = 0; i < formats.size(); i++) {
|
||||
auto &format = formats[i];
|
||||
|
@ -246,43 +288,49 @@ std::string AkVCam::Preferences::addCamera(const std::string &path,
|
|||
+ "\\Formats\\"
|
||||
+ std::to_string(i + 1);
|
||||
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
|
||||
write(prefix + "\\format", formatStr);
|
||||
write(prefix + "\\width", format.width());
|
||||
write(prefix + "\\height", format.height());
|
||||
write(prefix + "\\fps", format.minimumFrameRate().toString());
|
||||
ok &= write(prefix + "\\format", formatStr, true);
|
||||
ok &= write(prefix + "\\width", format.width(), true);
|
||||
ok &= write(prefix + "\\height", format.height(), true);
|
||||
ok &= write(prefix + "\\fps",
|
||||
format.minimumFrameRate().toString(),
|
||||
true);
|
||||
}
|
||||
|
||||
return path_;
|
||||
return ok? path_: std::string();
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::removeCamera(const std::string &path)
|
||||
bool AkVCam::Preferences::removeCamera(const std::string &path)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogInfo() << "Device: " << path << std::endl;
|
||||
int cameraIndex = cameraFromPath(path);
|
||||
|
||||
if (cameraIndex < 0)
|
||||
return;
|
||||
return false;
|
||||
|
||||
cameraSetFormats(size_t(cameraIndex), {});
|
||||
bool ok = true;
|
||||
ok &= cameraSetFormats(size_t(cameraIndex), {});
|
||||
|
||||
auto nCameras = camerasCount();
|
||||
deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + '\\');
|
||||
ok &= deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + '\\', true);
|
||||
|
||||
for (auto i = size_t(cameraIndex + 1); i < nCameras; i++)
|
||||
move("Cameras\\" + std::to_string(i + 1),
|
||||
"Cameras\\" + std::to_string(i));
|
||||
ok &= move("Cameras\\" + std::to_string(i + 1),
|
||||
"Cameras\\" + std::to_string(i),
|
||||
true);
|
||||
|
||||
if (nCameras > 1)
|
||||
write("Cameras\\size", int(nCameras - 1));
|
||||
ok &= write("Cameras\\size", int(nCameras - 1), true);
|
||||
else
|
||||
deleteKey("Cameras\\");
|
||||
ok &= deleteKey("Cameras\\", true);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
size_t AkVCam::Preferences::camerasCount()
|
||||
{
|
||||
AkLogFunction();
|
||||
int nCameras = readInt("Cameras\\size");
|
||||
int nCameras = readInt("Cameras\\size", 0, true);
|
||||
AkLogInfo() << "Cameras: " << nCameras << std::endl;
|
||||
|
||||
return size_t(nCameras);
|
||||
|
@ -359,31 +407,38 @@ std::string AkVCam::Preferences::cameraDescription(size_t cameraIndex)
|
|||
|
||||
return readString("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\description");
|
||||
+ "\\description",
|
||||
{},
|
||||
true);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::cameraSetDescription(size_t cameraIndex,
|
||||
bool AkVCam::Preferences::cameraSetDescription(size_t cameraIndex,
|
||||
const std::string &description)
|
||||
{
|
||||
if (cameraIndex >= camerasCount())
|
||||
return;
|
||||
return false;
|
||||
|
||||
write("Cameras\\" + std::to_string(cameraIndex + 1) + "\\description",
|
||||
description);
|
||||
return write("Cameras\\" + std::to_string(cameraIndex + 1) + "\\description",
|
||||
description,
|
||||
true);
|
||||
}
|
||||
|
||||
std::string AkVCam::Preferences::cameraPath(size_t cameraIndex)
|
||||
{
|
||||
return readString("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\path");
|
||||
+ "\\path",
|
||||
{},
|
||||
true);
|
||||
}
|
||||
|
||||
size_t AkVCam::Preferences::formatsCount(size_t cameraIndex)
|
||||
{
|
||||
return size_t(readInt("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\size"));
|
||||
+ "\\Formats\\size",
|
||||
0,
|
||||
true));
|
||||
}
|
||||
|
||||
AkVCam::VideoFormat AkVCam::Preferences::cameraFormat(size_t cameraIndex,
|
||||
|
@ -394,11 +449,11 @@ AkVCam::VideoFormat AkVCam::Preferences::cameraFormat(size_t cameraIndex,
|
|||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\"
|
||||
+ std::to_string(formatIndex + 1);
|
||||
auto format = readString(prefix + "\\format");
|
||||
auto format = readString(prefix + "\\format", {}, true);
|
||||
auto fourcc = VideoFormat::fourccFromString(format);
|
||||
int width = readInt(prefix + "\\width");
|
||||
int height = readInt(prefix + "\\height");
|
||||
auto fps = Fraction(readString(prefix + "\\fps"));
|
||||
int width = readInt(prefix + "\\width", 0, true);
|
||||
int height = readInt(prefix + "\\height", 0, true);
|
||||
auto fps = Fraction(readString(prefix + "\\fps", {}, true));
|
||||
|
||||
return VideoFormat(fourcc, width, height, {fps});
|
||||
}
|
||||
|
@ -418,19 +473,22 @@ std::vector<AkVCam::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\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()));
|
||||
bool ok = true;
|
||||
ok &= deleteKey("Cameras\\" + std::to_string(cameraIndex + 1) + "\\Formats\\",
|
||||
true);
|
||||
ok &= write("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()),
|
||||
true);
|
||||
|
||||
for (size_t i = 0; i < formats.size(); i++) {
|
||||
auto &format = formats[i];
|
||||
|
@ -439,14 +497,16 @@ void AkVCam::Preferences::cameraSetFormats(size_t cameraIndex,
|
|||
+ "\\Formats\\"
|
||||
+ std::to_string(i + 1);
|
||||
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
|
||||
write(prefix + "\\format", formatStr);
|
||||
write(prefix + "\\width", format.width());
|
||||
write(prefix + "\\height", format.height());
|
||||
write(prefix + "\\fps", format.minimumFrameRate().toString());
|
||||
ok &= write(prefix + "\\format", formatStr, true);
|
||||
ok &= write(prefix + "\\width", format.width(), true);
|
||||
ok &= write(prefix + "\\height", format.height(), true);
|
||||
ok &= write(prefix + "\\fps", format.minimumFrameRate().toString(), true);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex,
|
||||
bool AkVCam::Preferences::cameraAddFormat(size_t cameraIndex,
|
||||
const AkVCam::VideoFormat &format,
|
||||
int index)
|
||||
{
|
||||
|
@ -456,11 +516,13 @@ void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex,
|
|||
if (index < 0 || index > int(formats.size()))
|
||||
index = int(formats.size());
|
||||
|
||||
bool ok = true;
|
||||
formats.insert(formats.begin() + index, format);
|
||||
write("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()));
|
||||
ok &= write("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()),
|
||||
true);
|
||||
|
||||
for (size_t i = 0; i < formats.size(); i++) {
|
||||
auto &format = formats[i];
|
||||
|
@ -469,26 +531,30 @@ void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex,
|
|||
+ "\\Formats\\"
|
||||
+ std::to_string(i + 1);
|
||||
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
|
||||
write(prefix + "\\format", formatStr);
|
||||
write(prefix + "\\width", format.width());
|
||||
write(prefix + "\\height", format.height());
|
||||
write(prefix + "\\fps", format.minimumFrameRate().toString());
|
||||
ok &= write(prefix + "\\format", formatStr, true);
|
||||
ok &= write(prefix + "\\width", format.width(), true);
|
||||
ok &= write(prefix + "\\height", format.height(), true);
|
||||
ok &= write(prefix + "\\fps", format.minimumFrameRate().toString(), true);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index)
|
||||
bool AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto formats = cameraFormats(cameraIndex);
|
||||
|
||||
if (index < 0 || index >= int(formats.size()))
|
||||
return;
|
||||
return false;
|
||||
|
||||
bool ok = true;
|
||||
formats.erase(formats.begin() + index);
|
||||
write("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()));
|
||||
ok &= write("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Formats\\size",
|
||||
int(formats.size()),
|
||||
true);
|
||||
|
||||
for (size_t i = 0; i < formats.size(); i++) {
|
||||
auto &format = formats[i];
|
||||
|
@ -497,11 +563,15 @@ void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index)
|
|||
+ "\\Formats\\"
|
||||
+ std::to_string(i + 1);
|
||||
auto formatStr = VideoFormat::stringFromFourcc(format.fourcc());
|
||||
write(prefix + "\\format", formatStr);
|
||||
write(prefix + "\\width", format.width());
|
||||
write(prefix + "\\height", format.height());
|
||||
write(prefix + "\\fps", format.minimumFrameRate().toString());
|
||||
ok &= write(prefix + "\\format", formatStr, true);
|
||||
ok &= write(prefix + "\\width", format.width(), true);
|
||||
ok &= write(prefix + "\\height", format.height(), true);
|
||||
ok &= write(prefix + "\\fps",
|
||||
format.minimumFrameRate().toString(),
|
||||
true);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int AkVCam::Preferences::cameraControlValue(size_t cameraIndex,
|
||||
|
@ -513,15 +583,15 @@ int AkVCam::Preferences::cameraControlValue(size_t cameraIndex,
|
|||
+ key);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex,
|
||||
bool AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex,
|
||||
const std::string &key,
|
||||
int value)
|
||||
{
|
||||
write("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Controls\\"
|
||||
+ key,
|
||||
value);
|
||||
return write("Cameras\\"
|
||||
+ std::to_string(cameraIndex + 1)
|
||||
+ "\\Controls\\"
|
||||
+ key,
|
||||
value);
|
||||
}
|
||||
|
||||
std::string AkVCam::Preferences::picture()
|
||||
|
@ -529,19 +599,19 @@ std::string AkVCam::Preferences::picture()
|
|||
return readString("picture");
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::setPicture(const std::string &picture)
|
||||
bool AkVCam::Preferences::setPicture(const std::string &picture)
|
||||
{
|
||||
write("picture", picture);
|
||||
return write("picture", picture);
|
||||
}
|
||||
|
||||
int AkVCam::Preferences::logLevel()
|
||||
{
|
||||
return readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT);
|
||||
return readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT, true);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::setLogLevel(int logLevel)
|
||||
bool AkVCam::Preferences::setLogLevel(int logLevel)
|
||||
{
|
||||
write("loglevel", logLevel);
|
||||
return write("loglevel", logLevel, true);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::splitSubKey(const std::string &key,
|
||||
|
@ -564,16 +634,18 @@ void AkVCam::Preferences::splitSubKey(const std::string &key,
|
|||
bool AkVCam::Preferences::readValue(const std::string &key,
|
||||
DWORD dataTypeFlags,
|
||||
PVOID data,
|
||||
LPDWORD dataSize)
|
||||
LPDWORD dataSize,
|
||||
bool global)
|
||||
{
|
||||
AkLogFunction();
|
||||
HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER;
|
||||
std::string subKey;
|
||||
std::string val;
|
||||
splitSubKey(key, subKey, val);
|
||||
AkLogDebug() << "SubKey: " << subKey << std::endl;
|
||||
AkLogDebug() << "Value: " << val << std::endl;
|
||||
HKEY hkey = nullptr;
|
||||
auto result = RegOpenKeyExA(HKEY_CURRENT_USER,
|
||||
auto result = RegOpenKeyExA(rootKey,
|
||||
subKey.c_str(),
|
||||
0,
|
||||
KEY_READ | KEY_WOW64_64KEY,
|
||||
|
@ -594,19 +666,21 @@ bool AkVCam::Preferences::readValue(const std::string &key,
|
|||
return result == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::setValue(const std::string &key,
|
||||
bool AkVCam::Preferences::setValue(const std::string &key,
|
||||
DWORD dataType,
|
||||
LPCSTR data,
|
||||
DWORD dataSize)
|
||||
DWORD dataSize,
|
||||
bool global)
|
||||
{
|
||||
AkLogFunction();
|
||||
HKEY rootKey = global? HKEY_LOCAL_MACHINE: HKEY_CURRENT_USER;
|
||||
std::string subKey;
|
||||
std::string val;
|
||||
splitSubKey(key, subKey, val);
|
||||
AkLogDebug() << "SubKey: " << subKey << std::endl;
|
||||
AkLogDebug() << "Value: " << val << std::endl;
|
||||
HKEY hkey = nullptr;
|
||||
LONG result = RegCreateKeyExA(HKEY_CURRENT_USER,
|
||||
LONG result = RegCreateKeyExA(rootKey,
|
||||
subKey.c_str(),
|
||||
0,
|
||||
nullptr,
|
||||
|
@ -617,13 +691,15 @@ void AkVCam::Preferences::setValue(const std::string &key,
|
|||
nullptr);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return;
|
||||
return false;
|
||||
|
||||
RegSetValueExA(hkey,
|
||||
val.c_str(),
|
||||
0,
|
||||
dataType,
|
||||
reinterpret_cast<CONST BYTE *>(data),
|
||||
dataSize);
|
||||
result = RegSetValueExA(hkey,
|
||||
val.c_str(),
|
||||
0,
|
||||
dataType,
|
||||
reinterpret_cast<CONST BYTE *>(data),
|
||||
dataSize);
|
||||
RegCloseKey(hkey);
|
||||
|
||||
return result == ERROR_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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> ¶meters,
|
||||
const std::string &directory={},
|
||||
bool show=false);
|
||||
std::string manager() const;
|
||||
std::string alternativeManager() const;
|
||||
std::string alternativePlugin() const;
|
||||
};
|
||||
|
||||
static const int maxFrameWidth = 1920;
|
||||
|
@ -97,12 +106,17 @@ AkVCam::IpcBridge::IpcBridge()
|
|||
AkVCam::Logger::setLogLevel(loglevel);
|
||||
this->d->m_mainServer.start();
|
||||
this->registerPeer();
|
||||
this->d->updateDeviceSharedProperties();
|
||||
this->d->m_mainServer.connectPipeStateChanged(this,
|
||||
&IpcBridgePrivate::pipeStateChanged);
|
||||
}
|
||||
|
||||
AkVCam::IpcBridge::~IpcBridge()
|
||||
{
|
||||
this->d->m_mainServer.disconnectPipeStateChanged(this,
|
||||
&IpcBridgePrivate::pipeStateChanged);
|
||||
this->unregisterPeer();
|
||||
this->d->m_mainServer.stop(true);
|
||||
this->d->m_mainServer.stop();
|
||||
delete this->d;
|
||||
}
|
||||
|
||||
|
@ -113,6 +127,7 @@ std::string AkVCam::IpcBridge::picture() const
|
|||
|
||||
void AkVCam::IpcBridge::setPicture(const std::string &picture)
|
||||
{
|
||||
AkLogFunction();
|
||||
Preferences::setPicture(picture);
|
||||
Message message;
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED;
|
||||
|
@ -131,6 +146,7 @@ int AkVCam::IpcBridge::logLevel() const
|
|||
|
||||
void AkVCam::IpcBridge::setLogLevel(int logLevel)
|
||||
{
|
||||
AkLogFunction();
|
||||
Preferences::setLogLevel(logLevel);
|
||||
Logger::setLogLevel(logLevel);
|
||||
}
|
||||
|
@ -139,6 +155,11 @@ bool AkVCam::IpcBridge::registerPeer()
|
|||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (!this->d->m_portName.empty())
|
||||
return true;
|
||||
|
||||
AkLogDebug() << "Requesting port." << std::endl;
|
||||
|
||||
Message message;
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_REQUEST_PORT;
|
||||
message.dataSize = sizeof(MsgRequestPort);
|
||||
|
@ -149,10 +170,18 @@ bool AkVCam::IpcBridge::registerPeer()
|
|||
return false;
|
||||
|
||||
std::string portName(requestData->port);
|
||||
AkLogInfo() << "Recommended port name: " << portName << std::endl;
|
||||
|
||||
if (portName.empty()) {
|
||||
AkLogError() << "The returned por name is empty." << std::endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AkLogDebug() << "Starting message server." << std::endl;
|
||||
auto pipeName = "\\\\.\\pipe\\" + portName;
|
||||
this->d->m_messageServer.setPipeName(pipeName);
|
||||
this->d->m_messageServer.setHandlers(this->d->m_messageHandlers);
|
||||
AkLogInfo() << "Recommended port name: " << portName << std::endl;
|
||||
|
||||
if (!this->d->m_messageServer.start()) {
|
||||
AkLogError() << "Can't start message server" << std::endl;
|
||||
|
@ -160,6 +189,8 @@ bool AkVCam::IpcBridge::registerPeer()
|
|||
return false;
|
||||
}
|
||||
|
||||
AkLogInfo() << "Registering port: " << portName << std::endl;
|
||||
|
||||
message.clear();
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_ADD_PORT;
|
||||
message.dataSize = sizeof(MsgAddPort);
|
||||
|
@ -171,17 +202,17 @@ bool AkVCam::IpcBridge::registerPeer()
|
|||
pipeName.c_str(),
|
||||
(std::min<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,8 +498,18 @@ std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
|
|||
| PROCESS_VM_READ,
|
||||
FALSE,
|
||||
process[i]);
|
||||
if (!processHnd)
|
||||
continue;
|
||||
|
||||
if (!processHnd)
|
||||
continue;
|
||||
|
||||
char processName[MAX_PATH];
|
||||
memset(&processName, 0, sizeof(MAX_PATH));
|
||||
|
||||
if (GetModuleBaseNameA(processHnd,
|
||||
nullptr,
|
||||
processName,
|
||||
MAX_PATH))
|
||||
AkLogDebug() << "Enumerating modules for '" << processName << "'" << std::endl;
|
||||
|
||||
HMODULE modules[nElements];
|
||||
memset(modules, 0, nElements * sizeof(HMODULE));
|
||||
|
@ -470,7 +529,11 @@ std::vector<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> ¶meters,
|
||||
const std::string &directory,
|
||||
bool show)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (parameters.size() < 1)
|
||||
return E_FAIL;
|
||||
|
||||
auto command = parameters[0];
|
||||
std::string params;
|
||||
size_t i = 0;
|
||||
|
||||
for (auto ¶m: parameters) {
|
||||
if (i < 1) {
|
||||
i++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!params.empty())
|
||||
params += ' ';
|
||||
|
||||
auto param_ = replace(param, "\"", "\"\"\"");
|
||||
|
||||
if (param_.find(" ") == std::string::npos)
|
||||
params += param_;
|
||||
else
|
||||
params += "\"" + param_ + "\"";
|
||||
}
|
||||
|
||||
AkLogDebug() << "Command: " << command << std::endl;
|
||||
AkLogDebug() << "Arguments: " << params << std::endl;
|
||||
|
||||
SHELLEXECUTEINFOA execInfo;
|
||||
memset(&execInfo, 0, sizeof(SHELLEXECUTEINFO));
|
||||
execInfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
||||
execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||
execInfo.hwnd = nullptr;
|
||||
execInfo.lpVerb = "runas";
|
||||
execInfo.lpFile = command.data();
|
||||
execInfo.lpParameters = params.data();
|
||||
execInfo.lpDirectory = directory.data();
|
||||
execInfo.nShow = show? SW_SHOWNORMAL: SW_HIDE;
|
||||
execInfo.hInstApp = nullptr;
|
||||
ShellExecuteExA(&execInfo);
|
||||
|
||||
if (!execInfo.hProcess) {
|
||||
AkLogError() << "Failed executing command" << std::endl;
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
WaitForSingleObject(execInfo.hProcess, INFINITE);
|
||||
|
||||
DWORD exitCode;
|
||||
GetExitCodeProcess(execInfo.hProcess, &exitCode);
|
||||
CloseHandle(execInfo.hProcess);
|
||||
|
||||
if (FAILED(exitCode))
|
||||
AkLogError() << "Command failed with code "
|
||||
<< exitCode
|
||||
<< " ("
|
||||
<< stringFromError(exitCode)
|
||||
<< ")"
|
||||
<< std::endl;
|
||||
|
||||
AkLogError() << "Command exited with code " << exitCode << std::endl;
|
||||
|
||||
return int(exitCode);
|
||||
}
|
||||
|
||||
std::string AkVCam::IpcBridgePrivate::manager() const
|
||||
{
|
||||
AkLogFunction();
|
||||
auto pluginPath = locatePluginPath();
|
||||
auto path = realPath(pluginPath + "\\AkVCamManager.dll");
|
||||
|
||||
return fileExists(path)? path: std::string();
|
||||
}
|
||||
|
||||
std::string AkVCam::IpcBridgePrivate::alternativeManager() const
|
||||
{
|
||||
AkLogFunction();
|
||||
auto pluginPath = locatePluginPath();
|
||||
|
||||
#ifdef _WIN64
|
||||
auto path = realPath(pluginPath + "\\..\\x86\\AkVCamManager.dll");
|
||||
#else
|
||||
SYSTEM_INFO info;
|
||||
memset(&info, 0, sizeof(SYSTEM_INFO));
|
||||
GetNativeSystemInfo(&info);
|
||||
|
||||
if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
|
||||
return {};
|
||||
|
||||
auto path = realPath(pluginPath + "\\..\\x64\\AkVCamManager.dll");
|
||||
#endif
|
||||
|
||||
return fileExists(path)? path: std::string();
|
||||
}
|
||||
|
||||
std::string AkVCam::IpcBridgePrivate::alternativePlugin() const
|
||||
{
|
||||
AkLogFunction();
|
||||
auto pluginPath = locatePluginPath();
|
||||
|
||||
#ifdef _WIN64
|
||||
auto path = realPath(pluginPath + "\\..\\x86\\" DSHOW_PLUGIN_NAME ".dll");
|
||||
#else
|
||||
SYSTEM_INFO info;
|
||||
memset(&info, 0, sizeof(SYSTEM_INFO));
|
||||
GetNativeSystemInfo(&info);
|
||||
|
||||
if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
|
||||
return {};
|
||||
|
||||
auto path = realPath(pluginPath + "\\..\\x64\\" DSHOW_PLUGIN_NAME ".dll");
|
||||
#endif
|
||||
|
||||
return fileExists(path)? path: std::string();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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", "enable", "system/" + service);
|
||||
|
||||
component.addElevatedOperation("Execute",
|
||||
"launchctl", "load", "-w", daemonPlist,
|
||||
"launchctl", "bootstrap", "system", daemonPlist,
|
||||
"UNDOEXECUTE",
|
||||
"launchctl", "unload", "-w", daemonPlist);
|
||||
"launchctl", "bootout", "system", daemonPlist);
|
||||
|
||||
if (installer.isUninstaller())
|
||||
component.addElevatedOperation("Delete", daemonPlist);
|
||||
|
|
|
@ -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 =
|
||||
|
|
Loading…
Reference in a new issue