Many improvements:
- Devices controls working. - Placeholder picture now supports PNG and JPEG formats. - Placeholder picture can be updated on fly. - Clients listing working. - Device configurations can be loaded at once. - Print better looking tables in the manager.
This commit is contained in:
parent
39f383560c
commit
39c84d9788
29 changed files with 1315 additions and 902 deletions
|
@ -21,10 +21,12 @@
|
|||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
#include "cmdparser.h"
|
||||
#include "VCamUtils/src/ipcbridge.h"
|
||||
#include "VCamUtils/src/settings.h"
|
||||
#include "VCamUtils/src/image/videoformat.h"
|
||||
#include "VCamUtils/src/image/videoframe.h"
|
||||
#include "VCamUtils/src/logger/logger.h"
|
||||
|
@ -33,6 +35,9 @@
|
|||
std::bind(&member, this->d, std::placeholders::_1, std::placeholders::_2)
|
||||
|
||||
namespace AkVCam {
|
||||
using StringMatrix = std::vector<StringVector>;
|
||||
using VideoFormatMatrix = std::vector<std::vector<VideoFormat>>;
|
||||
|
||||
struct CmdParserFlags
|
||||
{
|
||||
StringVector flags;
|
||||
|
@ -56,6 +61,7 @@ namespace AkVCam {
|
|||
IpcBridge m_ipcBridge;
|
||||
bool m_parseable {false};
|
||||
|
||||
static const std::map<ControlType, std::string> &typeStrMap();
|
||||
std::string basename(const std::string &path);
|
||||
void printFlags(const std::vector<CmdParserFlags> &cmdFlags,
|
||||
size_t indent);
|
||||
|
@ -63,8 +69,13 @@ namespace AkVCam {
|
|||
size_t maxArgumentsLength();
|
||||
size_t maxFlagsLength(const std::vector<CmdParserFlags> &flags);
|
||||
size_t maxFlagsValueLength(const std::vector<CmdParserFlags> &flags);
|
||||
size_t maxStringLength(const StringVector &strings);
|
||||
size_t maxStringLength(const WStringVector &strings);
|
||||
size_t maxColumnLength(const StringVector &table,
|
||||
size_t width,
|
||||
size_t column);
|
||||
std::vector<size_t> maxColumnsLength(const StringVector &table,
|
||||
size_t width);
|
||||
void drawTableHLine(const std::vector<size_t> &columnsLength);
|
||||
void drawTable(const StringVector &table, size_t width);
|
||||
CmdParserCommand *parserCommand(const std::string &command);
|
||||
const CmdParserFlags *parserFlag(const std::vector<CmdParserFlags> &cmdFlags,
|
||||
const std::string &flag);
|
||||
|
@ -96,12 +107,26 @@ namespace AkVCam {
|
|||
int stream(const StringMap &flags, const StringVector &args);
|
||||
int showControls(const StringMap &flags, const StringVector &args);
|
||||
int readControl(const StringMap &flags, const StringVector &args);
|
||||
int writeControl(const StringMap &flags, const StringVector &args);
|
||||
int writeControls(const StringMap &flags, const StringVector &args);
|
||||
int picture(const StringMap &flags, const StringVector &args);
|
||||
int setPicture(const StringMap &flags, const StringVector &args);
|
||||
int logLevel(const StringMap &flags, const StringVector &args);
|
||||
int setLogLevel(const StringMap &flags, const StringVector &args);
|
||||
int showClients(const StringMap &flags, const StringVector &args);
|
||||
void loadGenerals(Settings &settings);
|
||||
VideoFormatMatrix readFormats(Settings &settings);
|
||||
std::vector<VideoFormat> readFormat(Settings &settings);
|
||||
StringMatrix matrixCombine(const StringMatrix &matrix);
|
||||
void matrixCombineP(const StringMatrix &matrix,
|
||||
size_t index,
|
||||
StringVector combined,
|
||||
StringMatrix &combinations);
|
||||
void createDevices(Settings &settings,
|
||||
const VideoFormatMatrix &availableFormats);
|
||||
void createDevice(Settings &settings,
|
||||
const VideoFormatMatrix &availableFormats);
|
||||
std::vector<VideoFormat> readDeviceFormats(Settings &settings,
|
||||
const VideoFormatMatrix &availableFormats);
|
||||
};
|
||||
|
||||
std::string operator *(const std::string &str, size_t n);
|
||||
|
@ -192,10 +217,31 @@ AkVCam::CmdParser::CmdParser()
|
|||
"DEVICE CONTROL",
|
||||
"Read device control.",
|
||||
AKVCAM_BIND_FUNC(CmdParserPrivate::readControl));
|
||||
this->addCommand("set-control",
|
||||
"DEVICE CONTROL VALUE",
|
||||
"Write device control value.",
|
||||
AKVCAM_BIND_FUNC(CmdParserPrivate::writeControl));
|
||||
this->addFlags("get-control",
|
||||
{"-c", "--description"},
|
||||
"Show control description.");
|
||||
this->addFlags("get-control",
|
||||
{"-t", "--type"},
|
||||
"Show control type.");
|
||||
this->addFlags("get-control",
|
||||
{"-m", "--min"},
|
||||
"Show minimum value for the control.");
|
||||
this->addFlags("get-control",
|
||||
{"-M", "--max"},
|
||||
"Show maximum value for the control.");
|
||||
this->addFlags("get-control",
|
||||
{"-s", "--step"},
|
||||
"Show increment/decrement step for the control.");
|
||||
this->addFlags("get-control",
|
||||
{"-d", "--default"},
|
||||
"Show default value for the control.");
|
||||
this->addFlags("get-control",
|
||||
{"-l", "--menu"},
|
||||
"Show options of a memu type control.");
|
||||
this->addCommand("set-controls",
|
||||
"DEVICE CONTROL_1=VALUE CONTROL_2=VALUE...",
|
||||
"Write device controls values.",
|
||||
AKVCAM_BIND_FUNC(CmdParserPrivate::writeControls));
|
||||
this->addCommand("picture",
|
||||
"",
|
||||
"Placeholder picture to show when no streaming.",
|
||||
|
@ -352,6 +398,17 @@ void AkVCam::CmdParser::addFlags(const std::string &command,
|
|||
this->addFlags(command, flags, "", helpString);
|
||||
}
|
||||
|
||||
const std::map<AkVCam::ControlType, std::string> &AkVCam::CmdParserPrivate::typeStrMap()
|
||||
{
|
||||
static const std::map<ControlType, std::string> typeStr {
|
||||
{ControlTypeInteger, "Integer"},
|
||||
{ControlTypeBoolean, "Boolean"},
|
||||
{ControlTypeMenu , "Menu" },
|
||||
};
|
||||
|
||||
return typeStr;
|
||||
}
|
||||
|
||||
std::string AkVCam::CmdParserPrivate::basename(const std::string &path)
|
||||
{
|
||||
auto rit =
|
||||
|
@ -393,10 +450,8 @@ void AkVCam::CmdParserPrivate::printFlags(const std::vector<CmdParserFlags> &cmd
|
|||
std::cout << std::string(spaces.data(), indent)
|
||||
<< fill(allFlags, maxFlagsLen);
|
||||
|
||||
if (maxFlagsValueLen > 0) {
|
||||
std::cout << " "
|
||||
<< fill(flag.value, maxFlagsValueLen);
|
||||
}
|
||||
if (maxFlagsValueLen > 0)
|
||||
std::cout << " " << fill(flag.value, maxFlagsValueLen);
|
||||
|
||||
std::cout << " "
|
||||
<< flag.helpString
|
||||
|
@ -444,24 +499,64 @@ size_t AkVCam::CmdParserPrivate::maxFlagsValueLength(const std::vector<CmdParser
|
|||
return length;
|
||||
}
|
||||
|
||||
size_t AkVCam::CmdParserPrivate::maxStringLength(const StringVector &strings)
|
||||
size_t AkVCam::CmdParserPrivate::maxColumnLength(const AkVCam::StringVector &table,
|
||||
size_t width,
|
||||
size_t column)
|
||||
{
|
||||
size_t length = 0;
|
||||
size_t height = table.size() / width;
|
||||
|
||||
for (auto &str: strings)
|
||||
for (size_t y = 0; y < height; y++) {
|
||||
auto &str = table[y * width + column];
|
||||
length = std::max(str.size(), length);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t AkVCam::CmdParserPrivate::maxStringLength(const WStringVector &strings)
|
||||
std::vector<size_t> AkVCam::CmdParserPrivate::maxColumnsLength(const AkVCam::StringVector &table,
|
||||
size_t width)
|
||||
{
|
||||
size_t length = 0;
|
||||
std::vector<size_t> lengths;
|
||||
|
||||
for (auto &str: strings)
|
||||
length = std::max(str.size(), length);
|
||||
for (size_t x = 0; x < width; x++)
|
||||
lengths.push_back(this->maxColumnLength(table, width, x));
|
||||
|
||||
return length;
|
||||
return lengths;
|
||||
}
|
||||
|
||||
void AkVCam::CmdParserPrivate::drawTableHLine(const std::vector<size_t> &columnsLength)
|
||||
{
|
||||
std::cout << '+';
|
||||
|
||||
for (auto &len: columnsLength)
|
||||
std::cout << std::string("-") * (len + 2) << '+';
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void AkVCam::CmdParserPrivate::drawTable(const AkVCam::StringVector &table,
|
||||
size_t width)
|
||||
{
|
||||
size_t height = table.size() / width;
|
||||
auto columnsLength = this->maxColumnsLength(table, width);
|
||||
this->drawTableHLine(columnsLength);
|
||||
|
||||
for (size_t y = 0; y < height; y++) {
|
||||
std::cout << "|";
|
||||
|
||||
for (size_t x = 0; x < width; x++) {
|
||||
auto &element = table[x + y * width];
|
||||
std::cout << " " << fill(element, columnsLength[x]) << " |";
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
if (y == 0 && height > 1)
|
||||
this->drawTableHLine(columnsLength);
|
||||
}
|
||||
|
||||
this->drawTableHLine(columnsLength);
|
||||
}
|
||||
|
||||
AkVCam::CmdParserCommand *AkVCam::CmdParserPrivate::parserCommand(const std::string &command)
|
||||
|
@ -609,36 +704,21 @@ int AkVCam::CmdParserPrivate::showDevices(const StringMap &flags,
|
|||
for (auto &device: devices)
|
||||
std::cout << device << std::endl;
|
||||
} else {
|
||||
StringVector devicesColumn;
|
||||
WStringVector descriptionsColumn;
|
||||
|
||||
devicesColumn.push_back("Device");
|
||||
descriptionsColumn.push_back(L"Description");
|
||||
std::vector<std::string> table {
|
||||
"Device",
|
||||
"Description"
|
||||
};
|
||||
auto columns = table.size();
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
|
||||
|
||||
for (auto &device: devices) {
|
||||
devicesColumn.push_back(device);
|
||||
descriptionsColumn.push_back(this->m_ipcBridge.description(device));
|
||||
table.push_back(device);
|
||||
auto description =
|
||||
cv.to_bytes(this->m_ipcBridge.description(device));
|
||||
table.push_back(description);
|
||||
}
|
||||
|
||||
auto devicesColumnSize = this->maxStringLength(devicesColumn);
|
||||
auto descriptionsColumnSize = this->maxStringLength(descriptionsColumn);
|
||||
|
||||
std::cout << fill("Device", devicesColumnSize)
|
||||
<< " | "
|
||||
<< fill("Description", descriptionsColumnSize)
|
||||
<< std::endl;
|
||||
std::cout << std::string("-")
|
||||
* (devicesColumnSize
|
||||
+ descriptionsColumnSize
|
||||
+ 4) << std::endl;
|
||||
|
||||
for (auto &device: devices) {
|
||||
std::cout << fill(device, devicesColumnSize)
|
||||
<< " | ";
|
||||
std::wcout << fill(this->m_ipcBridge.description(device),
|
||||
descriptionsColumnSize)
|
||||
<< std::endl;
|
||||
}
|
||||
this->drawTable(table, columns);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1003,7 +1083,7 @@ int AkVCam::CmdParserPrivate::update(const StringMap &flags,
|
|||
{
|
||||
UNUSED(flags);
|
||||
UNUSED(args);
|
||||
this->m_ipcBridge.update();
|
||||
this->m_ipcBridge.updateDevices();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1011,6 +1091,30 @@ int AkVCam::CmdParserPrivate::update(const StringMap &flags,
|
|||
int AkVCam::CmdParserPrivate::loadSettings(const AkVCam::StringMap &flags,
|
||||
const AkVCam::StringVector &args)
|
||||
{
|
||||
UNUSED(flags);
|
||||
|
||||
if (args.size() < 2) {
|
||||
std::cerr << "Settings file not provided." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Settings settings;
|
||||
|
||||
if (!settings.load(args[1])) {
|
||||
std::cerr << "Settings file not valid." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
this->loadGenerals(settings);
|
||||
auto devices = this->m_ipcBridge.devices();
|
||||
|
||||
for (auto &device: devices)
|
||||
this->m_ipcBridge.removeDevice(device);
|
||||
|
||||
this->createDevices(settings, this->readFormats(settings));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1108,18 +1212,279 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
|
|||
int AkVCam::CmdParserPrivate::showControls(const StringMap &flags,
|
||||
const StringVector &args)
|
||||
{
|
||||
UNUSED(flags);
|
||||
|
||||
if (args.size() < 2) {
|
||||
std::cerr << "Device not provided." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto deviceId = args[1];
|
||||
auto devices = this->m_ipcBridge.devices();
|
||||
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
||||
|
||||
if (dit == devices.end()) {
|
||||
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (this->m_parseable) {
|
||||
for (auto &control: this->m_ipcBridge.controls(deviceId))
|
||||
std::cout << control.id << std::endl;
|
||||
} else {
|
||||
auto typeStr = typeStrMap();
|
||||
|
||||
std::vector<std::string> table {
|
||||
"Control",
|
||||
"Description",
|
||||
"Type",
|
||||
"Minimum",
|
||||
"Maximum",
|
||||
"Step",
|
||||
"Default",
|
||||
"Value"
|
||||
};
|
||||
auto columns = table.size();
|
||||
|
||||
for (auto &control: this->m_ipcBridge.controls(deviceId)) {
|
||||
table.push_back(control.id);
|
||||
table.push_back(control.description);
|
||||
table.push_back(typeStr[control.type]);
|
||||
table.push_back(std::to_string(control.minimum));
|
||||
table.push_back(std::to_string(control.maximum));
|
||||
table.push_back(std::to_string(control.step));
|
||||
table.push_back(std::to_string(control.defaultValue));
|
||||
table.push_back(std::to_string(control.value));
|
||||
}
|
||||
|
||||
this->drawTable(table, columns);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
|
||||
const StringVector &args)
|
||||
{
|
||||
UNUSED(flags);
|
||||
|
||||
if (args.size() < 3) {
|
||||
std::cerr << "Not enough arguments." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto deviceId = args[1];
|
||||
auto devices = this->m_ipcBridge.devices();
|
||||
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
||||
|
||||
if (dit == devices.end()) {
|
||||
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (auto &control: this->m_ipcBridge.controls(deviceId))
|
||||
if (control.description == args[2]) {
|
||||
if (flags.empty()) {
|
||||
std::cout << control.value << std::endl;
|
||||
} else {
|
||||
if (this->containsFlag(flags, "get-control", "-c")) {
|
||||
auto typeStr = typeStrMap();
|
||||
std::cout << control.description << std::endl;
|
||||
}
|
||||
if (this->containsFlag(flags, "get-control", "-t")) {
|
||||
auto typeStr = typeStrMap();
|
||||
std::cout << typeStr[control.type] << std::endl;
|
||||
}
|
||||
|
||||
if (this->containsFlag(flags, "get-control", "-m")) {
|
||||
std::cout << control.minimum << std::endl;
|
||||
}
|
||||
|
||||
if (this->containsFlag(flags, "get-control", "-M")) {
|
||||
std::cout << control.maximum << std::endl;
|
||||
}
|
||||
|
||||
if (this->containsFlag(flags, "get-control", "-s")) {
|
||||
std::cout << control.step << std::endl;
|
||||
}
|
||||
|
||||
if (this->containsFlag(flags, "get-control", "-d")) {
|
||||
std::cout << control.defaultValue << std::endl;
|
||||
}
|
||||
|
||||
if (this->containsFlag(flags, "get-control", "-l")) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AkVCam::CmdParserPrivate::writeControl(const StringMap &flags,
|
||||
std::cerr << "'" << args[2] << "' control not available." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AkVCam::CmdParserPrivate::writeControls(const StringMap &flags,
|
||||
const StringVector &args)
|
||||
{
|
||||
UNUSED(flags);
|
||||
|
||||
if (args.size() < 3) {
|
||||
std::cerr << "Not enough arguments." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto deviceId = args[1];
|
||||
auto devices = this->m_ipcBridge.devices();
|
||||
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
||||
|
||||
if (dit == devices.end()) {
|
||||
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::map<std::string, int> controls;
|
||||
|
||||
for (size_t i = 2; i < args.size(); i++) {
|
||||
if (args[i].find('=') == std::string::npos) {
|
||||
std::cerr << "Argumment "
|
||||
<< i
|
||||
<< " is not in the form KEY=VALUE."
|
||||
<< std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto pair = splitOnce(args[i], "=");
|
||||
|
||||
if (pair.first.empty()) {
|
||||
std::cerr << "Key for argumment "
|
||||
<< i
|
||||
<< " is emty."
|
||||
<< std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto key = trimmed(pair.first);
|
||||
auto value = trimmed(pair.second);
|
||||
bool found = false;
|
||||
|
||||
for (auto &control: this->m_ipcBridge.controls(deviceId))
|
||||
if (control.id == key) {
|
||||
switch (control.type) {
|
||||
case ControlTypeInteger: {
|
||||
char *p = nullptr;
|
||||
auto val = strtol(value.c_str(), &p, 10);
|
||||
|
||||
if (*p) {
|
||||
std::cerr << "Value at argument "
|
||||
<< i
|
||||
<< " must be an integer."
|
||||
<< std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
controls[key] = val;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ControlTypeBoolean: {
|
||||
std::locale loc;
|
||||
std::transform(value.begin(),
|
||||
value.end(),
|
||||
value.begin(),
|
||||
[&loc](char c) {
|
||||
return std::tolower(c, loc);
|
||||
});
|
||||
|
||||
if (value == "0" || value == "false") {
|
||||
controls[key] = 0;
|
||||
} else if (value == "1" || value == "true") {
|
||||
controls[key] = 1;
|
||||
} else {
|
||||
std::cerr << "Value at argument "
|
||||
<< i
|
||||
<< " must be a boolean."
|
||||
<< std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ControlTypeMenu: {
|
||||
char *p = nullptr;
|
||||
auto val = strtoul(value.c_str(), &p, 10);
|
||||
|
||||
if (*p) {
|
||||
auto it = std::find(control.menu.begin(),
|
||||
control.menu.end(),
|
||||
value);
|
||||
|
||||
if (it == control.menu.end()) {
|
||||
std::cerr << "Value at argument "
|
||||
<< i
|
||||
<< " is not valid."
|
||||
<< std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
controls[key] = it - control.menu.begin();
|
||||
} else {
|
||||
if (val >= control.menu.size()) {
|
||||
std::cerr << "Value at argument "
|
||||
<< i
|
||||
<< " is out of range."
|
||||
<< std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
controls[key] = val;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
found = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
std::cerr << "No such '"
|
||||
<< key
|
||||
<< "' control in argument "
|
||||
<< i
|
||||
<< "."
|
||||
<< std::endl;
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
this->m_ipcBridge.setControls(deviceId, controls);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1195,7 +1560,6 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
|
|||
{
|
||||
UNUSED(flags);
|
||||
UNUSED(args);
|
||||
|
||||
auto clients = this->m_ipcBridge.clientsPids();
|
||||
|
||||
if (clients.empty())
|
||||
|
@ -1208,38 +1572,217 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
|
|||
<< this->m_ipcBridge.clientExe(pid)
|
||||
<< std::endl;
|
||||
} else {
|
||||
StringVector pidsColumn;
|
||||
StringVector exesColumn;
|
||||
|
||||
pidsColumn.push_back("Pid");
|
||||
exesColumn.push_back("Executable");
|
||||
std::vector<std::string> table {
|
||||
"Pid",
|
||||
"Executable"
|
||||
};
|
||||
auto columns = table.size();
|
||||
|
||||
for (auto &pid: clients) {
|
||||
pidsColumn.push_back(std::to_string(pid));
|
||||
exesColumn.push_back(this->m_ipcBridge.clientExe(pid));
|
||||
table.push_back(std::to_string(pid));
|
||||
table.push_back(this->m_ipcBridge.clientExe(pid));
|
||||
}
|
||||
|
||||
auto pidsColumnSize = this->maxStringLength(pidsColumn);
|
||||
auto exesColumnSize = this->maxStringLength(exesColumn);
|
||||
|
||||
std::cout << fill("Pid", pidsColumnSize)
|
||||
<< " | "
|
||||
<< fill("Executable", exesColumnSize)
|
||||
<< std::endl;
|
||||
std::cout << std::string("-")
|
||||
* (pidsColumnSize + exesColumnSize + 4)
|
||||
<< std::endl;
|
||||
|
||||
for (auto &pid: clients)
|
||||
std::cout << fill(std::to_string(pid), pidsColumnSize)
|
||||
<< " | "
|
||||
<< fill(this->m_ipcBridge.clientExe(pid), exesColumnSize)
|
||||
<< std::endl;
|
||||
this->drawTable(table, columns);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings)
|
||||
{
|
||||
settings.beginGroup("General");
|
||||
|
||||
if (settings.contains("default_frame")) {
|
||||
auto defaultFrame = settings.value("default_frame");
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
|
||||
this->m_ipcBridge.setPicture(cv.from_bytes(defaultFrame));
|
||||
}
|
||||
|
||||
if (settings.contains("loglevel")) {
|
||||
auto logLevel= settings.value("loglevel");
|
||||
char *p = nullptr;
|
||||
auto level = strtol(logLevel.c_str(), &p, 10);
|
||||
|
||||
if (*p)
|
||||
level = AkVCam::Logger::levelFromString(logLevel);
|
||||
|
||||
this->m_ipcBridge.setLogLevel(level);
|
||||
}
|
||||
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
AkVCam::VideoFormatMatrix AkVCam::CmdParserPrivate::readFormats(Settings &settings)
|
||||
{
|
||||
VideoFormatMatrix formatsMatrix;
|
||||
settings.beginGroup("Formats");
|
||||
auto nFormats = settings.beginArray("formats");
|
||||
|
||||
for (size_t i = 0; i < nFormats; i++) {
|
||||
settings.setArrayIndex(i);
|
||||
formatsMatrix.push_back(this->readFormat(settings));
|
||||
}
|
||||
|
||||
settings.endArray();
|
||||
settings.endGroup();
|
||||
|
||||
return formatsMatrix;
|
||||
}
|
||||
|
||||
std::vector<AkVCam::VideoFormat> AkVCam::CmdParserPrivate::readFormat(Settings &settings)
|
||||
{
|
||||
std::vector<AkVCam::VideoFormat> formats;
|
||||
|
||||
auto pixFormats = settings.valueList("format", ",");
|
||||
auto widths = settings.valueList("width", ",");
|
||||
auto heights = settings.valueList("height", ",");
|
||||
auto frameRates = settings.valueList("fps", ",");
|
||||
|
||||
if (pixFormats.empty()
|
||||
|| widths.empty()
|
||||
|| heights.empty()
|
||||
|| frameRates.empty()) {
|
||||
std::cerr << "Error reading formats." << std::endl;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringMatrix formatMatrix;
|
||||
formatMatrix.push_back(pixFormats);
|
||||
formatMatrix.push_back(widths);
|
||||
formatMatrix.push_back(heights);
|
||||
formatMatrix.push_back(frameRates);
|
||||
|
||||
for (auto &format_list: this->matrixCombine(formatMatrix)) {
|
||||
auto pixFormat = VideoFormat::fourccFromString(format_list[0]);
|
||||
char *p = nullptr;
|
||||
auto width = strtol(format_list[1].c_str(), &p, 10);
|
||||
p = nullptr;
|
||||
auto height = strtol(format_list[2].c_str(), &p, 10);
|
||||
Fraction frame_rate(format_list[3]);
|
||||
VideoFormat format(pixFormat,
|
||||
width,
|
||||
height,
|
||||
{frame_rate});
|
||||
|
||||
if (format.isValid())
|
||||
formats.push_back(format);
|
||||
}
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
AkVCam::StringMatrix AkVCam::CmdParserPrivate::matrixCombine(const StringMatrix &matrix)
|
||||
{
|
||||
StringVector combined;
|
||||
StringMatrix combinations;
|
||||
this->matrixCombineP(matrix, 0, combined, combinations);
|
||||
|
||||
return combinations;
|
||||
}
|
||||
|
||||
/* A matrix is a list of lists where each element in the main list is a row,
|
||||
* and each element in a row is a column. We combine each element in a row with
|
||||
* each element in the next rows.
|
||||
*/
|
||||
void AkVCam::CmdParserPrivate::matrixCombineP(const StringMatrix &matrix,
|
||||
size_t index,
|
||||
StringVector combined,
|
||||
StringMatrix &combinations)
|
||||
{
|
||||
if (index >= matrix.size()) {
|
||||
combinations.push_back(combined);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &data: matrix[index]) {
|
||||
auto combinedP1 = combined;
|
||||
combinedP1.push_back(data);
|
||||
this->matrixCombineP(matrix, index + 1, combinedP1, combinations);
|
||||
}
|
||||
}
|
||||
|
||||
void AkVCam::CmdParserPrivate::createDevices(Settings &settings,
|
||||
const VideoFormatMatrix &availableFormats)
|
||||
{
|
||||
auto devices = this->m_ipcBridge.devices();
|
||||
|
||||
for (auto &device: devices)
|
||||
this->m_ipcBridge.removeDevice(device);
|
||||
|
||||
settings.beginGroup("Cameras");
|
||||
size_t nCameras = settings.beginArray("cameras");
|
||||
|
||||
for (size_t i = 0; i < nCameras; i++) {
|
||||
settings.setArrayIndex(i);
|
||||
this->createDevice(settings, availableFormats);
|
||||
}
|
||||
|
||||
settings.endArray();
|
||||
settings.endGroup();
|
||||
this->m_ipcBridge.updateDevices();
|
||||
}
|
||||
|
||||
void AkVCam::CmdParserPrivate::createDevice(Settings &settings,
|
||||
const VideoFormatMatrix &availableFormats)
|
||||
{
|
||||
auto description = settings.value("description");
|
||||
|
||||
if (description.empty()) {
|
||||
std::cerr << "Device description is empty" << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto formats = this->readDeviceFormats(settings, availableFormats);
|
||||
|
||||
if (formats.empty()) {
|
||||
std::cerr << "Can't read device formats" << std::endl;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
|
||||
auto deviceId = this->m_ipcBridge.addDevice(cv.from_bytes(description));
|
||||
auto supportedFormats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
|
||||
|
||||
for (auto &format: formats) {
|
||||
auto it = std::find(supportedFormats.begin(),
|
||||
supportedFormats.end(),
|
||||
format.fourcc());
|
||||
|
||||
if (it != supportedFormats.end())
|
||||
this->m_ipcBridge.addFormat(deviceId, format, -1);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<AkVCam::VideoFormat> AkVCam::CmdParserPrivate::readDeviceFormats(Settings &settings,
|
||||
const VideoFormatMatrix &availableFormats)
|
||||
{
|
||||
std::vector<AkVCam::VideoFormat> formats;
|
||||
auto formatsIndex = settings.valueList("formats", ",");
|
||||
|
||||
for (auto &indexStr: formatsIndex) {
|
||||
char *p = nullptr;
|
||||
auto index = strtoul(indexStr.c_str(), &p, 10);
|
||||
|
||||
if (*p)
|
||||
continue;
|
||||
|
||||
index--;
|
||||
|
||||
if (index >= availableFormats.size())
|
||||
continue;
|
||||
|
||||
for (auto &format: availableFormats[index])
|
||||
formats.push_back(format);
|
||||
}
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
std::string AkVCam::operator *(const std::string &str, size_t n)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
#ifndef CMDPARSER_H
|
||||
#define CMDPARSER_H
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace AkVCam {
|
||||
|
|
|
@ -33,6 +33,26 @@ namespace AkVCam
|
|||
class VideoFormat;
|
||||
class VideoFrame;
|
||||
|
||||
enum ControlType
|
||||
{
|
||||
ControlTypeInteger,
|
||||
ControlTypeBoolean,
|
||||
ControlTypeMenu,
|
||||
};
|
||||
|
||||
struct DeviceControl
|
||||
{
|
||||
std::string id;
|
||||
std::string description;
|
||||
ControlType type;
|
||||
int minimum;
|
||||
int maximum;
|
||||
int step;
|
||||
int defaultValue;
|
||||
int value;
|
||||
std::vector<std::string> menu;
|
||||
};
|
||||
|
||||
class IpcBridge
|
||||
{
|
||||
public:
|
||||
|
@ -53,6 +73,8 @@ namespace AkVCam
|
|||
AKVCAM_SIGNAL(FrameReady,
|
||||
const std::string &deviceId,
|
||||
const VideoFrame &frame)
|
||||
AKVCAM_SIGNAL(PictureChanged,
|
||||
const std::string &picture)
|
||||
AKVCAM_SIGNAL(DeviceAdded,
|
||||
const std::string &deviceId)
|
||||
AKVCAM_SIGNAL(DeviceRemoved,
|
||||
|
@ -67,19 +89,9 @@ namespace AkVCam
|
|||
AKVCAM_SIGNAL(BroadcastingChanged,
|
||||
const std::string &deviceId,
|
||||
const std::string &broadcaster)
|
||||
AKVCAM_SIGNAL(MirrorChanged,
|
||||
AKVCAM_SIGNAL(ControlsChanged,
|
||||
const std::string &deviceId,
|
||||
bool horizontalMirror,
|
||||
bool verticalMirror)
|
||||
AKVCAM_SIGNAL(ScalingChanged,
|
||||
const std::string &deviceId,
|
||||
Scaling scaling)
|
||||
AKVCAM_SIGNAL(AspectRatioChanged,
|
||||
const std::string &deviceId,
|
||||
AspectRatio aspectRatio)
|
||||
AKVCAM_SIGNAL(SwapRgbChanged,
|
||||
const std::string &deviceId,
|
||||
bool swap)
|
||||
const std::map<std::string, int> &controls)
|
||||
|
||||
public:
|
||||
IpcBridge();
|
||||
|
@ -147,20 +159,9 @@ namespace AkVCam
|
|||
// Return return the status of the device.
|
||||
std::string broadcaster(const std::string &deviceId) const;
|
||||
|
||||
// Device is horizontal mirrored,
|
||||
bool isHorizontalMirrored(const std::string &deviceId);
|
||||
|
||||
// Device is vertical mirrored,
|
||||
bool isVerticalMirrored(const std::string &deviceId);
|
||||
|
||||
// Scaling mode for frames shown in clients.
|
||||
Scaling scalingMode(const std::string &deviceId);
|
||||
|
||||
// Aspect ratio mode for frames shown in clients.
|
||||
AspectRatio aspectRatioMode(const std::string &deviceId);
|
||||
|
||||
// Check if red and blue channels are swapped.
|
||||
bool swapRgb(const std::string &deviceId);
|
||||
std::vector<DeviceControl> controls(const std::string &deviceId);
|
||||
void setControls(const std::string &deviceId,
|
||||
const std::map<std::string, int> &controls);
|
||||
|
||||
// Returns the clients that are capturing from a virtual camera.
|
||||
std::vector<std::string> listeners(const std::string &deviceId);
|
||||
|
@ -179,7 +180,6 @@ namespace AkVCam
|
|||
const VideoFormat &format,
|
||||
int index=-1);
|
||||
void removeFormat(const std::string &deviceId, int index);
|
||||
void update();
|
||||
void updateDevices();
|
||||
|
||||
// Start frame transfer to the device.
|
||||
|
@ -193,22 +193,6 @@ namespace AkVCam
|
|||
bool write(const std::string &deviceId,
|
||||
const VideoFrame &frame);
|
||||
|
||||
// Set mirroring options for device,
|
||||
void setMirroring(const std::string &deviceId,
|
||||
bool horizontalMirrored,
|
||||
bool verticalMirrored);
|
||||
|
||||
// Set scaling options for device.
|
||||
void setScaling(const std::string &deviceId,
|
||||
Scaling scaling);
|
||||
|
||||
// Set aspect ratio options for device.
|
||||
void setAspectRatio(const std::string &deviceId,
|
||||
AspectRatio aspectRatio);
|
||||
|
||||
// Swap red and blue channels.
|
||||
void setSwapRgb(const std::string &deviceId, bool swap);
|
||||
|
||||
/* Client */
|
||||
|
||||
// Increment the count of device listeners
|
||||
|
|
|
@ -125,14 +125,14 @@ std::ostream &AkVCam::Logger::log(int logLevel)
|
|||
return dummy;
|
||||
|
||||
if (loggerPrivate()->fileName.empty())
|
||||
return std::cout;
|
||||
return std::cerr;
|
||||
|
||||
if (!loggerPrivate()->stream.is_open())
|
||||
loggerPrivate()->stream.open(loggerPrivate()->fileName,
|
||||
std::ios_base::out | std::ios_base::app);
|
||||
|
||||
if (!loggerPrivate()->stream.is_open())
|
||||
return std::cout;
|
||||
return std::cerr;
|
||||
|
||||
return loggerPrivate()->stream;
|
||||
}
|
||||
|
|
|
@ -270,7 +270,7 @@ int32_t AkVCam::Settings::valueInt32(const std::string &key) const
|
|||
auto value = this->value(key);
|
||||
|
||||
if (value.empty())
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
char *p = nullptr;
|
||||
|
||||
|
@ -282,7 +282,7 @@ uint32_t AkVCam::Settings::valueUInt32(const std::string &key) const
|
|||
auto value = this->value(key);
|
||||
|
||||
if (value.empty())
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
char *p = nullptr;
|
||||
|
||||
|
@ -297,18 +297,23 @@ std::vector<std::string> AkVCam::Settings::valueList(const std::string &key,
|
|||
if (value.empty())
|
||||
return {};
|
||||
|
||||
char *valuePtr = const_cast<char *>(value.c_str());
|
||||
std::vector<std::string> result;
|
||||
size_t pos = 0;
|
||||
|
||||
for (;;) {
|
||||
auto element = strsep(&valuePtr, separators.c_str());
|
||||
do {
|
||||
auto index = value.size();
|
||||
|
||||
if (!element)
|
||||
break;
|
||||
for (auto &separator: separators) {
|
||||
auto it = std::find(value.begin() + pos, value.end(), separator);
|
||||
|
||||
result.push_back(trimmed(element));
|
||||
if (size_t(it - value.begin()) < index)
|
||||
index = it - value.begin();
|
||||
}
|
||||
|
||||
result.push_back(trimmed(value.substr(pos, index - pos)));
|
||||
pos = index + 1;
|
||||
} while (pos < value.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -374,16 +379,15 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line,
|
|||
return element;
|
||||
}
|
||||
|
||||
auto pairSep = strchr(line.c_str(), '=');
|
||||
|
||||
if (!pairSep) {
|
||||
if (line.find('=') == std::string::npos) {
|
||||
if (ok)
|
||||
*ok = false;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
element.key = trimmed(line.substr(0, (size_t) (pairSep - line.c_str())));
|
||||
auto pair = splitOnce(line, "=");
|
||||
element.key = trimmed(pair.first);
|
||||
std::replace(element.key.begin(), element.key.end(), '\\', '/');
|
||||
|
||||
if (element.key.empty()) {
|
||||
|
@ -393,8 +397,7 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line,
|
|||
return {};
|
||||
}
|
||||
|
||||
auto offset = (size_t) (pairSep - line.c_str() + 1);
|
||||
element.value = trimmed(line.substr(offset, line.size() - offset));
|
||||
element.value = trimmed(pair.second);
|
||||
element.value = this->parseString(element.value);
|
||||
|
||||
if (ok)
|
||||
|
|
|
@ -212,3 +212,17 @@ std::vector<std::string> AkVCam::split(const std::string &str, char separator)
|
|||
|
||||
return elements;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> AkVCam::splitOnce(const std::string &str,
|
||||
const std::string &separator)
|
||||
{
|
||||
auto pos = str.find(separator);
|
||||
|
||||
if (pos == std::string::npos)
|
||||
return {str, ""};
|
||||
|
||||
auto first = str.substr(0, pos);
|
||||
auto second = pos + 1 < str.size()? str.substr(pos + 1): "";
|
||||
|
||||
return {first, second};
|
||||
}
|
||||
|
|
|
@ -158,6 +158,8 @@ namespace AkVCam
|
|||
std::string join(const std::vector<std::string> &strs,
|
||||
const std::string &separator);
|
||||
std::vector<std::string> split(const std::string &str, char separator);
|
||||
std::pair<std::string, std::string> splitOnce(const std::string &str,
|
||||
const std::string &separator);
|
||||
}
|
||||
|
||||
#endif // AKVCAMUTILS_UTILS_H
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
@ -45,11 +46,6 @@ namespace AkVCam
|
|||
std::vector<VideoFormat> formats;
|
||||
std::string broadcaster;
|
||||
std::vector<std::string> listeners;
|
||||
bool horizontalMirror {false};
|
||||
bool verticalMirror {false};
|
||||
Scaling scaling {ScalingFast};
|
||||
AspectRatio aspectRatio {AspectRatioIgnore};
|
||||
bool swapRgb {false};
|
||||
};
|
||||
|
||||
using AssistantPeers = std::map<std::string, xpc_connection_t>;
|
||||
|
@ -82,21 +78,15 @@ namespace AkVCam
|
|||
void deviceDestroy(xpc_connection_t client, xpc_object_t event);
|
||||
void deviceUpdate(xpc_connection_t client, xpc_object_t event);
|
||||
void setBroadcasting(xpc_connection_t client, xpc_object_t event);
|
||||
void setMirroring(xpc_connection_t client, xpc_object_t event);
|
||||
void setScaling(xpc_connection_t client, xpc_object_t event);
|
||||
void setAspectRatio(xpc_connection_t client, xpc_object_t event);
|
||||
void setSwapRgb(xpc_connection_t client, xpc_object_t event);
|
||||
void frameReady(xpc_connection_t client, xpc_object_t event);
|
||||
void pictureUpdated(xpc_connection_t client, xpc_object_t event);
|
||||
void listeners(xpc_connection_t client, xpc_object_t event);
|
||||
void listener(xpc_connection_t client, xpc_object_t event);
|
||||
void devices(xpc_connection_t client, xpc_object_t event);
|
||||
void description(xpc_connection_t client, xpc_object_t event);
|
||||
void formats(xpc_connection_t client, xpc_object_t event);
|
||||
void broadcasting(xpc_connection_t client, xpc_object_t event);
|
||||
void mirroring(xpc_connection_t client, xpc_object_t event);
|
||||
void scaling(xpc_connection_t client, xpc_object_t event);
|
||||
void aspectRatio(xpc_connection_t client, xpc_object_t event);
|
||||
void swapRgb(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);
|
||||
};
|
||||
|
@ -143,6 +133,7 @@ AkVCam::AssistantPrivate::AssistantPrivate()
|
|||
{
|
||||
this->m_messageHandlers = {
|
||||
{AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(AssistantPrivate::frameReady) },
|
||||
{AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED , AKVCAM_BIND_FUNC(AssistantPrivate::pictureUpdated) },
|
||||
{AKVCAM_ASSISTANT_MSG_REQUEST_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::requestPort) },
|
||||
{AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::addPort) },
|
||||
{AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::removePort) },
|
||||
|
@ -158,14 +149,7 @@ AkVCam::AssistantPrivate::AssistantPrivate()
|
|||
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER , AKVCAM_BIND_FUNC(AssistantPrivate::listener) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::broadcasting) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::setBroadcasting)},
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING , AKVCAM_BIND_FUNC(AssistantPrivate::mirroring) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(AssistantPrivate::setMirroring) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SCALING , AKVCAM_BIND_FUNC(AssistantPrivate::scaling) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING , AKVCAM_BIND_FUNC(AssistantPrivate::setScaling) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO , AKVCAM_BIND_FUNC(AssistantPrivate::aspectRatio) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO , AKVCAM_BIND_FUNC(AssistantPrivate::setAspectRatio) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB , AKVCAM_BIND_FUNC(AssistantPrivate::swapRgb) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB , AKVCAM_BIND_FUNC(AssistantPrivate::setSwapRgb) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(AssistantPrivate::controlsUpdated)},
|
||||
};
|
||||
|
||||
this->loadCameras();
|
||||
|
@ -500,113 +484,6 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
|
|||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::setMirroring(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
bool horizontalMirror = xpc_dictionary_get_bool(event, "hmirror");
|
||||
bool verticalMirror = xpc_dictionary_get_bool(event, "vmirror");
|
||||
bool ok = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
if (this->m_deviceConfigs[deviceId].horizontalMirror != horizontalMirror
|
||||
|| this->m_deviceConfigs[deviceId].verticalMirror != verticalMirror) {
|
||||
this->m_deviceConfigs[deviceId].horizontalMirror = horizontalMirror;
|
||||
this->m_deviceConfigs[deviceId].verticalMirror = verticalMirror;
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
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::setScaling(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
auto scaling = Scaling(xpc_dictionary_get_int64(event, "scaling"));
|
||||
bool ok = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
if (this->m_deviceConfigs[deviceId].scaling != scaling) {
|
||||
this->m_deviceConfigs[deviceId].scaling = scaling;
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
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::setAspectRatio(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
auto aspectRatio = AspectRatio(xpc_dictionary_get_int64(event, "aspect"));
|
||||
bool ok = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
if (this->m_deviceConfigs[deviceId].aspectRatio != aspectRatio) {
|
||||
this->m_deviceConfigs[deviceId].aspectRatio = aspectRatio;
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
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::setSwapRgb(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
auto swapRgb = xpc_dictionary_get_bool(event, "swap");
|
||||
bool ok = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
if (this->m_deviceConfigs[deviceId].swapRgb != swapRgb) {
|
||||
this->m_deviceConfigs[deviceId].swapRgb = swapRgb;
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
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,
|
||||
xpc_object_t event)
|
||||
{
|
||||
|
@ -634,6 +511,32 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
|
|||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
bool ok = true;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::listeners(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
|
@ -761,78 +664,24 @@ void AkVCam::AssistantPrivate::broadcasting(xpc_connection_t client,
|
|||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::mirroring(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client, xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
bool horizontalMirror = false;
|
||||
bool verticalMirror = false;
|
||||
bool ok = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0) {
|
||||
horizontalMirror = this->m_deviceConfigs[deviceId].horizontalMirror;
|
||||
verticalMirror = this->m_deviceConfigs[deviceId].verticalMirror;
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
ok = true;
|
||||
}
|
||||
|
||||
AkLogInfo() << "Device: " << deviceId << std::endl;
|
||||
AkLogInfo() << "Horizontal mirror: " << horizontalMirror << std::endl;
|
||||
AkLogInfo() << "Vertical mirror: " << verticalMirror << std::endl;
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
xpc_dictionary_set_bool(reply, "hmirror", horizontalMirror);
|
||||
xpc_dictionary_set_bool(reply, "vmirror", verticalMirror);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::scaling(xpc_connection_t client, xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
Scaling scaling = ScalingFast;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
scaling = this->m_deviceConfigs[deviceId].scaling;
|
||||
|
||||
AkLogInfo() << "Device: " << deviceId << std::endl;
|
||||
AkLogInfo() << "Scaling: " << scaling << std::endl;
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
xpc_dictionary_set_int64(reply, "scaling", scaling);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::aspectRatio(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
AspectRatio aspectRatio = AspectRatioIgnore;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
aspectRatio = this->m_deviceConfigs[deviceId].aspectRatio;
|
||||
|
||||
AkLogInfo() << "Device: " << deviceId << std::endl;
|
||||
AkLogInfo() << "Aspect ratio: " << aspectRatio << std::endl;
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
xpc_dictionary_set_int64(reply, "aspect", aspectRatio);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::AssistantPrivate::swapRgb(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
AkLogFunction();
|
||||
std::string deviceId = xpc_dictionary_get_string(event, "device");
|
||||
bool swapRgb = false;
|
||||
|
||||
if (this->m_deviceConfigs.count(deviceId) > 0)
|
||||
swapRgb = this->m_deviceConfigs[deviceId].swapRgb;
|
||||
|
||||
AkLogInfo() << "Device: " << deviceId << std::endl;
|
||||
AkLogInfo() << "Swap RGB: " << swapRgb << std::endl;
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
xpc_dictionary_set_bool(reply, "swap", swapRgb);
|
||||
xpc_dictionary_set_bool(reply, "status", ok);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
// General messages
|
||||
#define AKVCAM_ASSISTANT_MSG_ISALIVE 0x000
|
||||
#define AKVCAM_ASSISTANT_MSG_FRAME_READY 0x001
|
||||
#define AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED 0x002
|
||||
|
||||
// Assistant messages
|
||||
#define AKVCAM_ASSISTANT_MSG_REQUEST_PORT 0x100
|
||||
|
@ -51,20 +52,12 @@
|
|||
// Device dynamic properties
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING 0x400
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING 0x401
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING 0x402
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING 0x403
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SCALING 0x404
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING 0x405
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO 0x406
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO 0x407
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB 0x408
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB 0x409
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED 0x402
|
||||
|
||||
// Connections
|
||||
#define AKVCAM_ASSISTANT_MSG_CONNECTIONS 0x500
|
||||
#define AKVCAM_ASSISTANT_MSG_SETCONNECTIONS 0x501
|
||||
|
||||
|
||||
namespace AkVCam
|
||||
{
|
||||
using XpcMessage = std::function<void (xpc_connection_t, xpc_object_t)>;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <CoreFoundation/CFRunLoop.h>
|
||||
#include <xpc/xpc.h>
|
||||
#include <xpc/connection.h>
|
||||
|
|
|
@ -42,7 +42,8 @@ TEMPLATE = lib
|
|||
|
||||
LIBS = \
|
||||
-L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \
|
||||
-framework CoreFoundation
|
||||
-framework CoreFoundation \
|
||||
-framework CoreGraphics
|
||||
|
||||
SOURCES = \
|
||||
src/preferences.cpp \
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "preferences.h"
|
||||
#include "utils.h"
|
||||
#include "VCamUtils/src/image/videoformat.h"
|
||||
|
@ -609,3 +611,16 @@ void AkVCam::Preferences::setLogLevel(int logLevel)
|
|||
{
|
||||
write("loglevel", logLevel);
|
||||
}
|
||||
|
||||
int AkVCam::Preferences::cameraControlValue(size_t cameraIndex,
|
||||
const std::string &key)
|
||||
{
|
||||
return readInt("cameras." + std::to_string(cameraIndex) + ".controls." + key);
|
||||
}
|
||||
|
||||
void AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex,
|
||||
const std::string &key,
|
||||
int value)
|
||||
{
|
||||
write("cameras." + std::to_string(cameraIndex) + ".controls." + key, value);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#ifndef PREFERENCES_H
|
||||
#define PREFERENCES_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
@ -79,6 +80,11 @@ namespace AkVCam
|
|||
const VideoFormat &format,
|
||||
int index);
|
||||
void cameraRemoveFormat(size_t cameraIndex, int index);
|
||||
int cameraControlValue(size_t cameraIndex,
|
||||
const std::string &key);
|
||||
void cameraSetControlValue(size_t cameraIndex,
|
||||
const std::string &key,
|
||||
int value);
|
||||
std::wstring picture();
|
||||
void setPicture(const std::wstring &picture);
|
||||
int logLevel();
|
||||
|
|
|
@ -17,7 +17,97 @@
|
|||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#import <CoreGraphics/CGImage.h>
|
||||
#import <CoreGraphics/CGDataProvider.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "VCamUtils/src/logger/logger.h"
|
||||
#include "VCamUtils/src/image/videoformat.h"
|
||||
#include "VCamUtils/src/image/videoframe.h"
|
||||
|
||||
namespace AkVCam {
|
||||
namespace Utils {
|
||||
struct RGB24
|
||||
{
|
||||
uint8_t b;
|
||||
uint8_t g;
|
||||
uint8_t r;
|
||||
};
|
||||
|
||||
struct RGB32
|
||||
{
|
||||
uint8_t b;
|
||||
uint8_t g;
|
||||
uint8_t r;
|
||||
uint8_t x;
|
||||
};
|
||||
|
||||
inline const std::map<AkVCam::PixelFormat, FourCharCode> *formatsTable()
|
||||
{
|
||||
static const std::map<AkVCam::PixelFormat, FourCharCode> formatsTable {
|
||||
{AkVCam::PixelFormatRGB32, kCMPixelFormat_32ARGB },
|
||||
{AkVCam::PixelFormatRGB24, kCMPixelFormat_24RGB },
|
||||
{AkVCam::PixelFormatRGB16, kCMPixelFormat_16LE565 },
|
||||
{AkVCam::PixelFormatRGB15, kCMPixelFormat_16LE555 },
|
||||
{AkVCam::PixelFormatUYVY , kCMPixelFormat_422YpCbCr8 },
|
||||
{AkVCam::PixelFormatYUY2 , kCMPixelFormat_422YpCbCr8_yuvs}
|
||||
};
|
||||
|
||||
return &formatsTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AkVCam::uuidEqual(const REFIID &uuid1, const CFUUIDRef uuid2)
|
||||
{
|
||||
auto iid2 = CFUUIDGetUUIDBytes(uuid2);
|
||||
auto puuid1 = reinterpret_cast<const UInt8 *>(&uuid1);
|
||||
auto puuid2 = reinterpret_cast<const UInt8 *>(&iid2);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
if (puuid1[i] != puuid2[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string AkVCam::enumToString(UInt32 value)
|
||||
{
|
||||
auto valueChr = reinterpret_cast<char *>(&value);
|
||||
std::stringstream ss;
|
||||
|
||||
for (int i = 3; i >= 0; i--)
|
||||
if (valueChr[i] < 0)
|
||||
ss << std::hex << valueChr[i];
|
||||
else if (valueChr[i] < 32)
|
||||
ss << int(valueChr[i]);
|
||||
else
|
||||
ss << valueChr[i];
|
||||
|
||||
return "'" + ss.str() + "'";
|
||||
}
|
||||
|
||||
FourCharCode AkVCam::formatToCM(PixelFormat format)
|
||||
{
|
||||
for (auto &fmt: *Utils::formatsTable())
|
||||
if (fmt.first == format)
|
||||
return fmt.second;
|
||||
|
||||
return FourCharCode(0);
|
||||
}
|
||||
|
||||
AkVCam::PixelFormat AkVCam::formatFromCM(FourCharCode format)
|
||||
{
|
||||
for (auto &fmt: *Utils::formatsTable())
|
||||
if (fmt.second == format)
|
||||
return fmt.first;
|
||||
|
||||
return PixelFormat(0);
|
||||
}
|
||||
|
||||
std::shared_ptr<CFTypeRef> AkVCam::cfTypeFromStd(const std::string &str)
|
||||
{
|
||||
|
@ -135,3 +225,174 @@ std::wstring AkVCam::wstringFromCFType(CFTypeRef cfType)
|
|||
|
||||
return std::wstring(cstr, size_t(len));
|
||||
}
|
||||
|
||||
std::string AkVCam::realPath(const std::string &path)
|
||||
{
|
||||
char resolvedPath[PATH_MAX];
|
||||
memset(resolvedPath, 0, PATH_MAX);
|
||||
::realpath(path.c_str(), resolvedPath);
|
||||
|
||||
char realPath[PATH_MAX];
|
||||
memset(realPath, 0, PATH_MAX);
|
||||
readlink(resolvedPath, realPath, PATH_MAX);
|
||||
|
||||
if (strlen(realPath) < 1)
|
||||
return {resolvedPath};
|
||||
|
||||
return {realPath};
|
||||
}
|
||||
|
||||
AkVCam::VideoFrame AkVCam::loadPicture(const std::string &fileName)
|
||||
{
|
||||
AkLogFunction();
|
||||
VideoFrame frame;
|
||||
|
||||
if (frame.load(fileName)) {
|
||||
AkLogInfo() << "Picture loaded as BMP" << std::endl;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
auto fileDataProvider = CGDataProviderCreateWithFilename(fileName.c_str());
|
||||
|
||||
if (!fileDataProvider) {
|
||||
AkLogError() << "Can't create a data provider for '"
|
||||
<< fileName
|
||||
<< "'"
|
||||
<< std::endl;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Check if the file is a PNG and open it.
|
||||
auto cgImage = CGImageCreateWithPNGDataProvider(fileDataProvider,
|
||||
nullptr,
|
||||
true,
|
||||
kCGRenderingIntentDefault);
|
||||
|
||||
// If the file is not a PNG, try opening as JPEG.
|
||||
if (!cgImage) {
|
||||
AkLogWarning() << "Can't read '"
|
||||
<< fileName
|
||||
<< "' as a PNG image."
|
||||
<< std::endl;
|
||||
cgImage = CGImageCreateWithJPEGDataProvider(fileDataProvider,
|
||||
nullptr,
|
||||
true,
|
||||
kCGRenderingIntentDefault);
|
||||
}
|
||||
|
||||
CGDataProviderRelease(fileDataProvider);
|
||||
|
||||
// The file format is not supported, fail.
|
||||
if (!cgImage) {
|
||||
AkLogError() << "Can't read '"
|
||||
<< fileName
|
||||
<< "' as a JPEG image."
|
||||
<< std::endl;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FourCC format = 0;
|
||||
|
||||
if (CGImageGetBitsPerComponent(cgImage) == 8) {
|
||||
if (CGImageGetBitsPerPixel(cgImage) == 24)
|
||||
format = PixelFormatRGB24;
|
||||
else if (CGImageGetBitsPerPixel(cgImage) == 32) {
|
||||
format = PixelFormatRGB32;
|
||||
}
|
||||
}
|
||||
|
||||
auto width = CGImageGetWidth(cgImage);
|
||||
auto height = CGImageGetHeight(cgImage);
|
||||
|
||||
if (format == 0 || width < 1 || height < 1) {
|
||||
AkLogError() << "Invalid picture format: "
|
||||
<< "BPC="
|
||||
<< CGImageGetBitsPerComponent(cgImage)
|
||||
<< "BPP="
|
||||
<< CGImageGetBitsPerPixel(cgImage)
|
||||
<< " "
|
||||
<< width
|
||||
<< "x"
|
||||
<< height
|
||||
<< std::endl;
|
||||
CGImageRelease(cgImage);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
VideoFormat videoFormat(PixelFormatRGB24, width, height);
|
||||
frame = VideoFrame(videoFormat);
|
||||
|
||||
auto imageDataProvider = CGImageGetDataProvider(cgImage);
|
||||
|
||||
if (!imageDataProvider) {
|
||||
AkLogError() << "Can't get data provider for picture." << std::endl;
|
||||
CGImageRelease(cgImage);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto data = CGDataProviderCopyData(imageDataProvider);
|
||||
|
||||
if (!data) {
|
||||
AkLogError() << "Can't copy data from image provider." << std::endl;
|
||||
CGImageRelease(cgImage);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
auto lineSize = CGImageGetBytesPerRow(cgImage);
|
||||
|
||||
if (CGImageGetBitsPerPixel(cgImage) == 24) {
|
||||
for (int y = 0; y < videoFormat.height(); y++) {
|
||||
auto srcLine = reinterpret_cast<const Utils::RGB24 *>(CFDataGetBytePtr(data) + y * lineSize);
|
||||
auto dstLine = reinterpret_cast<Utils::RGB24 *>(frame.line(0, y));
|
||||
|
||||
for (int x = 0; x < videoFormat.height(); x++) {
|
||||
dstLine[x].r = srcLine[x].r;
|
||||
dstLine[x].g = srcLine[x].g;
|
||||
dstLine[x].b = srcLine[x].b;
|
||||
}
|
||||
}
|
||||
} else if (CGImageGetBitsPerPixel(cgImage) == 32) {
|
||||
if (CGImageGetAlphaInfo(cgImage) == kCGImageAlphaNone) {
|
||||
for (int y = 0; y < videoFormat.height(); y++) {
|
||||
auto srcLine = reinterpret_cast<const Utils::RGB32 *>(CFDataGetBytePtr(data) + y * lineSize);
|
||||
auto dstLine = reinterpret_cast<Utils::RGB24 *>(frame.line(0, y));
|
||||
|
||||
for (int x = 0; x < videoFormat.height(); x++) {
|
||||
dstLine[x].r = srcLine[x].r;
|
||||
dstLine[x].g = srcLine[x].g;
|
||||
dstLine[x].b = srcLine[x].b;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int y = 0; y < videoFormat.height(); y++) {
|
||||
auto srcLine = reinterpret_cast<const Utils::RGB32 *>(CFDataGetBytePtr(data) + y * lineSize);
|
||||
auto dstLine = reinterpret_cast<Utils::RGB24 *>(frame.line(0, y));
|
||||
|
||||
for (int x = 0; x < videoFormat.width(); x++) {
|
||||
dstLine[x].r = srcLine[x].r * srcLine[x].x / 255;
|
||||
dstLine[x].g = srcLine[x].g * srcLine[x].x / 255;
|
||||
dstLine[x].b = srcLine[x].b * srcLine[x].x / 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(data);
|
||||
CGImageRelease(cgImage);
|
||||
|
||||
AkLogDebug() << "Picture loaded as: "
|
||||
<< VideoFormat::stringFromFourcc(videoFormat.fourcc())
|
||||
<< " "
|
||||
<< videoFormat.width()
|
||||
<< "x"
|
||||
<< videoFormat.height()
|
||||
<< std::endl;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
|
|
@ -20,19 +20,29 @@
|
|||
#ifndef PLATFORM_UTILS_H
|
||||
#define PLATFORM_UTILS_H
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreMediaIO/CMIOHardwarePlugIn.h>
|
||||
#include <CoreMedia/CMFormatDescription.h>
|
||||
|
||||
#include "VCamUtils/src/image/videoformattypes.h"
|
||||
|
||||
namespace AkVCam
|
||||
{
|
||||
class VideoFormat;
|
||||
class VideoFrame;
|
||||
|
||||
bool uuidEqual(const REFIID &uuid1, const CFUUIDRef uuid2);
|
||||
std::string enumToString(UInt32 value);
|
||||
FourCharCode formatToCM(PixelFormat format);
|
||||
PixelFormat formatFromCM(FourCharCode format);
|
||||
std::shared_ptr<CFTypeRef> cfTypeFromStd(const std::string &str);
|
||||
std::shared_ptr<CFTypeRef> cfTypeFromStd(const std::wstring &str);
|
||||
std::shared_ptr<CFTypeRef> cfTypeFromStd(int num);
|
||||
std::shared_ptr<CFTypeRef> cfTypeFromStd(double num);
|
||||
std::string stringFromCFType(CFTypeRef cfType);
|
||||
std::wstring wstringFromCFType(CFTypeRef cfType);
|
||||
std::string realPath(const std::string &path);
|
||||
VideoFrame loadPicture(const std::string &fileName);
|
||||
}
|
||||
|
||||
#endif // PLATFORM_UTILS_H
|
||||
|
|
|
@ -17,12 +17,16 @@
|
|||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <fstream>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <libgen.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <IOSurface/IOSurface.h>
|
||||
#include <CoreMedia/CMFormatDescription.h>
|
||||
|
@ -31,6 +35,7 @@
|
|||
|
||||
#include "Assistant/src/assistantglobals.h"
|
||||
#include "PlatformUtils/src/preferences.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
#include "VCamUtils/src/image/videoformat.h"
|
||||
#include "VCamUtils/src/image/videoframe.h"
|
||||
#include "VCamUtils/src/ipcbridge.h"
|
||||
|
@ -61,6 +66,7 @@ namespace AkVCam
|
|||
inline void add(IpcBridge *bridge);
|
||||
void remove(IpcBridge *bridge);
|
||||
inline std::vector<IpcBridge *> &bridges();
|
||||
inline const std::vector<DeviceControl> &controls() const;
|
||||
|
||||
// Message handling methods
|
||||
void isAlive(xpc_connection_t client, xpc_object_t event);
|
||||
|
@ -68,12 +74,9 @@ namespace AkVCam
|
|||
void deviceDestroy(xpc_connection_t client, xpc_object_t event);
|
||||
void deviceUpdate(xpc_connection_t client, xpc_object_t event);
|
||||
void frameReady(xpc_connection_t client, xpc_object_t event);
|
||||
void setBroadcasting(xpc_connection_t client,
|
||||
xpc_object_t event);
|
||||
void setMirror(xpc_connection_t client, xpc_object_t event);
|
||||
void setScaling(xpc_connection_t client, xpc_object_t event);
|
||||
void setAspectRatio(xpc_connection_t client, xpc_object_t event);
|
||||
void setSwapRgb(xpc_connection_t client, xpc_object_t event);
|
||||
void pictureUpdated(xpc_connection_t client, xpc_object_t event);
|
||||
void setBroadcasting(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 messageReceived(xpc_connection_t client, xpc_object_t event);
|
||||
|
@ -83,10 +86,9 @@ namespace AkVCam
|
|||
std::string homePath() const;
|
||||
bool fileExists(const std::wstring &path) const;
|
||||
bool fileExists(const std::string &path) const;
|
||||
std::wstring fileName(const std::wstring &path) const;
|
||||
bool mkpath(const std::string &path) const;
|
||||
bool rm(const std::string &path) const;
|
||||
std::wstring locateDriverPath() const;
|
||||
static std::string locatePluginPath();
|
||||
|
||||
private:
|
||||
std::vector<IpcBridge *> m_bridges;
|
||||
|
@ -169,7 +171,18 @@ std::wstring AkVCam::IpcBridge::picture() const
|
|||
|
||||
void AkVCam::IpcBridge::setPicture(const std::wstring &picture)
|
||||
{
|
||||
AkLogFunction();
|
||||
Preferences::setPicture(picture);
|
||||
|
||||
if (!this->d->m_serverMessagePort)
|
||||
return;
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED);
|
||||
xpc_dictionary_set_string(dictionary, "picture", cv.to_bytes(picture).c_str());
|
||||
xpc_connection_send_message(this->d->m_serverMessagePort, dictionary);
|
||||
xpc_release(dictionary);
|
||||
}
|
||||
|
||||
int AkVCam::IpcBridge::logLevel() const
|
||||
|
@ -446,139 +459,62 @@ std::string AkVCam::IpcBridge::broadcaster(const std::string &deviceId) const
|
|||
return broadcaster;
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::isHorizontalMirrored(const std::string &deviceId)
|
||||
std::vector<AkVCam::DeviceControl> AkVCam::IpcBridge::controls(const std::string &deviceId)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto cameraIndex = Preferences::cameraFromPath(deviceId);
|
||||
|
||||
if (!this->d->m_serverMessagePort)
|
||||
return false;
|
||||
if (cameraIndex < 0)
|
||||
return {};
|
||||
|
||||
std::vector<DeviceControl> controls;
|
||||
|
||||
for (auto &control: this->d->controls()) {
|
||||
controls.push_back(control);
|
||||
controls.back().value =
|
||||
Preferences::cameraControlValue(cameraIndex, control.id);
|
||||
}
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridge::setControls(const std::string &deviceId,
|
||||
const std::map<std::string, int> &controls)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto cameraIndex = Preferences::cameraFromPath(deviceId);
|
||||
|
||||
if (cameraIndex < 0)
|
||||
return;
|
||||
|
||||
bool updated = false;
|
||||
|
||||
for (auto &control: this->d->controls()) {
|
||||
auto oldValue =
|
||||
Preferences::cameraControlValue(cameraIndex, control.id);
|
||||
|
||||
if (controls.count(control.id)) {
|
||||
auto newValue = controls.at(control.id);
|
||||
|
||||
if (newValue != oldValue) {
|
||||
Preferences::cameraSetControlValue(cameraIndex,
|
||||
control.id,
|
||||
newValue);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->d->m_serverMessagePort || !updated)
|
||||
return;
|
||||
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
|
||||
if (replyType != XPC_TYPE_DICTIONARY) {
|
||||
xpc_release(reply);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool horizontalMirror = xpc_dictionary_get_bool(reply, "hmirror");
|
||||
xpc_release(reply);
|
||||
|
||||
return horizontalMirror;
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::isVerticalMirrored(const std::string &deviceId)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (!this->d->m_serverMessagePort)
|
||||
return false;
|
||||
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
|
||||
if (replyType != XPC_TYPE_DICTIONARY) {
|
||||
xpc_release(reply);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool verticalMirror = xpc_dictionary_get_bool(reply, "vmirror");
|
||||
xpc_release(reply);
|
||||
|
||||
return verticalMirror;
|
||||
}
|
||||
|
||||
AkVCam::Scaling AkVCam::IpcBridge::scalingMode(const std::string &deviceId)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (!this->d->m_serverMessagePort)
|
||||
return ScalingFast;
|
||||
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SCALING);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
|
||||
if (replyType != XPC_TYPE_DICTIONARY) {
|
||||
xpc_release(reply);
|
||||
|
||||
return ScalingFast;
|
||||
}
|
||||
|
||||
auto scaling = Scaling(xpc_dictionary_get_int64(reply, "scaling"));
|
||||
xpc_release(reply);
|
||||
|
||||
return scaling;
|
||||
}
|
||||
|
||||
AkVCam::AspectRatio AkVCam::IpcBridge::aspectRatioMode(const std::string &deviceId)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (!this->d->m_serverMessagePort)
|
||||
return AspectRatioIgnore;
|
||||
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
|
||||
if (replyType != XPC_TYPE_DICTIONARY) {
|
||||
xpc_release(reply);
|
||||
|
||||
return AspectRatioIgnore;
|
||||
}
|
||||
|
||||
auto aspectRatio = AspectRatio(xpc_dictionary_get_int64(reply, "aspect"));
|
||||
xpc_release(reply);
|
||||
|
||||
return aspectRatio;
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::swapRgb(const std::string &deviceId)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (!this->d->m_serverMessagePort)
|
||||
return false;
|
||||
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
|
||||
if (replyType != XPC_TYPE_DICTIONARY) {
|
||||
xpc_release(reply);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto swap = xpc_dictionary_get_bool(reply, "swap");
|
||||
xpc_release(reply);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
std::vector<std::string> AkVCam::IpcBridge::listeners(const std::string &deviceId)
|
||||
|
@ -618,17 +554,19 @@ std::vector<std::string> AkVCam::IpcBridge::listeners(const std::string &deviceI
|
|||
|
||||
std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
|
||||
{
|
||||
auto driverPath = this->d->locateDriverPath();
|
||||
AkLogFunction();
|
||||
auto driverPath = this->d->locatePluginPath();
|
||||
AkLogDebug() << "Plugin path: " << driverPath << std::endl;
|
||||
|
||||
if (driverPath.empty())
|
||||
return {};
|
||||
|
||||
auto plugin = this->d->fileName(driverPath);
|
||||
std::wstring pluginPath =
|
||||
CMIO_PLUGINS_DAL_PATH_L L"/"
|
||||
+ plugin
|
||||
+ L"/Contents/MacOS/" CMIO_PLUGIN_NAME_L;
|
||||
std::string path(pluginPath.begin(), pluginPath.end());
|
||||
auto path = driverPath + "/Contents/MacOS/" CMIO_PLUGIN_NAME;
|
||||
AkLogDebug() << "Plugin binary: " << path << std::endl;
|
||||
|
||||
if (!this->d->fileExists(path))
|
||||
return {};
|
||||
|
||||
auto npids = proc_listpidspath(PROC_ALL_PIDS,
|
||||
0,
|
||||
path.c_str(),
|
||||
|
@ -692,7 +630,7 @@ void AkVCam::IpcBridge::removeFormat(const std::string &deviceId, int index)
|
|||
index);
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridge::update()
|
||||
void AkVCam::IpcBridge::updateDevices()
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
|
@ -705,19 +643,6 @@ void AkVCam::IpcBridge::update()
|
|||
AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE);
|
||||
xpc_connection_send_message(this->d->m_serverMessagePort, dictionary);
|
||||
xpc_release(dictionary);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -856,79 +781,6 @@ bool AkVCam::IpcBridge::write(const std::string &deviceId,
|
|||
return true;
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridge::setMirroring(const std::string &deviceId,
|
||||
bool horizontalMirrored,
|
||||
bool verticalMirrored)
|
||||
{
|
||||
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_SETMIRRORING);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
xpc_dictionary_set_bool(dictionary, "hmirror", horizontalMirrored);
|
||||
xpc_dictionary_set_bool(dictionary, "vmirror", verticalMirrored);
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridge::setScaling(const std::string &deviceId,
|
||||
Scaling scaling)
|
||||
{
|
||||
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_SETSCALING);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
xpc_dictionary_set_int64(dictionary, "scaling", scaling);
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridge::setAspectRatio(const std::string &deviceId,
|
||||
AspectRatio aspectRatio)
|
||||
{
|
||||
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_SETASPECTRATIO);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
xpc_dictionary_set_int64(dictionary, "aspect", aspectRatio);
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridge::setSwapRgb(const std::string &deviceId, bool swap)
|
||||
{
|
||||
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_SETSWAPRGB);
|
||||
xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
|
||||
xpc_dictionary_set_bool(dictionary, "swap", swap);
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::addListener(const std::string &deviceId)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
@ -993,16 +845,14 @@ AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
|
|||
this->m_messageHandlers = {
|
||||
{AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) },
|
||||
{AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) },
|
||||
{AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED , AKVCAM_BIND_FUNC(IpcBridgePrivate::pictureUpdated) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_CREATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceCreate) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceDestroy) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceUpdate) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerAdd) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerRemove) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setBroadcasting)},
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setMirror) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setScaling) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO , AKVCAM_BIND_FUNC(IpcBridgePrivate::setAspectRatio) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB , AKVCAM_BIND_FUNC(IpcBridgePrivate::setSwapRgb) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(IpcBridgePrivate::controlsUpdated)},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1038,6 +888,31 @@ std::vector<AkVCam::IpcBridge *> &AkVCam::IpcBridgePrivate::bridges()
|
|||
return this->m_bridges;
|
||||
}
|
||||
|
||||
const std::vector<AkVCam::DeviceControl> &AkVCam::IpcBridgePrivate::controls() const
|
||||
{
|
||||
static const std::vector<std::string> scalingMenu {
|
||||
"Fast",
|
||||
"Linear"
|
||||
};
|
||||
static const std::vector<std::string> aspectRatioMenu {
|
||||
"Ignore",
|
||||
"Keep",
|
||||
"Expanding"
|
||||
};
|
||||
static const auto scalingMax = int(scalingMenu.size()) - 1;
|
||||
static const auto aspectRatioMax = int(aspectRatioMenu.size()) - 1;
|
||||
|
||||
static const std::vector<DeviceControl> controls {
|
||||
{"hflip" , "Horizontal Mirror", ControlTypeBoolean, 0, 1 , 1, 0, 0, {} },
|
||||
{"vflip" , "Vertical Mirror" , ControlTypeBoolean, 0, 1 , 1, 0, 0, {} },
|
||||
{"scaling" , "Scaling" , ControlTypeMenu , 0, scalingMax , 1, 0, 0, scalingMenu },
|
||||
{"aspect_ratio", "Aspect Ratio" , ControlTypeMenu , 0, aspectRatioMax, 1, 0, 0, aspectRatioMenu},
|
||||
{"swap_rgb" , "Swap RGB" , ControlTypeBoolean, 0, 1 , 1, 0, 0, {} },
|
||||
};
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridgePrivate::isAlive(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
|
@ -1118,6 +993,18 @@ void AkVCam::IpcBridgePrivate::frameReady(xpc_connection_t client,
|
|||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridgePrivate::pictureUpdated(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
|
||||
std::string picture = xpc_dictionary_get_string(event, "picture");
|
||||
|
||||
for (auto bridge: this->m_bridges)
|
||||
AKVCAM_EMIT(bridge, PictureChanged, picture)
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
|
@ -1133,7 +1020,7 @@ void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client,
|
|||
AKVCAM_EMIT(bridge, BroadcastingChanged, deviceId, broadcaster)
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridgePrivate::setMirror(xpc_connection_t client,
|
||||
void AkVCam::IpcBridgePrivate::controlsUpdated(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
|
@ -1141,61 +1028,23 @@ void AkVCam::IpcBridgePrivate::setMirror(xpc_connection_t client,
|
|||
|
||||
std::string deviceId =
|
||||
xpc_dictionary_get_string(event, "device");
|
||||
bool horizontalMirror =
|
||||
xpc_dictionary_get_bool(event, "hmirror");
|
||||
bool verticalMirror =
|
||||
xpc_dictionary_get_bool(event, "vmirror");
|
||||
auto cameraIndex = Preferences::cameraFromPath(deviceId);
|
||||
std::map<std::string, int> controls;
|
||||
|
||||
for (auto &control: this->controls())
|
||||
controls[control.id] =
|
||||
Preferences::cameraControlValue(cameraIndex, control.id);
|
||||
|
||||
for (auto bridge: this->m_bridges)
|
||||
AKVCAM_EMIT(bridge,
|
||||
MirrorChanged,
|
||||
ControlsChanged,
|
||||
deviceId,
|
||||
horizontalMirror,
|
||||
verticalMirror)
|
||||
}
|
||||
controls)
|
||||
|
||||
void AkVCam::IpcBridgePrivate::setScaling(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
|
||||
std::string deviceId =
|
||||
xpc_dictionary_get_string(event, "device");
|
||||
auto scaling =
|
||||
Scaling(xpc_dictionary_get_int64(event, "scaling"));
|
||||
|
||||
for (auto bridge: this->m_bridges)
|
||||
AKVCAM_EMIT(bridge, ScalingChanged, deviceId, scaling)
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridgePrivate::setAspectRatio(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
|
||||
std::string deviceId =
|
||||
xpc_dictionary_get_string(event, "device");
|
||||
auto aspectRatio =
|
||||
AspectRatio(xpc_dictionary_get_int64(event, "aspect"));
|
||||
|
||||
for (auto bridge: this->m_bridges)
|
||||
AKVCAM_EMIT(bridge, AspectRatioChanged, deviceId, aspectRatio)
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridgePrivate::setSwapRgb(xpc_connection_t client,
|
||||
xpc_object_t event)
|
||||
{
|
||||
UNUSED(client);
|
||||
AkLogFunction();
|
||||
|
||||
std::string deviceId =
|
||||
xpc_dictionary_get_string(event, "device");
|
||||
auto swap = xpc_dictionary_get_bool(event, "swap");
|
||||
|
||||
for (auto bridge: this->m_bridges)
|
||||
AKVCAM_EMIT(bridge, SwapRgbChanged, deviceId, swap)
|
||||
auto reply = xpc_dictionary_create_reply(event);
|
||||
xpc_dictionary_set_bool(reply, "status", cameraIndex >= 0);
|
||||
xpc_connection_send_message(client, reply);
|
||||
xpc_release(reply);
|
||||
}
|
||||
|
||||
void AkVCam::IpcBridgePrivate::listenerAdd(xpc_connection_t client,
|
||||
|
@ -1280,11 +1129,6 @@ bool AkVCam::IpcBridgePrivate::fileExists(const std::string &path) const
|
|||
return stat(path.c_str(), &stats) == 0;
|
||||
}
|
||||
|
||||
std::wstring AkVCam::IpcBridgePrivate::fileName(const std::wstring &path) const
|
||||
{
|
||||
return path.substr(path.rfind(L'/') + 1);
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridgePrivate::mkpath(const std::string &path) const
|
||||
{
|
||||
if (path.empty())
|
||||
|
@ -1341,32 +1185,14 @@ bool AkVCam::IpcBridgePrivate::rm(const std::string &path) const
|
|||
return ok;
|
||||
}
|
||||
|
||||
std::wstring AkVCam::IpcBridgePrivate::locateDriverPath() const
|
||||
std::string AkVCam::IpcBridgePrivate::locatePluginPath()
|
||||
{
|
||||
AkLogFunction();
|
||||
std::wstring driverPath;
|
||||
Dl_info info;
|
||||
memset(&info, 0, sizeof(Dl_info));
|
||||
dladdr(reinterpret_cast<void *>(&AkVCam::IpcBridgePrivate::locatePluginPath),
|
||||
&info);
|
||||
std::string dirName = dirname(const_cast<char *>(info.dli_fname));
|
||||
|
||||
for (auto it = this->driverPaths()->rbegin();
|
||||
it != this->driverPaths()->rend();
|
||||
it++) {
|
||||
auto path = *it;
|
||||
path = replace(path, L"\\", L"/");
|
||||
|
||||
if (path.back() != L'/')
|
||||
path += L'/';
|
||||
|
||||
path += CMIO_PLUGIN_NAME_L L".plugin";
|
||||
|
||||
if (!this->fileExists(path + L"/Contents/MacOS/" CMIO_PLUGIN_NAME_L))
|
||||
continue;
|
||||
|
||||
if (!this->fileExists(path + L"/Contents/Resources/" CMIO_PLUGIN_ASSISTANT_NAME_L))
|
||||
continue;
|
||||
|
||||
driverPath = path;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return driverPath;
|
||||
return realPath(dirName + "/../..");
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ LIBS = \
|
|||
-L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \
|
||||
-L$${OUT_PWD}/../VCamIPC/$${BIN_DIR} -lVCamIPC \
|
||||
-framework CoreFoundation \
|
||||
-framework CoreGraphics \
|
||||
-framework CoreMedia \
|
||||
-framework CoreMediaIO \
|
||||
-framework CoreVideo \
|
||||
|
@ -57,7 +58,6 @@ TEMPLATE = lib
|
|||
HEADERS += \
|
||||
src/plugin.h \
|
||||
src/plugininterface.h \
|
||||
src/utils.h \
|
||||
src/device.h \
|
||||
src/object.h \
|
||||
src/stream.h \
|
||||
|
@ -69,7 +69,6 @@ HEADERS += \
|
|||
SOURCES += \
|
||||
src/plugin.cpp \
|
||||
src/plugininterface.cpp \
|
||||
src/utils.cpp \
|
||||
src/device.cpp \
|
||||
src/object.cpp \
|
||||
src/stream.cpp \
|
||||
|
@ -88,8 +87,7 @@ RESOURCESPATH = $${CONTENTSPATH}/Resources
|
|||
|
||||
INSTALLS += \
|
||||
targetLib \
|
||||
infoPlist \
|
||||
resources
|
||||
infoPlist
|
||||
|
||||
targetLib.files = $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME})
|
||||
targetLib.path = $${PREFIX}/$${MACBINPATH}
|
||||
|
@ -99,12 +97,7 @@ infoPlist.files = $$shell_path($${OUT_PWD}/Info.plist)
|
|||
infoPlist.path = $${PREFIX}/$${CONTENTSPATH}
|
||||
infoPlist.CONFIG += no_check_exist
|
||||
|
||||
resources.files = ../../share/TestFrame/TestFrame.bmp
|
||||
resources.path = $${PREFIX}/$${RESOURCESPATH}
|
||||
|
||||
QMAKE_POST_LINK = \
|
||||
$$sprintf($$QMAKE_MKDIR_CMD, $$shell_path($${OUT_PWD}/../../$${MACBINPATH})) $${CMD_SEP} \
|
||||
$$sprintf($$QMAKE_MKDIR_CMD, $$shell_path($${OUT_PWD}/../../$${RESOURCESPATH})) $${CMD_SEP} \
|
||||
$(COPY) $$shell_path($${OUT_PWD}/Info.plist) $$shell_path($${OUT_PWD}/../../$${CONTENTSPATH}) $${CMD_SEP} \
|
||||
$(COPY) $$shell_path($${OUT_PWD}/$${BIN_DIR}/lib$${CMIO_PLUGIN_NAME}.$${QMAKE_EXTENSION_SHLIB}) $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME}) $${CMD_SEP} \
|
||||
$(COPY) $$shell_path($${PWD}/../../share/TestFrame/TestFrame.bmp) $$shell_path($${OUT_PWD}/../../$${RESOURCESPATH})
|
||||
$(COPY) $$shell_path($${OUT_PWD}/$${BIN_DIR}/lib$${CMIO_PLUGIN_NAME}.$${QMAKE_EXTENSION_SHLIB}) $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME}) $${CMD_SEP}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "device.h"
|
||||
#include "utils.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
#include "VCamUtils/src/logger/logger.h"
|
||||
|
||||
AkVCam::Device::Device(CMIOHardwarePlugInRef pluginInterface,
|
||||
|
@ -194,16 +194,28 @@ void AkVCam::Device::frameReady(const AkVCam::VideoFrame &frame)
|
|||
stream.second->frameReady(frame);
|
||||
}
|
||||
|
||||
void AkVCam::Device::setPicture(const std::string &picture)
|
||||
{
|
||||
for (auto &stream: this->m_streams)
|
||||
stream.second->setPicture(picture);
|
||||
}
|
||||
|
||||
void AkVCam::Device::setBroadcasting(const std::string &broadcaster)
|
||||
{
|
||||
for (auto &stream: this->m_streams)
|
||||
stream.second->setBroadcasting(broadcaster);
|
||||
}
|
||||
|
||||
void AkVCam::Device::setMirror(bool horizontalMirror, bool verticalMirror)
|
||||
void AkVCam::Device::setHorizontalMirror(bool horizontalMirror)
|
||||
{
|
||||
for (auto &stream: this->m_streams)
|
||||
stream.second->setMirror(horizontalMirror, verticalMirror);
|
||||
stream.second->setHorizontalMirror(horizontalMirror);
|
||||
}
|
||||
|
||||
void AkVCam::Device::setVerticalMirror(bool verticalMirror)
|
||||
{
|
||||
for (auto &stream: this->m_streams)
|
||||
stream.second->setVerticalMirror(verticalMirror);
|
||||
}
|
||||
|
||||
void AkVCam::Device::setScaling(Scaling scaling)
|
||||
|
|
|
@ -52,8 +52,10 @@ namespace AkVCam
|
|||
|
||||
void serverStateChanged(IpcBridge::ServerState state);
|
||||
void frameReady(const VideoFrame &frame);
|
||||
void setPicture(const std::string &picture);
|
||||
void setBroadcasting(const std::string &broadcaster);
|
||||
void setMirror(bool horizontalMirror, bool verticalMirror);
|
||||
void setHorizontalMirror(bool horizontalMirror);
|
||||
void setVerticalMirror(bool verticalMirror);
|
||||
void setScaling(Scaling scaling);
|
||||
void setAspectRatio(AspectRatio aspectRatio);
|
||||
void setSwapRgb(bool swap);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "object.h"
|
||||
#include "utils.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
|
||||
AkVCam::Object::Object(CMIOHardwarePlugInRef pluginInterface,
|
||||
Object *parent):
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
#include "objectinterface.h"
|
||||
#include "utils.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
#include "VCamUtils/src/logger/logger.h"
|
||||
|
||||
AkVCam::ObjectInterface::ObjectInterface():
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <map>
|
||||
|
||||
#include "object.h"
|
||||
#include "utils.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
#include "VCamUtils/src/image/videoformat.h"
|
||||
|
||||
namespace AkVCam
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
|
||||
#include "plugin.h"
|
||||
#include "utils.h"
|
||||
#include "PlatformUtils/src/preferences.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
#include "VCamUtils/src/ipcbridge.h"
|
||||
#include "VCamUtils/src/logger/logger.h"
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
#include <IOKit/audio/IOAudioTypes.h>
|
||||
|
||||
#include "plugininterface.h"
|
||||
#include "utils.h"
|
||||
#include "Assistant/src/assistantglobals.h"
|
||||
#include "PlatformUtils/src/preferences.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
#include "VCamUtils/src/image/videoformat.h"
|
||||
#include "VCamUtils/src/ipcbridge.h"
|
||||
#include "VCamUtils/src/logger/logger.h"
|
||||
|
@ -163,11 +163,9 @@ AkVCam::PluginInterface::PluginInterface():
|
|||
this->d->m_ipcBridge.connectDeviceRemoved(this, &PluginInterface::deviceRemoved);
|
||||
this->d->m_ipcBridge.connectDevicesUpdated(this, &PluginInterface::devicesUpdated);
|
||||
this->d->m_ipcBridge.connectFrameReady(this, &PluginInterface::frameReady);
|
||||
this->d->m_ipcBridge.connectPictureChanged(this, &PluginInterface::pictureChanged);
|
||||
this->d->m_ipcBridge.connectBroadcastingChanged(this, &PluginInterface::setBroadcasting);
|
||||
this->d->m_ipcBridge.connectMirrorChanged(this, &PluginInterface::setMirror);
|
||||
this->d->m_ipcBridge.connectScalingChanged(this, &PluginInterface::setScaling);
|
||||
this->d->m_ipcBridge.connectAspectRatioChanged(this, &PluginInterface::setAspectRatio);
|
||||
this->d->m_ipcBridge.connectSwapRgbChanged(this, &PluginInterface::setSwapRgb);
|
||||
this->d->m_ipcBridge.connectControlsChanged(this, &PluginInterface::controlsChanged);
|
||||
}
|
||||
|
||||
AkVCam::PluginInterface::~PluginInterface()
|
||||
|
@ -318,6 +316,16 @@ void AkVCam::PluginInterface::frameReady(void *userData,
|
|||
device->frameReady(frame);
|
||||
}
|
||||
|
||||
void AkVCam::PluginInterface::pictureChanged(void *userData,
|
||||
const std::string &picture)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto self = reinterpret_cast<PluginInterface *>(userData);
|
||||
|
||||
for (auto device: self->m_devices)
|
||||
device->setPicture(picture);
|
||||
}
|
||||
|
||||
void AkVCam::PluginInterface::setBroadcasting(void *userData,
|
||||
const std::string &deviceId,
|
||||
const std::string &broadcaster)
|
||||
|
@ -332,53 +340,31 @@ void AkVCam::PluginInterface::setBroadcasting(void *userData,
|
|||
device->setBroadcasting(broadcaster);
|
||||
}
|
||||
|
||||
void AkVCam::PluginInterface::setMirror(void *userData,
|
||||
void AkVCam::PluginInterface::controlsChanged(void *userData,
|
||||
const std::string &deviceId,
|
||||
bool horizontalMirror,
|
||||
bool verticalMirror)
|
||||
const std::map<std::string, int> &controls)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogInfo() << "Device: " << deviceId << std::endl;
|
||||
auto self = reinterpret_cast<PluginInterface *>(userData);
|
||||
|
||||
for (auto device: self->m_devices)
|
||||
if (device->deviceId() == deviceId)
|
||||
device->setMirror(horizontalMirror, verticalMirror);
|
||||
if (device->deviceId() == deviceId) {
|
||||
if (controls.count("hflip"))
|
||||
device->setHorizontalMirror(controls.at("hflip"));
|
||||
|
||||
if (controls.count("vflip"))
|
||||
device->setHorizontalMirror(controls.at("vflip"));
|
||||
|
||||
if (controls.count("scaling"))
|
||||
device->setScaling(Scaling(controls.at("scaling")));
|
||||
|
||||
if (controls.count("aspect_ratio"))
|
||||
device->setAspectRatio(AspectRatio(controls.at("aspect_ratio")));
|
||||
|
||||
if (controls.count("swap_rgb"))
|
||||
device->setSwapRgb(controls.at("swap_rgb"));
|
||||
}
|
||||
|
||||
void AkVCam::PluginInterface::setScaling(void *userData,
|
||||
const std::string &deviceId,
|
||||
Scaling scaling)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto self = reinterpret_cast<PluginInterface *>(userData);
|
||||
|
||||
for (auto device: self->m_devices)
|
||||
if (device->deviceId() == deviceId)
|
||||
device->setScaling(scaling);
|
||||
}
|
||||
|
||||
void AkVCam::PluginInterface::setAspectRatio(void *userData,
|
||||
const std::string &deviceId,
|
||||
AspectRatio aspectRatio)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto self = reinterpret_cast<PluginInterface *>(userData);
|
||||
|
||||
for (auto device: self->m_devices)
|
||||
if (device->deviceId() == deviceId)
|
||||
device->setAspectRatio(aspectRatio);
|
||||
}
|
||||
|
||||
void AkVCam::PluginInterface::setSwapRgb(void *userData,
|
||||
const std::string &deviceId,
|
||||
bool swap)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto self = reinterpret_cast<PluginInterface *>(userData);
|
||||
|
||||
for (auto device: self->m_devices)
|
||||
if (device->deviceId() == deviceId)
|
||||
device->setSwapRgb(swap);
|
||||
}
|
||||
|
||||
void AkVCam::PluginInterface::addListener(void *userData,
|
||||
|
@ -412,6 +398,13 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
|
|||
device->connectRemoveListener(this, &PluginInterface::removeListener);
|
||||
this->m_devices.push_back(device);
|
||||
|
||||
auto cameraIndex = Preferences::cameraFromPath(deviceId);
|
||||
auto hflip = Preferences::cameraControlValue(cameraIndex, "hflip");
|
||||
auto vflip = Preferences::cameraControlValue(cameraIndex, "vflip");
|
||||
auto scaling = Preferences::cameraControlValue(cameraIndex, "scaling");
|
||||
auto aspectRatio = Preferences::cameraControlValue(cameraIndex, "aspect_ratio");
|
||||
auto swapRgb = Preferences::cameraControlValue(cameraIndex, "swap_rgb");
|
||||
|
||||
// Define device properties.
|
||||
device->properties().setProperty(kCMIOObjectPropertyName,
|
||||
description);
|
||||
|
@ -469,11 +462,11 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
|
|||
}
|
||||
|
||||
device->setBroadcasting(this->d->m_ipcBridge.broadcaster(deviceId));
|
||||
device->setMirror(this->d->m_ipcBridge.isHorizontalMirrored(deviceId),
|
||||
this->d->m_ipcBridge.isVerticalMirrored(deviceId));
|
||||
device->setScaling(this->d->m_ipcBridge.scalingMode(deviceId));
|
||||
device->setAspectRatio(this->d->m_ipcBridge.aspectRatioMode(deviceId));
|
||||
device->setSwapRgb(this->d->m_ipcBridge.swapRgb(deviceId));
|
||||
device->setHorizontalMirror(hflip);
|
||||
device->setVerticalMirror(vflip);
|
||||
device->setScaling(Scaling(scaling));
|
||||
device->setAspectRatio(AspectRatio(aspectRatio));
|
||||
device->setSwapRgb(swapRgb);
|
||||
|
||||
return true;
|
||||
|
||||
|
@ -509,11 +502,17 @@ void AkVCam::PluginInterfacePrivate::updateDevices()
|
|||
{
|
||||
for (auto &device: this->self->m_devices) {
|
||||
device->setBroadcasting(this->m_ipcBridge.broadcaster(device->deviceId()));
|
||||
device->setMirror(this->m_ipcBridge.isHorizontalMirrored(device->deviceId()),
|
||||
this->m_ipcBridge.isVerticalMirrored(device->deviceId()));
|
||||
device->setScaling(this->m_ipcBridge.scalingMode(device->deviceId()));
|
||||
device->setAspectRatio(this->m_ipcBridge.aspectRatioMode(device->deviceId()));
|
||||
device->setSwapRgb(this->m_ipcBridge.swapRgb(device->deviceId()));
|
||||
auto cameraIndex = Preferences::cameraFromPath(device->deviceId());
|
||||
auto hflip = Preferences::cameraControlValue(cameraIndex, "hflip");
|
||||
auto vflip = Preferences::cameraControlValue(cameraIndex, "vflip");
|
||||
auto scaling = Preferences::cameraControlValue(cameraIndex, "scaling");
|
||||
auto aspectRatio = Preferences::cameraControlValue(cameraIndex, "aspect_ratio");
|
||||
auto swapRgb = Preferences::cameraControlValue(cameraIndex, "swap_rgb");
|
||||
device->setHorizontalMirror(hflip);
|
||||
device->setVerticalMirror(vflip);
|
||||
device->setScaling(Scaling(scaling));
|
||||
device->setAspectRatio(AspectRatio(aspectRatio));
|
||||
device->setSwapRgb(swapRgb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,22 +58,14 @@ namespace AkVCam
|
|||
static void frameReady(void *userData,
|
||||
const std::string &deviceId,
|
||||
const VideoFrame &frame);
|
||||
static void pictureChanged(void *userData,
|
||||
const std::string &picture);
|
||||
static void setBroadcasting(void *userData,
|
||||
const std::string &deviceId,
|
||||
const std::string &broadcaster);
|
||||
static void setMirror(void *userData,
|
||||
static void controlsChanged(void *userData,
|
||||
const std::string &deviceId,
|
||||
bool horizontalMirror,
|
||||
bool verticalMirror);
|
||||
static void setScaling(void *userData,
|
||||
const std::string &deviceId,
|
||||
Scaling scaling);
|
||||
static void setAspectRatio(void *userData,
|
||||
const std::string &deviceId,
|
||||
AspectRatio aspectRatio);
|
||||
static void setSwapRgb(void *userData,
|
||||
const std::string &deviceId,
|
||||
bool swap);
|
||||
const std::map<std::string, int> &controls);
|
||||
static void addListener(void *userData,
|
||||
const std::string &deviceId);
|
||||
static void removeListener(void *userData,
|
||||
|
|
|
@ -17,15 +17,18 @@
|
|||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <thread>
|
||||
#include <locale>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <CoreMediaIO/CMIOSampleBuffer.h>
|
||||
|
||||
#include "stream.h"
|
||||
#include "clock.h"
|
||||
#include "utils.h"
|
||||
#include "PlatformUtils/src/preferences.h"
|
||||
#include "PlatformUtils/src/utils.h"
|
||||
#include "VCamUtils/src/image/videoformat.h"
|
||||
#include "VCamUtils/src/image/videoframe.h"
|
||||
#include "VCamUtils/src/logger/logger.h"
|
||||
|
@ -78,7 +81,7 @@ AkVCam::Stream::Stream(bool registerObject,
|
|||
|
||||
if (!picture.empty()) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
|
||||
this->d->m_testFrame.load(cv.to_bytes(picture));
|
||||
this->d->m_testFrame = loadPicture(cv.to_bytes(picture));
|
||||
}
|
||||
|
||||
this->d->m_clock =
|
||||
|
@ -158,6 +161,20 @@ OSStatus AkVCam::Stream::registerObject(bool regist)
|
|||
return status;
|
||||
}
|
||||
|
||||
void AkVCam::Stream::setPicture(const std::string &picture)
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogDebug() << "Picture: " << picture;
|
||||
this->d->m_testFrame = loadPicture(picture);
|
||||
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();
|
||||
}
|
||||
|
||||
void AkVCam::Stream::setBridge(IpcBridge *bridge)
|
||||
{
|
||||
this->d->m_bridge = bridge;
|
||||
|
@ -307,15 +324,24 @@ void AkVCam::Stream::setBroadcasting(const std::string &broadcaster)
|
|||
this->d->m_mutex.unlock();
|
||||
}
|
||||
|
||||
void AkVCam::Stream::setMirror(bool horizontalMirror, bool verticalMirror)
|
||||
void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (this->d->m_horizontalMirror == horizontalMirror
|
||||
&& this->d->m_verticalMirror == verticalMirror)
|
||||
if (this->d->m_horizontalMirror == horizontalMirror)
|
||||
return;
|
||||
|
||||
this->d->m_horizontalMirror = horizontalMirror;
|
||||
this->d->updateTestFrame();
|
||||
}
|
||||
|
||||
void AkVCam::Stream::setVerticalMirror(bool verticalMirror)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (this->d->m_verticalMirror == verticalMirror)
|
||||
return;
|
||||
|
||||
this->d->m_verticalMirror = verticalMirror;
|
||||
this->d->updateTestFrame();
|
||||
}
|
||||
|
|
|
@ -54,8 +54,10 @@ namespace AkVCam
|
|||
|
||||
void serverStateChanged(IpcBridge::ServerState state);
|
||||
void frameReady(const VideoFrame &frame);
|
||||
void setPicture(const std::string &picture);
|
||||
void setBroadcasting(const std::string &broadcaster);
|
||||
void setMirror(bool horizontalMirror, bool verticalMirror);
|
||||
void setHorizontalMirror(bool horizontalMirror);
|
||||
void setVerticalMirror(bool verticalMirror);
|
||||
void setScaling(Scaling scaling);
|
||||
void setAspectRatio(AspectRatio aspectRatio);
|
||||
void setSwapRgb(bool swap);
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/* akvirtualcamera, virtual camera for Mac and Windows.
|
||||
* Copyright (C) 2020 Gonzalo Exequiel Pedone
|
||||
*
|
||||
* akvirtualcamera is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* akvirtualcamera is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with akvirtualcamera. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
inline const std::map<AkVCam::PixelFormat, FourCharCode> *formatsTable()
|
||||
{
|
||||
static const std::map<AkVCam::PixelFormat, FourCharCode> formatsTable {
|
||||
{AkVCam::PixelFormatRGB32, kCMPixelFormat_32ARGB },
|
||||
{AkVCam::PixelFormatRGB24, kCMPixelFormat_24RGB },
|
||||
{AkVCam::PixelFormatRGB16, kCMPixelFormat_16LE565 },
|
||||
{AkVCam::PixelFormatRGB15, kCMPixelFormat_16LE555 },
|
||||
{AkVCam::PixelFormatUYVY , kCMPixelFormat_422YpCbCr8 },
|
||||
{AkVCam::PixelFormatYUY2 , kCMPixelFormat_422YpCbCr8_yuvs}
|
||||
};
|
||||
|
||||
return &formatsTable;
|
||||
}
|
||||
|
||||
bool AkVCam::uuidEqual(const REFIID &uuid1, const CFUUIDRef uuid2)
|
||||
{
|
||||
auto iid2 = CFUUIDGetUUIDBytes(uuid2);
|
||||
auto puuid1 = reinterpret_cast<const UInt8 *>(&uuid1);
|
||||
auto puuid2 = reinterpret_cast<const UInt8 *>(&iid2);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
if (puuid1[i] != puuid2[i])
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string AkVCam::enumToString(UInt32 value)
|
||||
{
|
||||
auto valueChr = reinterpret_cast<char *>(&value);
|
||||
std::stringstream ss;
|
||||
|
||||
for (int i = 3; i >= 0; i--)
|
||||
if (valueChr[i] < 0)
|
||||
ss << std::hex << valueChr[i];
|
||||
else if (valueChr[i] < 32)
|
||||
ss << int(valueChr[i]);
|
||||
else
|
||||
ss << valueChr[i];
|
||||
|
||||
return "'" + ss.str() + "'";
|
||||
}
|
||||
|
||||
FourCharCode AkVCam::formatToCM(PixelFormat format)
|
||||
{
|
||||
for (auto &fmt: *formatsTable())
|
||||
if (fmt.first == format)
|
||||
return fmt.second;
|
||||
|
||||
return FourCharCode(0);
|
||||
}
|
||||
|
||||
AkVCam::PixelFormat AkVCam::formatFromCM(FourCharCode format)
|
||||
{
|
||||
for (auto &fmt: *formatsTable())
|
||||
if (fmt.second == format)
|
||||
return fmt.first;
|
||||
|
||||
return PixelFormat(0);
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/* akvirtualcamera, virtual camera for Mac and Windows.
|
||||
* Copyright (C) 2020 Gonzalo Exequiel Pedone
|
||||
*
|
||||
* akvirtualcamera is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* akvirtualcamera is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with akvirtualcamera. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Web-Site: http://webcamoid.github.io/
|
||||
*/
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <CoreMediaIO/CMIOHardwarePlugIn.h>
|
||||
#include <CoreMedia/CMFormatDescription.h>
|
||||
|
||||
#include "VCamUtils/src/image/videoformattypes.h"
|
||||
|
||||
namespace AkVCam
|
||||
{
|
||||
bool uuidEqual(const REFIID &uuid1, const CFUUIDRef uuid2);
|
||||
std::string enumToString(UInt32 value);
|
||||
FourCharCode formatToCM(PixelFormat format);
|
||||
PixelFormat formatFromCM(FourCharCode format);
|
||||
}
|
||||
|
||||
#endif // UTILS_H
|
Loading…
Reference in a new issue