Added --fps flag to regulate stream. Added hacks commands.
This commit is contained in:
parent
64296f0223
commit
0957eb4be6
20 changed files with 1228 additions and 34 deletions
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -61,6 +62,15 @@ namespace AkVCam {
|
||||||
std::string helpString;
|
std::string helpString;
|
||||||
ProgramOptionsFunc func;
|
ProgramOptionsFunc func;
|
||||||
std::vector<CmdParserFlags> flags;
|
std::vector<CmdParserFlags> flags;
|
||||||
|
bool advanced {false};
|
||||||
|
|
||||||
|
CmdParserCommand();
|
||||||
|
CmdParserCommand(const std::string &command,
|
||||||
|
const std::string &arguments,
|
||||||
|
const std::string &helpString,
|
||||||
|
const ProgramOptionsFunc &func,
|
||||||
|
const std::vector<CmdParserFlags> flags,
|
||||||
|
bool advanced);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CmdParserPrivate
|
class CmdParserPrivate
|
||||||
|
@ -74,8 +84,8 @@ namespace AkVCam {
|
||||||
std::string basename(const std::string &path);
|
std::string basename(const std::string &path);
|
||||||
void printFlags(const std::vector<CmdParserFlags> &cmdFlags,
|
void printFlags(const std::vector<CmdParserFlags> &cmdFlags,
|
||||||
size_t indent);
|
size_t indent);
|
||||||
size_t maxCommandLength();
|
size_t maxCommandLength(bool showAdvancedHelp);
|
||||||
size_t maxArgumentsLength();
|
size_t maxArgumentsLength(bool showAdvancedHelp);
|
||||||
size_t maxFlagsLength(const std::vector<CmdParserFlags> &flags);
|
size_t maxFlagsLength(const std::vector<CmdParserFlags> &flags);
|
||||||
size_t maxFlagsValueLength(const std::vector<CmdParserFlags> &flags);
|
size_t maxFlagsValueLength(const std::vector<CmdParserFlags> &flags);
|
||||||
size_t maxColumnLength(const StringVector &table,
|
size_t maxColumnLength(const StringVector &table,
|
||||||
|
@ -126,6 +136,9 @@ namespace AkVCam {
|
||||||
int setLogLevel(const StringMap &flags, const StringVector &args);
|
int setLogLevel(const StringMap &flags, const StringVector &args);
|
||||||
int showClients(const StringMap &flags, const StringVector &args);
|
int showClients(const StringMap &flags, const StringVector &args);
|
||||||
int dumpInfo(const StringMap &flags, const StringVector &args);
|
int dumpInfo(const StringMap &flags, const StringVector &args);
|
||||||
|
int hacks(const StringMap &flags, const StringVector &args);
|
||||||
|
int hackInfo(const StringMap &flags, const StringVector &args);
|
||||||
|
int hack(const StringMap &flags, const StringVector &args);
|
||||||
void loadGenerals(Settings &settings);
|
void loadGenerals(Settings &settings);
|
||||||
VideoFormatMatrix readFormats(Settings &settings);
|
VideoFormatMatrix readFormats(Settings &settings);
|
||||||
std::vector<VideoFormat> readFormat(Settings &settings);
|
std::vector<VideoFormat> readFormat(Settings &settings);
|
||||||
|
@ -154,6 +167,9 @@ AkVCam::CmdParser::CmdParser()
|
||||||
this->addFlags("",
|
this->addFlags("",
|
||||||
{"-h", "--help"},
|
{"-h", "--help"},
|
||||||
"Show help.");
|
"Show help.");
|
||||||
|
this->addFlags("",
|
||||||
|
{"--help-all"},
|
||||||
|
"Show advanced help.");
|
||||||
this->addFlags("",
|
this->addFlags("",
|
||||||
{"-v", "--version"},
|
{"-v", "--version"},
|
||||||
"Show program version.");
|
"Show program version.");
|
||||||
|
@ -236,6 +252,10 @@ AkVCam::CmdParser::CmdParser()
|
||||||
"DEVICE FORMAT WIDTH HEIGHT",
|
"DEVICE FORMAT WIDTH HEIGHT",
|
||||||
"Read frames from stdin and send them to the device.",
|
"Read frames from stdin and send them to the device.",
|
||||||
AKVCAM_BIND_FUNC(CmdParserPrivate::stream));
|
AKVCAM_BIND_FUNC(CmdParserPrivate::stream));
|
||||||
|
this->addFlags("stream",
|
||||||
|
{"-f", "--fps"},
|
||||||
|
"FPS",
|
||||||
|
"Read stream input at a constant frame rate.");
|
||||||
this->addCommand("listen-events",
|
this->addCommand("listen-events",
|
||||||
"",
|
"",
|
||||||
"Keep the manager running and listening to global events.",
|
"Keep the manager running and listening to global events.",
|
||||||
|
@ -297,6 +317,30 @@ AkVCam::CmdParser::CmdParser()
|
||||||
"",
|
"",
|
||||||
"Show all information in a parseable XML format.",
|
"Show all information in a parseable XML format.",
|
||||||
AKVCAM_BIND_FUNC(CmdParserPrivate::dumpInfo));
|
AKVCAM_BIND_FUNC(CmdParserPrivate::dumpInfo));
|
||||||
|
this->addCommand("hacks",
|
||||||
|
"",
|
||||||
|
"List system hacks to make the virtual camera work.",
|
||||||
|
AKVCAM_BIND_FUNC(CmdParserPrivate::hacks),
|
||||||
|
true);
|
||||||
|
this->addCommand("hack-info",
|
||||||
|
"HACK",
|
||||||
|
"Show hack information.",
|
||||||
|
AKVCAM_BIND_FUNC(CmdParserPrivate::hackInfo),
|
||||||
|
true);
|
||||||
|
this->addFlags("hack-info",
|
||||||
|
{"-s", "--issafe"},
|
||||||
|
"Is hack safe?");
|
||||||
|
this->addFlags("hack-info",
|
||||||
|
{"-c", "--description"},
|
||||||
|
"Show hack description.");
|
||||||
|
this->addCommand("hack",
|
||||||
|
"HACK PARAMS...",
|
||||||
|
"Apply system hack.",
|
||||||
|
AKVCAM_BIND_FUNC(CmdParserPrivate::hack),
|
||||||
|
true);
|
||||||
|
this->addFlags("hack",
|
||||||
|
{"-y", "--yes"},
|
||||||
|
"Accept all risks and continue anyway.");
|
||||||
}
|
}
|
||||||
|
|
||||||
AkVCam::CmdParser::~CmdParser()
|
AkVCam::CmdParser::~CmdParser()
|
||||||
|
@ -374,7 +418,10 @@ int AkVCam::CmdParser::parse(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->d->m_ipcBridge.needsRoot(command->command))
|
if (this->d->m_ipcBridge.needsRoot(command->command)
|
||||||
|
|| (command->command == "hack"
|
||||||
|
&& arguments.size() >= 2
|
||||||
|
&& this->d->m_ipcBridge.hackNeedsRoot(arguments[1])))
|
||||||
return this->d->m_ipcBridge.sudo(argc, argv);
|
return this->d->m_ipcBridge.sudo(argc, argv);
|
||||||
|
|
||||||
return command->func(flags, arguments);
|
return command->func(flags, arguments);
|
||||||
|
@ -388,7 +435,8 @@ void AkVCam::CmdParser::setDefaultFuntion(const ProgramOptionsFunc &func)
|
||||||
void AkVCam::CmdParser::addCommand(const std::string &command,
|
void AkVCam::CmdParser::addCommand(const std::string &command,
|
||||||
const std::string &arguments,
|
const std::string &arguments,
|
||||||
const std::string &helpString,
|
const std::string &helpString,
|
||||||
const ProgramOptionsFunc &func)
|
const ProgramOptionsFunc &func,
|
||||||
|
bool advanced)
|
||||||
{
|
{
|
||||||
auto it =
|
auto it =
|
||||||
std::find_if(this->d->m_commands.begin(),
|
std::find_if(this->d->m_commands.begin(),
|
||||||
|
@ -402,12 +450,14 @@ void AkVCam::CmdParser::addCommand(const std::string &command,
|
||||||
arguments,
|
arguments,
|
||||||
helpString,
|
helpString,
|
||||||
func,
|
func,
|
||||||
{}});
|
{},
|
||||||
|
advanced});
|
||||||
} else {
|
} else {
|
||||||
it->command = command;
|
it->command = command;
|
||||||
it->arguments = arguments;
|
it->arguments = arguments;
|
||||||
it->helpString = helpString;
|
it->helpString = helpString;
|
||||||
it->func = func;
|
it->func = func;
|
||||||
|
it->advanced = advanced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,22 +547,24 @@ void AkVCam::CmdParserPrivate::printFlags(const std::vector<CmdParserFlags> &cmd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AkVCam::CmdParserPrivate::maxCommandLength()
|
size_t AkVCam::CmdParserPrivate::maxCommandLength(bool showAdvancedHelp)
|
||||||
{
|
{
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
|
|
||||||
for (auto &cmd: this->m_commands)
|
for (auto &cmd: this->m_commands)
|
||||||
length = std::max(cmd.command.size(), length);
|
if (!cmd.advanced || showAdvancedHelp)
|
||||||
|
length = std::max(cmd.command.size(), length);
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AkVCam::CmdParserPrivate::maxArgumentsLength()
|
size_t AkVCam::CmdParserPrivate::maxArgumentsLength(bool showAdvancedHelp)
|
||||||
{
|
{
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
|
|
||||||
for (auto &cmd: this->m_commands)
|
for (auto &cmd: this->m_commands)
|
||||||
length = std::max(cmd.arguments.size(), length);
|
if (!cmd.advanced || showAdvancedHelp)
|
||||||
|
length = std::max(cmd.arguments.size(), length);
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
@ -674,7 +726,9 @@ std::string AkVCam::CmdParserPrivate::flagValue(const AkVCam::StringMap &flags,
|
||||||
int AkVCam::CmdParserPrivate::defaultHandler(const StringMap &flags,
|
int AkVCam::CmdParserPrivate::defaultHandler(const StringMap &flags,
|
||||||
const StringVector &args)
|
const StringVector &args)
|
||||||
{
|
{
|
||||||
if (flags.empty() || this->containsFlag(flags, "", "-h"))
|
if (flags.empty()
|
||||||
|
|| this->containsFlag(flags, "", "-h")
|
||||||
|
|| this->containsFlag(flags, "", "--help-all"))
|
||||||
return this->showHelp(flags, args);
|
return this->showHelp(flags, args);
|
||||||
|
|
||||||
if (this->containsFlag(flags, "", "-v")) {
|
if (this->containsFlag(flags, "", "-v")) {
|
||||||
|
@ -707,11 +761,13 @@ int AkVCam::CmdParserPrivate::showHelp(const StringMap &flags,
|
||||||
std::cout << "Commands:" << std::endl;
|
std::cout << "Commands:" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
auto maxCmdLen = this->maxCommandLength();
|
bool showAdvancedHelp = this->containsFlag(flags, "", "--help-all");
|
||||||
auto maxArgsLen = this->maxArgumentsLength();
|
auto maxCmdLen = this->maxCommandLength(showAdvancedHelp);
|
||||||
|
auto maxArgsLen = this->maxArgumentsLength(showAdvancedHelp);
|
||||||
|
|
||||||
for (auto &cmd: this->m_commands) {
|
for (auto &cmd: this->m_commands) {
|
||||||
if (cmd.command.empty())
|
if (cmd.command.empty()
|
||||||
|
|| (cmd.advanced && !showAdvancedHelp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::cout << " "
|
std::cout << " "
|
||||||
|
@ -1230,6 +1286,30 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto fpsStr = this->flagValue(flags, "stream", "-f");
|
||||||
|
double fps = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
|
||||||
|
if (!fpsStr.empty()) {
|
||||||
|
p = nullptr;
|
||||||
|
fps = int(strtod(fpsStr.c_str(), &p));
|
||||||
|
|
||||||
|
if (*p) {
|
||||||
|
if (!Fraction::isFraction(fpsStr)) {
|
||||||
|
std::cerr << "The framerate must be a number or a fraction." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fps = Fraction(fpsStr).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fps <= 0 || std::isinf(fps)) {
|
||||||
|
std::cerr << "The framerate is out of range." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VideoFormat fmt(format, int(width), int(height), {{30, 1}});
|
VideoFormat fmt(format, int(width), int(height), {{30, 1}});
|
||||||
|
|
||||||
if (!this->m_ipcBridge.deviceStart(deviceId, fmt)) {
|
if (!this->m_ipcBridge.deviceStart(deviceId, fmt)) {
|
||||||
|
@ -1253,6 +1333,20 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
|
||||||
_setmode(_fileno(stdin), _O_BINARY);
|
_setmode(_fileno(stdin), _O_BINARY);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
auto clock = [] (const std::chrono::time_point<std::chrono::high_resolution_clock> &since) -> double {
|
||||||
|
return std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - since).count();
|
||||||
|
};
|
||||||
|
|
||||||
|
const double minThreshold = 0.04;
|
||||||
|
const double maxThreshold = 0.1;
|
||||||
|
const double framedupThreshold = 0.1;
|
||||||
|
const double nosyncThreshold = 10.0;
|
||||||
|
|
||||||
|
double lastPts = 0.0;
|
||||||
|
auto t0 = std::chrono::high_resolution_clock::now();
|
||||||
|
double drift = 0;
|
||||||
|
uint64_t i = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
std::cin.read(reinterpret_cast<char *>(frame.data().data()
|
std::cin.read(reinterpret_cast<char *>(frame.data().data()
|
||||||
+ bufferSize),
|
+ bufferSize),
|
||||||
|
@ -1260,7 +1354,46 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
|
||||||
bufferSize += size_t(std::cin.gcount());
|
bufferSize += size_t(std::cin.gcount());
|
||||||
|
|
||||||
if (bufferSize == frame.data().size()) {
|
if (bufferSize == frame.data().size()) {
|
||||||
this->m_ipcBridge.write(deviceId, frame);
|
if (fpsStr.empty()) {
|
||||||
|
this->m_ipcBridge.write(deviceId, frame);
|
||||||
|
} else {
|
||||||
|
double pts = double(i) / fps;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
double clock_pts = clock(t0) + drift;
|
||||||
|
double diff = pts - clock_pts;
|
||||||
|
double delay = pts - lastPts;
|
||||||
|
double syncThreshold =
|
||||||
|
std::max(minThreshold,
|
||||||
|
std::min(delay, maxThreshold));
|
||||||
|
|
||||||
|
if (!std::isnan(diff)
|
||||||
|
&& std::abs(diff) < nosyncThreshold
|
||||||
|
&& delay < framedupThreshold) {
|
||||||
|
if (diff <= -syncThreshold) {
|
||||||
|
lastPts = pts;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff > syncThreshold) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::duration<double>(diff - syncThreshold));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drift = clock(t0) - pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->m_ipcBridge.write(deviceId, frame);
|
||||||
|
lastPts = pts;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
bufferSize = 0;
|
bufferSize = 0;
|
||||||
}
|
}
|
||||||
} while (!std::cin.eof() && !exit);
|
} while (!std::cin.eof() && !exit);
|
||||||
|
@ -1365,8 +1498,6 @@ int AkVCam::CmdParserPrivate::showControls(const StringMap &flags,
|
||||||
int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
|
int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
|
||||||
const StringVector &args)
|
const StringVector &args)
|
||||||
{
|
{
|
||||||
UNUSED(flags);
|
|
||||||
|
|
||||||
if (args.size() < 3) {
|
if (args.size() < 3) {
|
||||||
std::cerr << "Not enough arguments." << std::endl;
|
std::cerr << "Not enough arguments." << std::endl;
|
||||||
|
|
||||||
|
@ -1389,9 +1520,9 @@ int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
|
||||||
std::cout << control.value << std::endl;
|
std::cout << control.value << std::endl;
|
||||||
} else {
|
} else {
|
||||||
if (this->containsFlag(flags, "get-control", "-c")) {
|
if (this->containsFlag(flags, "get-control", "-c")) {
|
||||||
auto typeStr = typeStrMap();
|
|
||||||
std::cout << control.description << std::endl;
|
std::cout << control.description << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->containsFlag(flags, "get-control", "-t")) {
|
if (this->containsFlag(flags, "get-control", "-t")) {
|
||||||
auto typeStr = typeStrMap();
|
auto typeStr = typeStrMap();
|
||||||
std::cout << typeStr[control.type] << std::endl;
|
std::cout << typeStr[control.type] << std::endl;
|
||||||
|
@ -1871,6 +2002,146 @@ int AkVCam::CmdParserPrivate::dumpInfo(const AkVCam::StringMap &flags,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AkVCam::CmdParserPrivate::hacks(const AkVCam::StringMap &flags,
|
||||||
|
const AkVCam::StringVector &args)
|
||||||
|
{
|
||||||
|
UNUSED(flags);
|
||||||
|
UNUSED(args);
|
||||||
|
|
||||||
|
auto hacks = this->m_ipcBridge.hacks();
|
||||||
|
|
||||||
|
if (hacks.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (this->m_parseable) {
|
||||||
|
for (auto &hack: hacks)
|
||||||
|
std::cout << hack << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "Hacks are intended to fix common problems with the "
|
||||||
|
"virtual camera, and are intended to be used by developers "
|
||||||
|
"and advanced users only." << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "WARNING: Unsafe hacks can brick your system, make it "
|
||||||
|
"unstable, or expose it to a serious security risk. You "
|
||||||
|
"are solely responsible of whatever happens for using "
|
||||||
|
"them. You been warned, don't come and cry later."
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::vector<std::string> table {
|
||||||
|
"Hack",
|
||||||
|
"Is safe?",
|
||||||
|
"Description"
|
||||||
|
};
|
||||||
|
auto columns = table.size();
|
||||||
|
|
||||||
|
for (auto &hack: hacks) {
|
||||||
|
table.push_back(hack);
|
||||||
|
table.push_back(this->m_ipcBridge.hackIsSafe(hack)? "Yes": "No");
|
||||||
|
table.push_back(this->m_ipcBridge.hackDescription(hack));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->drawTable(table, columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::CmdParserPrivate::hackInfo(const AkVCam::StringMap &flags,
|
||||||
|
const AkVCam::StringVector &args)
|
||||||
|
{
|
||||||
|
if (args.size() < 2) {
|
||||||
|
std::cerr << "Not enough arguments." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hack = args[1];
|
||||||
|
auto hacks = this->m_ipcBridge.hacks();
|
||||||
|
auto dit = std::find(hacks.begin(), hacks.end(), hack);
|
||||||
|
|
||||||
|
if (dit == hacks.end()) {
|
||||||
|
std::cerr << "Unknown hack: " << hack << "." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->containsFlag(flags, "hack-info", "-c"))
|
||||||
|
std::cout << this->m_ipcBridge.hackDescription(hack) << std::endl;
|
||||||
|
|
||||||
|
if (this->containsFlag(flags, "hack-info", "-s")) {
|
||||||
|
if (this->m_ipcBridge.hackIsSafe(hack))
|
||||||
|
std::cout << "Yes" << std::endl;
|
||||||
|
else
|
||||||
|
std::cout << "No" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::CmdParserPrivate::hack(const AkVCam::StringMap &flags,
|
||||||
|
const AkVCam::StringVector &args)
|
||||||
|
{
|
||||||
|
if (args.size() < 2) {
|
||||||
|
std::cerr << "Not enough arguments." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto hack = args[1];
|
||||||
|
auto hacks = this->m_ipcBridge.hacks();
|
||||||
|
auto dit = std::find(hacks.begin(), hacks.end(), hack);
|
||||||
|
|
||||||
|
if (dit == hacks.end()) {
|
||||||
|
std::cerr << "Unknown hack: " << hack << "." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accepted = this->m_parseable | this->m_ipcBridge.hackIsSafe(hack);
|
||||||
|
|
||||||
|
if (!accepted && !this->m_parseable) {
|
||||||
|
std::cout << "WARNING: Applying this hack can brick your system, make "
|
||||||
|
"it unstable, or expose it to a serious security risk. "
|
||||||
|
"Agreeing to continue, you accept the full responsability "
|
||||||
|
"of whatever happens from now on."
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
if (this->containsFlag(flags, "hack", "-y")) {
|
||||||
|
std::cout << "You agreed to continue from command line."
|
||||||
|
<< std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
accepted = true;
|
||||||
|
} else {
|
||||||
|
std::cout << "If you agree to continue write YES: ";
|
||||||
|
std::string answer;
|
||||||
|
std::cin >> answer;
|
||||||
|
std::cout << std::endl;
|
||||||
|
accepted = answer == "YES";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accepted) {
|
||||||
|
std::cerr << "Hack not applied." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringVector hargs;
|
||||||
|
|
||||||
|
for (size_t i = 2; i < args.size(); i++)
|
||||||
|
hargs.push_back(args[i]);
|
||||||
|
|
||||||
|
auto result = this->m_ipcBridge.execHack(hack, hargs);
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
std::cout << "Success" << std::endl;
|
||||||
|
else
|
||||||
|
std::cout << "Failed" << std::endl;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings)
|
void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings)
|
||||||
{
|
{
|
||||||
settings.beginGroup("General");
|
settings.beginGroup("General");
|
||||||
|
@ -2081,3 +2352,22 @@ std::string AkVCam::operator *(size_t n, const std::string &str)
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AkVCam::CmdParserCommand::CmdParserCommand()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::CmdParserCommand::CmdParserCommand(const std::string &command,
|
||||||
|
const std::string &arguments,
|
||||||
|
const std::string &helpString,
|
||||||
|
const AkVCam::ProgramOptionsFunc &func,
|
||||||
|
const std::vector<AkVCam::CmdParserFlags> flags,
|
||||||
|
bool advanced):
|
||||||
|
command(command),
|
||||||
|
arguments(arguments),
|
||||||
|
helpString(helpString),
|
||||||
|
func(func),
|
||||||
|
flags(flags),
|
||||||
|
advanced(advanced)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
@ -42,7 +42,8 @@ namespace AkVCam {
|
||||||
void addCommand(const std::string &command,
|
void addCommand(const std::string &command,
|
||||||
const std::string &arguments,
|
const std::string &arguments,
|
||||||
const std::string &helpString,
|
const std::string &helpString,
|
||||||
const ProgramOptionsFunc &func);
|
const ProgramOptionsFunc &func,
|
||||||
|
bool advanced=false);
|
||||||
void addFlags(const std::string &command,
|
void addFlags(const std::string &command,
|
||||||
const StringVector &flags,
|
const StringVector &flags,
|
||||||
const std::string &value,
|
const std::string &value,
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* Web-Site: http://webcamoid.github.io/
|
* Web-Site: http://webcamoid.github.io/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -143,6 +144,42 @@ std::string AkVCam::Fraction::toString() const
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AkVCam::Fraction::isInfinity() const
|
||||||
|
{
|
||||||
|
return this->d->m_num != 0 && this->d->m_den == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::Fraction::sign() const
|
||||||
|
{
|
||||||
|
return std::signbit(this->d->m_num) == std::signbit(this->d->m_den)? 1: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::Fraction::isFraction(const std::string &str)
|
||||||
|
{
|
||||||
|
auto pos = str.find('/');
|
||||||
|
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
auto strCpy = trimmed(str);
|
||||||
|
char *p = nullptr;
|
||||||
|
strtol(strCpy.c_str(), &p, 10);
|
||||||
|
|
||||||
|
if (*p)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
auto numStr = trimmed(str.substr(0, pos));
|
||||||
|
auto denStr = trimmed(str.substr(pos + 1));
|
||||||
|
char *p = nullptr;
|
||||||
|
char *q = nullptr;
|
||||||
|
strtol(numStr.c_str(), &p, 10);
|
||||||
|
strtol(denStr.c_str(), &q, 10);
|
||||||
|
|
||||||
|
if (*p || *q)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostream &operator <<(std::ostream &os, const AkVCam::Fraction &fraction)
|
std::ostream &operator <<(std::ostream &os, const AkVCam::Fraction &fraction)
|
||||||
{
|
{
|
||||||
os << fraction.toString();
|
os << fraction.toString();
|
||||||
|
|
|
@ -47,6 +47,9 @@ namespace AkVCam
|
||||||
int64_t &den();
|
int64_t &den();
|
||||||
double value() const;
|
double value() const;
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
|
bool isInfinity() const;
|
||||||
|
int sign() const;
|
||||||
|
static bool isFraction(const std::string &str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FractionPrivate *d;
|
FractionPrivate *d;
|
||||||
|
|
|
@ -174,6 +174,15 @@ namespace AkVCam
|
||||||
bool needsRoot(const std::string &operation) const;
|
bool needsRoot(const std::string &operation) const;
|
||||||
int sudo(int argc, char **argv) const;
|
int sudo(int argc, char **argv) const;
|
||||||
|
|
||||||
|
/* Hacks */
|
||||||
|
|
||||||
|
std::vector<std::string> hacks() const;
|
||||||
|
std::string hackDescription(const std::string &hack) const;
|
||||||
|
bool hackIsSafe(const std::string &hack) const;
|
||||||
|
bool hackNeedsRoot(const std::string &hack) const;
|
||||||
|
int execHack(const std::string &hack,
|
||||||
|
const std::vector<std::string> &args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IpcBridgePrivate *d;
|
IpcBridgePrivate *d;
|
||||||
|
|
||||||
|
|
|
@ -151,3 +151,11 @@ std::pair<std::string, std::string> AkVCam::splitOnce(const std::string &str,
|
||||||
|
|
||||||
return {first, second};
|
return {first, second};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AkVCam::move(const std::string &from, const std::string &to)
|
||||||
|
{
|
||||||
|
std::ifstream infile(from, std::ios::in | std::ios::binary);
|
||||||
|
std::ofstream outfile(to, std::ios::out | std::ios::binary);
|
||||||
|
outfile << infile.rdbuf();
|
||||||
|
std::remove(from.c_str());
|
||||||
|
}
|
||||||
|
|
|
@ -154,6 +154,7 @@ namespace AkVCam
|
||||||
std::vector<std::string> split(const std::string &str, char separator);
|
std::vector<std::string> split(const std::string &str, char separator);
|
||||||
std::pair<std::string, std::string> splitOnce(const std::string &str,
|
std::pair<std::string, std::string> splitOnce(const std::string &str,
|
||||||
const std::string &separator);
|
const std::string &separator);
|
||||||
|
void move(const std::string &from, const std::string &to);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // AKVCAMUTILS_UTILS_H
|
#endif // AKVCAMUTILS_UTILS_H
|
||||||
|
|
|
@ -2,7 +2,7 @@ version: 9.0.0.{build}-{branch}
|
||||||
os: MinGW
|
os: MinGW
|
||||||
|
|
||||||
image: Visual Studio 2019
|
image: Visual Studio 2019
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
- x64
|
- x64
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ environment:
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
- CMAKE_GENERATOR: MSYS Makefiles
|
- CMAKE_GENERATOR: MSYS Makefiles
|
||||||
- CMAKE_GENERATOR: Visual Studio 16 2019
|
- CMAKE_GENERATOR: Visual Studio 16 2019
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ports\ci\appveyor\install_deps.bat
|
- ports\ci\appveyor\install_deps.bat
|
||||||
|
|
|
@ -45,8 +45,32 @@
|
||||||
#define AKVCAM_BIND_FUNC(member) \
|
#define AKVCAM_BIND_FUNC(member) \
|
||||||
std::bind(&member, this, std::placeholders::_1, std::placeholders::_2)
|
std::bind(&member, this, std::placeholders::_1, std::placeholders::_2)
|
||||||
|
|
||||||
|
#define AKVCAM_BIND_HACK_FUNC(member) \
|
||||||
|
std::bind(&member, this, std::placeholders::_1)
|
||||||
|
|
||||||
namespace AkVCam
|
namespace AkVCam
|
||||||
{
|
{
|
||||||
|
class Hack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using HackFunc = std::function<int (const std::vector<std::string> &args)>;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
bool isSafe {false};
|
||||||
|
bool needsRoot {false};
|
||||||
|
HackFunc func;
|
||||||
|
|
||||||
|
Hack();
|
||||||
|
Hack(const std::string &name,
|
||||||
|
const std::string &description,
|
||||||
|
bool isSafe,
|
||||||
|
bool needsRoot,
|
||||||
|
const HackFunc &func);
|
||||||
|
Hack(const Hack &other);
|
||||||
|
Hack &operator =(const Hack &other);
|
||||||
|
};
|
||||||
|
|
||||||
class IpcBridgePrivate
|
class IpcBridgePrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -81,6 +105,22 @@ namespace AkVCam
|
||||||
// Utility methods
|
// Utility methods
|
||||||
bool fileExists(const std::string &path) const;
|
bool fileExists(const std::string &path) const;
|
||||||
static std::string locatePluginPath();
|
static std::string locatePluginPath();
|
||||||
|
bool isRoot() const;
|
||||||
|
std::vector<std::string> listServices() const;
|
||||||
|
std::vector<std::string> listDisabledServices() const;
|
||||||
|
inline bool isServiceLoaded(const std::string &service) const;
|
||||||
|
inline bool isServiceDisabled(const std::string &service) const;
|
||||||
|
bool readEntitlements(const std::string &app,
|
||||||
|
const std::string &output) const;
|
||||||
|
|
||||||
|
// Hacks
|
||||||
|
const std::vector<Hack> &hacks();
|
||||||
|
int setServiceUp(const std::vector<std::string> &args);
|
||||||
|
int setServiceDown(const std::vector<std::string> &args);
|
||||||
|
int disableLibraryValidation(const std::vector<std::string> &args);
|
||||||
|
int codeResign(const std::vector<std::string> &args);
|
||||||
|
int unsign(const std::vector<std::string> &args);
|
||||||
|
int disableSIP(const std::vector<std::string> &args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<IpcBridge *> m_bridges;
|
std::vector<IpcBridge *> m_bridges;
|
||||||
|
@ -763,15 +803,64 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
|
||||||
|
|
||||||
bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
|
bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
|
||||||
{
|
{
|
||||||
UNUSED(operation);
|
static const std::vector<std::string> operations;
|
||||||
|
auto it = std::find(operations.begin(), operations.end(), operation);
|
||||||
|
|
||||||
return false;
|
return it != operations.end() && !this->d->isRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AkVCam::IpcBridge::sudo(int argc, char **argv) const
|
int AkVCam::IpcBridge::sudo(int argc, char **argv) const
|
||||||
{
|
{
|
||||||
UNUSED(argc);
|
UNUSED(argc);
|
||||||
UNUSED(argv);
|
UNUSED(argv);
|
||||||
|
std::cerr << "You must run this command with administrator privileges." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> AkVCam::IpcBridge::hacks() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> hacks;
|
||||||
|
|
||||||
|
for (auto &hack: this->d->hacks())
|
||||||
|
hacks.push_back(hack.name);
|
||||||
|
|
||||||
|
return hacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AkVCam::IpcBridge::hackDescription(const std::string &hack) const
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.description;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridge::hackIsSafe(const std::string &hack) const
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.isSafe;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridge::hackNeedsRoot(const std::string &hack) const
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.needsRoot && !this->d->isRoot();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridge::execHack(const std::string &hack,
|
||||||
|
const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.func(args);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1061,3 +1150,470 @@ std::string AkVCam::IpcBridgePrivate::locatePluginPath()
|
||||||
|
|
||||||
return realPath(dirName + "/../..");
|
return realPath(dirName + "/../..");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridgePrivate::isRoot() const
|
||||||
|
{
|
||||||
|
AkLogFunction();
|
||||||
|
|
||||||
|
return getuid() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> AkVCam::IpcBridgePrivate::listServices() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> services;
|
||||||
|
auto proc = popen("launchctl list", "r");
|
||||||
|
|
||||||
|
if (proc) {
|
||||||
|
while (!feof(proc)) {
|
||||||
|
char line[1024];
|
||||||
|
|
||||||
|
if (!fgets(line, 1024, proc))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (strncmp(line, "PID", 3) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char *pline = strtok(line, " \n\r\t");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 3 && pline != nullptr; i++) {
|
||||||
|
if (i == 2)
|
||||||
|
services.push_back(pline);
|
||||||
|
|
||||||
|
pline = strtok(nullptr, " \n\r\t");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> AkVCam::IpcBridgePrivate::listDisabledServices() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> services;
|
||||||
|
auto proc = popen("launchctl print-disabled system/", "r");
|
||||||
|
|
||||||
|
if (proc) {
|
||||||
|
while (!feof(proc)) {
|
||||||
|
char line[1024];
|
||||||
|
|
||||||
|
if (!fgets(line, 1024, proc))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (strncmp(line, "\t", 1) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string service;
|
||||||
|
char *pline = strtok(line, " ");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 3 && pline; i++) {
|
||||||
|
if (i == 0)
|
||||||
|
service = std::string(pline).substr(1, strlen(pline) - 2);
|
||||||
|
|
||||||
|
if (i == 2 && strncmp(pline, "true", 4) == 0)
|
||||||
|
services.push_back(service);
|
||||||
|
|
||||||
|
pline = strtok(nullptr, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridgePrivate::isServiceLoaded(const std::string &service) const
|
||||||
|
{
|
||||||
|
auto services = this->listServices();
|
||||||
|
|
||||||
|
return std::find(services.begin(), services.end(), service) != services.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridgePrivate::isServiceDisabled(const std::string &service) const
|
||||||
|
{
|
||||||
|
auto services = this->listDisabledServices();
|
||||||
|
|
||||||
|
return std::find(services.begin(), services.end(), service) != services.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridgePrivate::readEntitlements(const std::string &app,
|
||||||
|
const std::string &output) const
|
||||||
|
{
|
||||||
|
bool writen = false;
|
||||||
|
std::string cmd = "codesign -d --entitlements - \"" + app + "\"";
|
||||||
|
auto proc = popen(cmd.c_str(), "r");
|
||||||
|
|
||||||
|
if (proc) {
|
||||||
|
auto entitlements = fopen(output.c_str(), "w");
|
||||||
|
|
||||||
|
if (entitlements) {
|
||||||
|
for (size_t i = 0; !feof(proc); i++) {
|
||||||
|
char data[1024];
|
||||||
|
auto len = fread(data, 1, 1024, proc);
|
||||||
|
|
||||||
|
if (len < 1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
offset = std::string(data, len).find("<?xml");
|
||||||
|
|
||||||
|
fwrite(data + offset, 1, len - offset, entitlements);
|
||||||
|
writen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(entitlements);
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(proc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return writen;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<AkVCam::Hack> &AkVCam::IpcBridgePrivate::hacks()
|
||||||
|
{
|
||||||
|
static const std::vector<AkVCam::Hack> hacks {
|
||||||
|
{"set-service-up",
|
||||||
|
"Setup and start virtual camera service if isn't working",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
AKVCAM_BIND_HACK_FUNC(IpcBridgePrivate::setServiceUp)},
|
||||||
|
{"set-service-down",
|
||||||
|
"Stop and unregister virtual camera service",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
AKVCAM_BIND_HACK_FUNC(IpcBridgePrivate::setServiceDown)},
|
||||||
|
{"disable-library-validation",
|
||||||
|
"Disable external plugins validation in app bundle",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
AKVCAM_BIND_HACK_FUNC(IpcBridgePrivate::disableLibraryValidation)},
|
||||||
|
{"code-re-sign",
|
||||||
|
"Remove app code signature and re-sign it with a developer signature",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
AKVCAM_BIND_HACK_FUNC(IpcBridgePrivate::codeResign)},
|
||||||
|
{"unsign",
|
||||||
|
"Remove app code signature",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
AKVCAM_BIND_HACK_FUNC(IpcBridgePrivate::unsign)},
|
||||||
|
{"disable-sip",
|
||||||
|
"Disable System Integrity Protection",
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
AKVCAM_BIND_HACK_FUNC(IpcBridgePrivate::disableSIP)}
|
||||||
|
};
|
||||||
|
|
||||||
|
return hacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::setServiceUp(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
UNUSED(args);
|
||||||
|
AkLogFunction();
|
||||||
|
std::string pluginPath = this->locatePluginPath();
|
||||||
|
static const std::string dstPluginPath =
|
||||||
|
"/Library/CoreMediaIO/Plug-Ins/DAL/" CMIO_PLUGIN_NAME ".plugin";
|
||||||
|
|
||||||
|
if (!fileExists(dstPluginPath))
|
||||||
|
if (symlink(pluginPath.c_str(), dstPluginPath.c_str()) != 0) {
|
||||||
|
std::cerr << strerror(errno) << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string daemonPlist =
|
||||||
|
"/Library/LaunchDaemons/" CMIO_ASSISTANT_NAME ".plist";
|
||||||
|
|
||||||
|
if (!fileExists(daemonPlist)) {
|
||||||
|
std::ofstream plist(daemonPlist);
|
||||||
|
|
||||||
|
if (!plist.is_open())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
plist << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||||
|
plist << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" ";
|
||||||
|
plist << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl;
|
||||||
|
plist << "<plist version=\"1.0\">" << std::endl;
|
||||||
|
plist << " <dict>" << std::endl;
|
||||||
|
plist << " <key>Label</key>" << std::endl;
|
||||||
|
plist << " <string>" CMIO_ASSISTANT_NAME "</string>" << std::endl;
|
||||||
|
plist << " <key>ProgramArguments</key>" << std::endl;
|
||||||
|
plist << " <array>" << std::endl;
|
||||||
|
plist << " <string>";
|
||||||
|
plist << pluginPath;
|
||||||
|
plist << "/Contents/Resources/AkVCamAssistant";
|
||||||
|
plist << "</string>" << std::endl;
|
||||||
|
plist << " <string>--timeout</string>" << std::endl;
|
||||||
|
plist << " <string>300.0</string>" << std::endl;
|
||||||
|
plist << " </array>" << std::endl;
|
||||||
|
plist << " <key>MachServices</key>" << std::endl;
|
||||||
|
plist << " <dict>" << std::endl;
|
||||||
|
plist << " <key>" CMIO_ASSISTANT_NAME "</key>" << std::endl;
|
||||||
|
plist << " <true/>" << std::endl;
|
||||||
|
plist << " </dict>" << std::endl;
|
||||||
|
plist << " <key>StandardOutPath</key>" << std::endl;
|
||||||
|
plist << " <string>/tmp/AkVCamAssistant.log</string>" << std::endl;
|
||||||
|
plist << " <key>StandardErrorPath</key>" << std::endl;
|
||||||
|
plist << " <string>/tmp/AkVCamAssistant.log</string>" << std::endl;
|
||||||
|
plist << " </dict>" << std::endl;
|
||||||
|
plist << "</plist>" << std::endl;
|
||||||
|
plist.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->isServiceDisabled(CMIO_ASSISTANT_NAME)) {
|
||||||
|
int result = system("launchctl enable system/" CMIO_ASSISTANT_NAME);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->isServiceLoaded(CMIO_ASSISTANT_NAME)) {
|
||||||
|
auto cmd = "launchctl bootstrap system " + daemonPlist;
|
||||||
|
int result = system(cmd.c_str());
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::setServiceDown(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
UNUSED(args);
|
||||||
|
AkLogFunction();
|
||||||
|
|
||||||
|
static const std::string daemonPlist =
|
||||||
|
"/Library/LaunchDaemons/" CMIO_ASSISTANT_NAME ".plist";
|
||||||
|
|
||||||
|
if (fileExists(daemonPlist)) {
|
||||||
|
if (this->isServiceLoaded(CMIO_ASSISTANT_NAME)) {
|
||||||
|
auto cmd = "launchctl bootout system " + daemonPlist;
|
||||||
|
int result = system(cmd.c_str());
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::remove(daemonPlist.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string dstPluginPath =
|
||||||
|
"/Library/CoreMediaIO/Plug-Ins/DAL/" CMIO_PLUGIN_NAME ".plugin";
|
||||||
|
|
||||||
|
if (this->fileExists(dstPluginPath))
|
||||||
|
if (unlink(dstPluginPath.c_str()) != 0) {
|
||||||
|
std::cerr << strerror(errno) << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::disableLibraryValidation(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
if (args.size() < 1) {
|
||||||
|
std::cerr << "Not enough arguments." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->fileExists(args[0])) {
|
||||||
|
std::cerr << "No such file or directory." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string entitlementsXml = "/tmp/entitlements.xml";
|
||||||
|
static const std::string dlv =
|
||||||
|
"com.apple.security.cs.disable-library-validation";
|
||||||
|
|
||||||
|
if (this->readEntitlements(args[0], entitlementsXml)) {
|
||||||
|
auto nsdlv = [NSString
|
||||||
|
stringWithCString: dlv.c_str()
|
||||||
|
encoding: [NSString defaultCStringEncoding]];
|
||||||
|
auto nsEntitlementsXml = [NSString
|
||||||
|
stringWithCString: entitlementsXml.c_str()
|
||||||
|
encoding: [NSString defaultCStringEncoding]];
|
||||||
|
auto nsEntitlementsXmlUrl = [NSURL
|
||||||
|
fileURLWithPath: nsEntitlementsXml];
|
||||||
|
NSError *error = nil;
|
||||||
|
auto entitlements =
|
||||||
|
[[NSXMLDocument alloc]
|
||||||
|
initWithContentsOfURL: nsEntitlementsXmlUrl
|
||||||
|
options: 0
|
||||||
|
error: &error];
|
||||||
|
[nsEntitlementsXmlUrl release];
|
||||||
|
const std::string xpath =
|
||||||
|
"/plist/dict/key[contains(text(), \"" + dlv + "\")]";
|
||||||
|
auto nsxpath = [NSString
|
||||||
|
stringWithCString: xpath.c_str()
|
||||||
|
encoding: [NSString defaultCStringEncoding]];
|
||||||
|
auto nodes = [entitlements nodesForXPath: nsxpath error: &error];
|
||||||
|
[nsxpath release];
|
||||||
|
|
||||||
|
if ([nodes count] < 1) {
|
||||||
|
auto key = [[NSXMLElement alloc] initWithName: @"key" stringValue: nsdlv];
|
||||||
|
[(NSXMLElement *) entitlements.rootElement.children[0] addChild: key];
|
||||||
|
[key release];
|
||||||
|
auto value = [[NSXMLElement alloc] initWithName: @"true"];
|
||||||
|
[(NSXMLElement *) entitlements.rootElement.children[0] addChild: value];
|
||||||
|
[value release];
|
||||||
|
} else {
|
||||||
|
for (NSXMLNode *node: nodes) {
|
||||||
|
auto value = [[NSXMLElement alloc] initWithName: @"true"];
|
||||||
|
[(NSXMLElement *) node.parent replaceChildAtIndex: node.nextSibling.index withNode: value];
|
||||||
|
[value release];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = [entitlements XMLDataWithOptions: NSXMLNodePrettyPrint
|
||||||
|
| NSXMLNodeCompactEmptyElement];
|
||||||
|
[data writeToFile: nsEntitlementsXml atomically: YES];
|
||||||
|
[data release];
|
||||||
|
[entitlements release];
|
||||||
|
[nsEntitlementsXml release];
|
||||||
|
[nsdlv release];
|
||||||
|
} else {
|
||||||
|
std::ofstream entitlements(entitlementsXml);
|
||||||
|
|
||||||
|
if (entitlements.is_open()) {
|
||||||
|
entitlements << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
|
||||||
|
entitlements << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl;
|
||||||
|
entitlements << "<plist version=\"1.0\">" << std::endl;
|
||||||
|
entitlements << "\t<dict>" << std::endl;
|
||||||
|
entitlements << "\t\t<key>" << dlv << "</key>" << std::endl;
|
||||||
|
entitlements << "\t\t<true/>" << std::endl;
|
||||||
|
entitlements << "\t</dict>" << std::endl;
|
||||||
|
entitlements << "</plist>" << std::endl;
|
||||||
|
entitlements.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cmd = "codesign --entitlements \""
|
||||||
|
+ entitlementsXml
|
||||||
|
+ "\" -f -s - \""
|
||||||
|
+ args[0]
|
||||||
|
+ "\"";
|
||||||
|
int result = system(cmd.c_str());
|
||||||
|
std::remove(entitlementsXml.c_str());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::codeResign(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
if (args.size() < 1) {
|
||||||
|
std::cerr << "Not enough arguments." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->fileExists(args[0])) {
|
||||||
|
std::cerr << "No such file or directory." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string entitlementsXml = "/tmp/entitlements.xml";
|
||||||
|
std::string cmd;
|
||||||
|
|
||||||
|
if (this->readEntitlements(args[0], entitlementsXml))
|
||||||
|
cmd = "codesign --entitlements \""
|
||||||
|
+ entitlementsXml
|
||||||
|
+ "\" -f -s - \""
|
||||||
|
+ args[0]
|
||||||
|
+ "\"";
|
||||||
|
else
|
||||||
|
cmd = "codesign -f -s - \"" + args[0] + "\"";
|
||||||
|
|
||||||
|
int result = system(cmd.c_str());
|
||||||
|
std::remove(entitlementsXml.c_str());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::unsign(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
if (args.size() < 1) {
|
||||||
|
std::cerr << "Not enough arguments." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->fileExists(args[0])) {
|
||||||
|
std::cerr << "No such file or directory." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cmd = "codesign --remove-signature \"" + args[0] + "\"";
|
||||||
|
|
||||||
|
return system(cmd.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::disableSIP(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
std::cerr << "SIP (System Integrity Protection) can't be disbled from "
|
||||||
|
"inside the system, you must reboot your system and then "
|
||||||
|
"press and hold Command + R keys on boot to enter to the "
|
||||||
|
"recovery mode, then go to Utilities > Terminal and run:"
|
||||||
|
<< std::endl;
|
||||||
|
std::cerr << std::endl;
|
||||||
|
std::cerr << "csrutil enable --without fs" << std::endl;
|
||||||
|
std::cerr << std::endl;
|
||||||
|
std::cerr << "If that does not works, then run:" << std::endl;
|
||||||
|
std::cerr << std::endl;
|
||||||
|
std::cerr << "csrutil disable" << std::endl;
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack::Hack()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack::Hack(const std::string &name,
|
||||||
|
const std::string &description,
|
||||||
|
bool isSafe,
|
||||||
|
bool needsRoot,
|
||||||
|
const Hack::HackFunc &func):
|
||||||
|
name(name),
|
||||||
|
description(description),
|
||||||
|
isSafe(isSafe),
|
||||||
|
needsRoot(needsRoot),
|
||||||
|
func(func)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack::Hack(const Hack &other):
|
||||||
|
name(other.name),
|
||||||
|
description(other.description),
|
||||||
|
isSafe(other.isSafe),
|
||||||
|
needsRoot(other.needsRoot),
|
||||||
|
func(other.func)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack &AkVCam::Hack::operator =(const Hack &other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
this->name = other.name;
|
||||||
|
this->description = other.description;
|
||||||
|
this->isSafe = other.isSafe;
|
||||||
|
this->needsRoot = other.needsRoot;
|
||||||
|
this->func = other.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ target_include_directories(Assistant
|
||||||
PRIVATE ..
|
PRIVATE ..
|
||||||
PRIVATE ../..)
|
PRIVATE ../..)
|
||||||
target_link_libraries(Assistant
|
target_link_libraries(Assistant
|
||||||
PlatformUtils
|
PlatformUtils
|
||||||
advapi32
|
advapi32
|
||||||
gdi32
|
gdi32
|
||||||
ole32
|
ole32
|
||||||
|
|
|
@ -42,7 +42,7 @@ add_library(PlatformUtils STATIC
|
||||||
add_dependencies(PlatformUtils VCamUtils)
|
add_dependencies(PlatformUtils VCamUtils)
|
||||||
target_compile_definitions(PlatformUtils PRIVATE PLATFORMUTILS_LIBRARY)
|
target_compile_definitions(PlatformUtils PRIVATE PLATFORMUTILS_LIBRARY)
|
||||||
target_include_directories(PlatformUtils PRIVATE ../..)
|
target_include_directories(PlatformUtils PRIVATE ../..)
|
||||||
target_link_libraries(PlatformUtils
|
target_link_libraries(PlatformUtils
|
||||||
VCamUtils
|
VCamUtils
|
||||||
advapi32
|
advapi32
|
||||||
kernel32
|
kernel32
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#include "VCamUtils/src/logger.h"
|
#include "VCamUtils/src/logger.h"
|
||||||
|
|
||||||
namespace AkVCam
|
namespace AkVCam
|
||||||
{
|
{
|
||||||
struct PipeThread
|
struct PipeThread
|
||||||
{
|
{
|
||||||
std::shared_ptr<std::thread> thread;
|
std::shared_ptr<std::thread> thread;
|
||||||
|
@ -400,7 +400,7 @@ void AkVCam::MessageServerPrivate::messagesLoop()
|
||||||
for (auto &thread: this->m_clientsThreads)
|
for (auto &thread: this->m_clientsThreads)
|
||||||
thread->thread->join();
|
thread->thread->join();
|
||||||
|
|
||||||
LocalFree(securityDescriptor);
|
LocalFree(securityDescriptor);
|
||||||
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
|
AKVCAM_EMIT(this->self, StateChanged, MessageServer::StateStopped)
|
||||||
AkLogDebug() << "Server stopped." << std::endl;
|
AkLogDebug() << "Server stopped." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ target_include_directories(VCamIPC
|
||||||
PRIVATE ..
|
PRIVATE ..
|
||||||
PRIVATE ../..)
|
PRIVATE ../..)
|
||||||
target_link_libraries(VCamIPC
|
target_link_libraries(VCamIPC
|
||||||
PlatformUtils
|
PlatformUtils
|
||||||
advapi32
|
advapi32
|
||||||
kernel32
|
kernel32
|
||||||
psapi)
|
psapi)
|
||||||
|
|
|
@ -49,6 +49,27 @@ namespace AkVCam
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Hack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using HackFunc = std::function<int (const std::vector<std::string> &args)>;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
bool isSafe {false};
|
||||||
|
bool needsRoot {false};
|
||||||
|
HackFunc func;
|
||||||
|
|
||||||
|
Hack();
|
||||||
|
Hack(const std::string &name,
|
||||||
|
const std::string &description,
|
||||||
|
bool isSafe,
|
||||||
|
bool needsRoot,
|
||||||
|
const HackFunc &func);
|
||||||
|
Hack(const Hack &other);
|
||||||
|
Hack &operator =(const Hack &other);
|
||||||
|
};
|
||||||
|
|
||||||
class IpcBridgePrivate
|
class IpcBridgePrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -87,9 +108,16 @@ namespace AkVCam
|
||||||
int sudo(const std::vector<std::string> ¶meters,
|
int sudo(const std::vector<std::string> ¶meters,
|
||||||
const std::string &directory={},
|
const std::string &directory={},
|
||||||
bool show=false);
|
bool show=false);
|
||||||
|
std::string assistant() const;
|
||||||
std::string manager() const;
|
std::string manager() const;
|
||||||
std::string alternativeManager() const;
|
std::string alternativeManager() const;
|
||||||
std::string alternativePlugin() const;
|
std::string alternativePlugin() const;
|
||||||
|
std::string service() const;
|
||||||
|
|
||||||
|
// Hacks
|
||||||
|
const std::vector<Hack> &hacks();
|
||||||
|
int setServiceUp(const std::vector<std::string> &args);
|
||||||
|
int setServiceDown(const std::vector<std::string> &args);
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int maxFrameWidth = 1920;
|
static const int maxFrameWidth = 1920;
|
||||||
|
@ -178,7 +206,7 @@ bool AkVCam::IpcBridge::registerPeer()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AkLogDebug() << "Starting message server." << std::endl;
|
AkLogDebug() << "Starting message server." << std::endl;
|
||||||
auto pipeName = "\\\\.\\pipe\\" + portName;
|
auto pipeName = "\\\\.\\pipe\\" + portName;
|
||||||
this->d->m_messageServer.setPipeName(pipeName);
|
this->d->m_messageServer.setPipeName(pipeName);
|
||||||
this->d->m_messageServer.setHandlers(this->d->m_messageHandlers);
|
this->d->m_messageServer.setHandlers(this->d->m_messageHandlers);
|
||||||
|
@ -244,7 +272,7 @@ void AkVCam::IpcBridge::unregisterPeer()
|
||||||
(std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
|
(std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
|
||||||
MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME,
|
MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME,
|
||||||
&message);
|
&message);
|
||||||
this->d->m_messageServer.stop();
|
this->d->m_messageServer.stop();
|
||||||
this->d->m_sharedMemory.setName({});
|
this->d->m_sharedMemory.setName({});
|
||||||
this->d->m_globalMutex = {};
|
this->d->m_globalMutex = {};
|
||||||
this->d->m_portName.clear();
|
this->d->m_portName.clear();
|
||||||
|
@ -465,7 +493,7 @@ std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
|
||||||
std::vector<std::string> plugins;
|
std::vector<std::string> plugins;
|
||||||
|
|
||||||
// First check for the existence of the main plugin binary.
|
// First check for the existence of the main plugin binary.
|
||||||
auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll";
|
auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll";
|
||||||
AkLogDebug() << "Plugin binary: " << path << std::endl;
|
AkLogDebug() << "Plugin binary: " << path << std::endl;
|
||||||
|
|
||||||
if (fileExists(path))
|
if (fileExists(path))
|
||||||
|
@ -826,15 +854,15 @@ bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
|
||||||
{
|
{
|
||||||
static const std::vector<std::string> operations {
|
static const std::vector<std::string> operations {
|
||||||
"add-device",
|
"add-device",
|
||||||
|
"add-format",
|
||||||
|
"load",
|
||||||
"remove-device",
|
"remove-device",
|
||||||
"remove-devices",
|
"remove-devices",
|
||||||
"set-description",
|
|
||||||
"add-format",
|
|
||||||
"remove-format",
|
"remove-format",
|
||||||
"remove-formats",
|
"remove-formats",
|
||||||
"update",
|
"set-description",
|
||||||
"load",
|
|
||||||
"set-loglevel",
|
"set-loglevel",
|
||||||
|
"update"
|
||||||
};
|
};
|
||||||
|
|
||||||
auto it = std::find(operations.begin(), operations.end(), operation);
|
auto it = std::find(operations.begin(), operations.end(), operation);
|
||||||
|
@ -853,6 +881,53 @@ int AkVCam::IpcBridge::sudo(int argc, char **argv) const
|
||||||
return this->d->sudo(arguments);
|
return this->d->sudo(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> AkVCam::IpcBridge::hacks() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> hacks;
|
||||||
|
|
||||||
|
for (auto &hack: this->d->hacks())
|
||||||
|
hacks.push_back(hack.name);
|
||||||
|
|
||||||
|
return hacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AkVCam::IpcBridge::hackDescription(const std::string &hack) const
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.description;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridge::hackIsSafe(const std::string &hack) const
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.isSafe;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AkVCam::IpcBridge::hackNeedsRoot(const std::string &hack) const
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.needsRoot && !this->d->isRoot();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridge::execHack(const std::string &hack,
|
||||||
|
const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
for (auto &hck: this->d->hacks())
|
||||||
|
if (hck.name == hack)
|
||||||
|
return hck.func(args);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
|
AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
|
||||||
self(self)
|
self(self)
|
||||||
{
|
{
|
||||||
|
@ -1180,6 +1255,31 @@ int AkVCam::IpcBridgePrivate::sudo(const std::vector<std::string> ¶meters,
|
||||||
return int(exitCode);
|
return int(exitCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string AkVCam::IpcBridgePrivate::assistant() const
|
||||||
|
{
|
||||||
|
AkLogFunction();
|
||||||
|
auto pluginPath = locatePluginPath();
|
||||||
|
auto path = realPath(pluginPath + "\\" DSHOW_PLUGIN_ASSISTANT_NAME ".exe");
|
||||||
|
|
||||||
|
if (fileExists(path))
|
||||||
|
return path;
|
||||||
|
|
||||||
|
#ifdef _WIN64
|
||||||
|
path = realPath(pluginPath + "\\..\\x86\\" DSHOW_PLUGIN_ASSISTANT_NAME ".exe");
|
||||||
|
#else
|
||||||
|
SYSTEM_INFO info;
|
||||||
|
memset(&info, 0, sizeof(SYSTEM_INFO));
|
||||||
|
GetNativeSystemInfo(&info);
|
||||||
|
|
||||||
|
if (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
path = realPath(pluginPath + "\\..\\x64\\" DSHOW_PLUGIN_ASSISTANT_NAME ".exe");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return fileExists(path)? path: std::string();
|
||||||
|
}
|
||||||
|
|
||||||
std::string AkVCam::IpcBridgePrivate::manager() const
|
std::string AkVCam::IpcBridgePrivate::manager() const
|
||||||
{
|
{
|
||||||
AkLogFunction();
|
AkLogFunction();
|
||||||
|
@ -1230,3 +1330,188 @@ std::string AkVCam::IpcBridgePrivate::alternativePlugin() const
|
||||||
|
|
||||||
return fileExists(path)? path: std::string();
|
return fileExists(path)? path: std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string AkVCam::IpcBridgePrivate::service() const
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
auto manager = OpenSCManager(nullptr, nullptr, GENERIC_READ);
|
||||||
|
|
||||||
|
if (manager) {
|
||||||
|
auto service = OpenServiceA(manager,
|
||||||
|
DSHOW_PLUGIN_ASSISTANT_NAME,
|
||||||
|
SERVICE_QUERY_CONFIG);
|
||||||
|
|
||||||
|
if (service) {
|
||||||
|
DWORD bytesNeeded = 0;
|
||||||
|
QueryServiceConfig(service, nullptr, 0, &bytesNeeded);
|
||||||
|
auto bufSize = bytesNeeded;
|
||||||
|
auto serviceConfig =
|
||||||
|
reinterpret_cast<LPQUERY_SERVICE_CONFIGA>(LocalAlloc(LMEM_FIXED,
|
||||||
|
bufSize));
|
||||||
|
if (serviceConfig) {
|
||||||
|
if (QueryServiceConfigA(service,
|
||||||
|
serviceConfig,
|
||||||
|
bufSize,
|
||||||
|
&bytesNeeded)) {
|
||||||
|
path = std::string(serviceConfig->lpBinaryPathName);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalFree(serviceConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseServiceHandle(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseServiceHandle(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<AkVCam::Hack> &AkVCam::IpcBridgePrivate::hacks()
|
||||||
|
{
|
||||||
|
static const std::vector<AkVCam::Hack> hacks {
|
||||||
|
{"set-service-up",
|
||||||
|
"Setup and start virtual camera service if isn't working",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
AKVCAM_BIND_FUNC(IpcBridgePrivate::setServiceUp)},
|
||||||
|
{"set-service-down",
|
||||||
|
"Stop and unregister virtual camera service",
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
AKVCAM_BIND_FUNC(IpcBridgePrivate::setServiceDown)}
|
||||||
|
};
|
||||||
|
|
||||||
|
return hacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::setServiceUp(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
UNUSED(args);
|
||||||
|
AkLogFunction();
|
||||||
|
|
||||||
|
// If the service is not installed, install it.
|
||||||
|
auto servicePath = this->service();
|
||||||
|
|
||||||
|
if (servicePath.empty()) {
|
||||||
|
auto assistant = this->assistant();
|
||||||
|
|
||||||
|
if (assistant.empty())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
auto result = this->sudo({assistant, "--install"});
|
||||||
|
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the service.
|
||||||
|
bool result = false;
|
||||||
|
auto manager = OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT);
|
||||||
|
|
||||||
|
if (manager) {
|
||||||
|
auto service = OpenService(manager,
|
||||||
|
TEXT(DSHOW_PLUGIN_ASSISTANT_NAME),
|
||||||
|
SERVICE_START);
|
||||||
|
|
||||||
|
if (service) {
|
||||||
|
result = StartService(service, 0, nullptr);
|
||||||
|
CloseServiceHandle(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseServiceHandle(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result? 0: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AkVCam::IpcBridgePrivate::setServiceDown(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
UNUSED(args);
|
||||||
|
AkLogFunction();
|
||||||
|
auto servicePath = this->service();
|
||||||
|
|
||||||
|
if (servicePath.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Stop the service.
|
||||||
|
bool stopped = false;
|
||||||
|
auto manager = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
||||||
|
|
||||||
|
if (manager) {
|
||||||
|
auto service = OpenService(manager,
|
||||||
|
TEXT(DSHOW_PLUGIN_ASSISTANT_NAME),
|
||||||
|
SERVICE_STOP | SERVICE_QUERY_STATUS);
|
||||||
|
|
||||||
|
if (service) {
|
||||||
|
SERVICE_STATUS status;
|
||||||
|
memset(&status, 0, sizeof(SERVICE_STATUS));
|
||||||
|
|
||||||
|
if (ControlService(service, SERVICE_CONTROL_STOP, &status)) {
|
||||||
|
memset(&status, 0, sizeof(SERVICE_STATUS));
|
||||||
|
|
||||||
|
while(QueryServiceStatus(service, &status)) {
|
||||||
|
if (status.dwCurrentState != SERVICE_STOP_PENDING)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Sleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopped = status.dwCurrentState == SERVICE_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseServiceHandle(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseServiceHandle(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stopped)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Unistall the service.
|
||||||
|
|
||||||
|
return this->sudo({servicePath, "--uninstall"});
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack::Hack()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack::Hack(const std::string &name,
|
||||||
|
const std::string &description,
|
||||||
|
bool isSafe,
|
||||||
|
bool needsRoot,
|
||||||
|
const Hack::HackFunc &func):
|
||||||
|
name(name),
|
||||||
|
description(description),
|
||||||
|
isSafe(isSafe),
|
||||||
|
needsRoot(needsRoot),
|
||||||
|
func(func)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack::Hack(const Hack &other):
|
||||||
|
name(other.name),
|
||||||
|
description(other.description),
|
||||||
|
isSafe(other.isSafe),
|
||||||
|
needsRoot(other.needsRoot),
|
||||||
|
func(other.func)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AkVCam::Hack &AkVCam::Hack::operator =(const Hack &other)
|
||||||
|
{
|
||||||
|
if (this != &other) {
|
||||||
|
this->name = other.name;
|
||||||
|
this->description = other.description;
|
||||||
|
this->isSafe = other.isSafe;
|
||||||
|
this->needsRoot = other.needsRoot;
|
||||||
|
this->func = other.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
|
@ -203,7 +203,9 @@ HRESULT AkVCam::Pin::stateChanged(void *userData, FILTER_STATE state)
|
||||||
return VFW_E_NOT_COMMITTED;
|
return VFW_E_NOT_COMMITTED;
|
||||||
|
|
||||||
self->d->updateTestFrame();
|
self->d->updateTestFrame();
|
||||||
|
self->d->m_mutex.lock();
|
||||||
self->d->m_currentFrame = self->d->m_testFrameAdapted;
|
self->d->m_currentFrame = self->d->m_testFrameAdapted;
|
||||||
|
self->d->m_mutex.unlock();
|
||||||
self->d->m_pts = -1;
|
self->d->m_pts = -1;
|
||||||
self->d->m_ptsDrift = 0;
|
self->d->m_ptsDrift = 0;
|
||||||
|
|
||||||
|
@ -241,7 +243,9 @@ HRESULT AkVCam::Pin::stateChanged(void *userData, FILTER_STATE state)
|
||||||
CloseHandle(self->d->m_sendFrameEvent);
|
CloseHandle(self->d->m_sendFrameEvent);
|
||||||
self->d->m_sendFrameEvent = nullptr;
|
self->d->m_sendFrameEvent = nullptr;
|
||||||
self->d->m_memAllocator->Decommit();
|
self->d->m_memAllocator->Decommit();
|
||||||
|
self->d->m_mutex.lock();
|
||||||
self->d->m_currentFrame.clear();
|
self->d->m_currentFrame.clear();
|
||||||
|
self->d->m_mutex.unlock();
|
||||||
self->d->m_testFrameAdapted.clear();
|
self->d->m_testFrameAdapted.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
0
ports/ci/appveyor/build.bat
Executable file → Normal file
0
ports/ci/appveyor/build.bat
Executable file → Normal file
0
ports/ci/appveyor/deploy.bat
Executable file → Normal file
0
ports/ci/appveyor/deploy.bat
Executable file → Normal file
0
ports/ci/appveyor/install_deps.bat
Executable file → Normal file
0
ports/ci/appveyor/install_deps.bat
Executable file → Normal file
0
ports/ci/appveyor/push_artifacts.bat
Executable file → Normal file
0
ports/ci/appveyor/push_artifacts.bat
Executable file → Normal file
0
ports/ci/appveyor/upload.bat
Executable file → Normal file
0
ports/ci/appveyor/upload.bat
Executable file → Normal file
Loading…
Reference in a new issue