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:
Gonzalo Exequiel Pedone 2020-09-09 12:21:03 -03:00
parent 39f383560c
commit 39c84d9788
No known key found for this signature in database
GPG key ID: B8B09E63E9B85BAF
29 changed files with 1315 additions and 902 deletions

View file

@ -21,10 +21,12 @@
#include <codecvt> #include <codecvt>
#include <iostream> #include <iostream>
#include <functional> #include <functional>
#include <locale>
#include <sstream> #include <sstream>
#include "cmdparser.h" #include "cmdparser.h"
#include "VCamUtils/src/ipcbridge.h" #include "VCamUtils/src/ipcbridge.h"
#include "VCamUtils/src/settings.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/image/videoframe.h"
#include "VCamUtils/src/logger/logger.h" #include "VCamUtils/src/logger/logger.h"
@ -33,6 +35,9 @@
std::bind(&member, this->d, std::placeholders::_1, std::placeholders::_2) std::bind(&member, this->d, std::placeholders::_1, std::placeholders::_2)
namespace AkVCam { namespace AkVCam {
using StringMatrix = std::vector<StringVector>;
using VideoFormatMatrix = std::vector<std::vector<VideoFormat>>;
struct CmdParserFlags struct CmdParserFlags
{ {
StringVector flags; StringVector flags;
@ -56,6 +61,7 @@ namespace AkVCam {
IpcBridge m_ipcBridge; IpcBridge m_ipcBridge;
bool m_parseable {false}; bool m_parseable {false};
static const std::map<ControlType, std::string> &typeStrMap();
std::string basename(const std::string &path); std::string basename(const std::string &path);
void printFlags(const std::vector<CmdParserFlags> &cmdFlags, void printFlags(const std::vector<CmdParserFlags> &cmdFlags,
size_t indent); size_t indent);
@ -63,8 +69,13 @@ namespace AkVCam {
size_t maxArgumentsLength(); size_t maxArgumentsLength();
size_t maxFlagsLength(const std::vector<CmdParserFlags> &flags); size_t maxFlagsLength(const std::vector<CmdParserFlags> &flags);
size_t maxFlagsValueLength(const std::vector<CmdParserFlags> &flags); size_t maxFlagsValueLength(const std::vector<CmdParserFlags> &flags);
size_t maxStringLength(const StringVector &strings); size_t maxColumnLength(const StringVector &table,
size_t maxStringLength(const WStringVector &strings); 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); CmdParserCommand *parserCommand(const std::string &command);
const CmdParserFlags *parserFlag(const std::vector<CmdParserFlags> &cmdFlags, const CmdParserFlags *parserFlag(const std::vector<CmdParserFlags> &cmdFlags,
const std::string &flag); const std::string &flag);
@ -96,12 +107,26 @@ namespace AkVCam {
int stream(const StringMap &flags, const StringVector &args); int stream(const StringMap &flags, const StringVector &args);
int showControls(const StringMap &flags, const StringVector &args); int showControls(const StringMap &flags, const StringVector &args);
int readControl(const StringMap &flags, const StringVector &args); int readControl(const StringMap &flags, const StringVector &args);
int writeControl(const StringMap &flags, const StringVector &args); int writeControls(const StringMap &flags, const StringVector &args);
int picture(const StringMap &flags, const StringVector &args); int picture(const StringMap &flags, const StringVector &args);
int setPicture(const StringMap &flags, const StringVector &args); int setPicture(const StringMap &flags, const StringVector &args);
int logLevel(const StringMap &flags, const StringVector &args); int logLevel(const StringMap &flags, const StringVector &args);
int setLogLevel(const StringMap &flags, const StringVector &args); int setLogLevel(const StringMap &flags, const StringVector &args);
int showClients(const StringMap &flags, const StringVector &args); int showClients(const StringMap &flags, const StringVector &args);
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); std::string operator *(const std::string &str, size_t n);
@ -192,10 +217,31 @@ AkVCam::CmdParser::CmdParser()
"DEVICE CONTROL", "DEVICE CONTROL",
"Read device control.", "Read device control.",
AKVCAM_BIND_FUNC(CmdParserPrivate::readControl)); AKVCAM_BIND_FUNC(CmdParserPrivate::readControl));
this->addCommand("set-control", this->addFlags("get-control",
"DEVICE CONTROL VALUE", {"-c", "--description"},
"Write device control value.", "Show control description.");
AKVCAM_BIND_FUNC(CmdParserPrivate::writeControl)); 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", this->addCommand("picture",
"", "",
"Placeholder picture to show when no streaming.", "Placeholder picture to show when no streaming.",
@ -352,6 +398,17 @@ void AkVCam::CmdParser::addFlags(const std::string &command,
this->addFlags(command, flags, "", helpString); 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) std::string AkVCam::CmdParserPrivate::basename(const std::string &path)
{ {
auto rit = auto rit =
@ -393,10 +450,8 @@ void AkVCam::CmdParserPrivate::printFlags(const std::vector<CmdParserFlags> &cmd
std::cout << std::string(spaces.data(), indent) std::cout << std::string(spaces.data(), indent)
<< fill(allFlags, maxFlagsLen); << fill(allFlags, maxFlagsLen);
if (maxFlagsValueLen > 0) { if (maxFlagsValueLen > 0)
std::cout << " " std::cout << " " << fill(flag.value, maxFlagsValueLen);
<< fill(flag.value, maxFlagsValueLen);
}
std::cout << " " std::cout << " "
<< flag.helpString << flag.helpString
@ -444,24 +499,64 @@ size_t AkVCam::CmdParserPrivate::maxFlagsValueLength(const std::vector<CmdParser
return length; 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 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); length = std::max(str.size(), length);
}
return 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) for (size_t x = 0; x < width; x++)
length = std::max(str.size(), length); 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) AkVCam::CmdParserCommand *AkVCam::CmdParserPrivate::parserCommand(const std::string &command)
@ -609,36 +704,21 @@ int AkVCam::CmdParserPrivate::showDevices(const StringMap &flags,
for (auto &device: devices) for (auto &device: devices)
std::cout << device << std::endl; std::cout << device << std::endl;
} else { } else {
StringVector devicesColumn; std::vector<std::string> table {
WStringVector descriptionsColumn; "Device",
"Description"
devicesColumn.push_back("Device"); };
descriptionsColumn.push_back(L"Description"); auto columns = table.size();
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
for (auto &device: devices) { for (auto &device: devices) {
devicesColumn.push_back(device); table.push_back(device);
descriptionsColumn.push_back(this->m_ipcBridge.description(device)); auto description =
cv.to_bytes(this->m_ipcBridge.description(device));
table.push_back(description);
} }
auto devicesColumnSize = this->maxStringLength(devicesColumn); this->drawTable(table, columns);
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;
}
} }
return 0; return 0;
@ -1003,7 +1083,7 @@ int AkVCam::CmdParserPrivate::update(const StringMap &flags,
{ {
UNUSED(flags); UNUSED(flags);
UNUSED(args); UNUSED(args);
this->m_ipcBridge.update(); this->m_ipcBridge.updateDevices();
return 0; return 0;
} }
@ -1011,6 +1091,30 @@ int AkVCam::CmdParserPrivate::update(const StringMap &flags,
int AkVCam::CmdParserPrivate::loadSettings(const AkVCam::StringMap &flags, int AkVCam::CmdParserPrivate::loadSettings(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args) 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; return 0;
} }
@ -1108,18 +1212,279 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
int AkVCam::CmdParserPrivate::showControls(const StringMap &flags, int AkVCam::CmdParserPrivate::showControls(const StringMap &flags,
const StringVector &args) 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; return 0;
} }
int AkVCam::CmdParserPrivate::readControl(const StringMap &flags, int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
const StringVector &args) const StringVector &args)
{ {
UNUSED(flags);
if (args.size() < 3) {
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; 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) 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; return 0;
} }
@ -1195,7 +1560,6 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
{ {
UNUSED(flags); UNUSED(flags);
UNUSED(args); UNUSED(args);
auto clients = this->m_ipcBridge.clientsPids(); auto clients = this->m_ipcBridge.clientsPids();
if (clients.empty()) if (clients.empty())
@ -1208,38 +1572,217 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
<< this->m_ipcBridge.clientExe(pid) << this->m_ipcBridge.clientExe(pid)
<< std::endl; << std::endl;
} else { } else {
StringVector pidsColumn; std::vector<std::string> table {
StringVector exesColumn; "Pid",
"Executable"
pidsColumn.push_back("Pid"); };
exesColumn.push_back("Executable"); auto columns = table.size();
for (auto &pid: clients) { for (auto &pid: clients) {
pidsColumn.push_back(std::to_string(pid)); table.push_back(std::to_string(pid));
exesColumn.push_back(this->m_ipcBridge.clientExe(pid)); table.push_back(this->m_ipcBridge.clientExe(pid));
} }
auto pidsColumnSize = this->maxStringLength(pidsColumn); this->drawTable(table, columns);
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;
} }
return 0; 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::string AkVCam::operator *(const std::string &str, size_t n)
{ {
std::stringstream ss; std::stringstream ss;

View file

@ -20,8 +20,9 @@
#ifndef CMDPARSER_H #ifndef CMDPARSER_H
#define CMDPARSER_H #define CMDPARSER_H
#include <string> #include <functional>
#include <map> #include <map>
#include <string>
#include <vector> #include <vector>
namespace AkVCam { namespace AkVCam {

View file

@ -33,6 +33,26 @@ namespace AkVCam
class VideoFormat; class VideoFormat;
class VideoFrame; 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 class IpcBridge
{ {
public: public:
@ -53,6 +73,8 @@ namespace AkVCam
AKVCAM_SIGNAL(FrameReady, AKVCAM_SIGNAL(FrameReady,
const std::string &deviceId, const std::string &deviceId,
const VideoFrame &frame) const VideoFrame &frame)
AKVCAM_SIGNAL(PictureChanged,
const std::string &picture)
AKVCAM_SIGNAL(DeviceAdded, AKVCAM_SIGNAL(DeviceAdded,
const std::string &deviceId) const std::string &deviceId)
AKVCAM_SIGNAL(DeviceRemoved, AKVCAM_SIGNAL(DeviceRemoved,
@ -67,19 +89,9 @@ namespace AkVCam
AKVCAM_SIGNAL(BroadcastingChanged, AKVCAM_SIGNAL(BroadcastingChanged,
const std::string &deviceId, const std::string &deviceId,
const std::string &broadcaster) const std::string &broadcaster)
AKVCAM_SIGNAL(MirrorChanged, AKVCAM_SIGNAL(ControlsChanged,
const std::string &deviceId, const std::string &deviceId,
bool horizontalMirror, const std::map<std::string, int> &controls)
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)
public: public:
IpcBridge(); IpcBridge();
@ -147,20 +159,9 @@ namespace AkVCam
// Return return the status of the device. // Return return the status of the device.
std::string broadcaster(const std::string &deviceId) const; std::string broadcaster(const std::string &deviceId) const;
// Device is horizontal mirrored, std::vector<DeviceControl> controls(const std::string &deviceId);
bool isHorizontalMirrored(const std::string &deviceId); void setControls(const std::string &deviceId,
const std::map<std::string, int> &controls);
// 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);
// Returns the clients that are capturing from a virtual camera. // Returns the clients that are capturing from a virtual camera.
std::vector<std::string> listeners(const std::string &deviceId); std::vector<std::string> listeners(const std::string &deviceId);
@ -179,7 +180,6 @@ namespace AkVCam
const VideoFormat &format, const VideoFormat &format,
int index=-1); int index=-1);
void removeFormat(const std::string &deviceId, int index); void removeFormat(const std::string &deviceId, int index);
void update();
void updateDevices(); void updateDevices();
// Start frame transfer to the device. // Start frame transfer to the device.
@ -193,22 +193,6 @@ namespace AkVCam
bool write(const std::string &deviceId, bool write(const std::string &deviceId,
const VideoFrame &frame); 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 */ /* Client */
// Increment the count of device listeners // Increment the count of device listeners

View file

@ -125,14 +125,14 @@ std::ostream &AkVCam::Logger::log(int logLevel)
return dummy; return dummy;
if (loggerPrivate()->fileName.empty()) if (loggerPrivate()->fileName.empty())
return std::cout; return std::cerr;
if (!loggerPrivate()->stream.is_open()) if (!loggerPrivate()->stream.is_open())
loggerPrivate()->stream.open(loggerPrivate()->fileName, loggerPrivate()->stream.open(loggerPrivate()->fileName,
std::ios_base::out | std::ios_base::app); std::ios_base::out | std::ios_base::app);
if (!loggerPrivate()->stream.is_open()) if (!loggerPrivate()->stream.is_open())
return std::cout; return std::cerr;
return loggerPrivate()->stream; return loggerPrivate()->stream;
} }

View file

@ -270,7 +270,7 @@ int32_t AkVCam::Settings::valueInt32(const std::string &key) const
auto value = this->value(key); auto value = this->value(key);
if (value.empty()) if (value.empty())
return false; return 0;
char *p = nullptr; char *p = nullptr;
@ -282,7 +282,7 @@ uint32_t AkVCam::Settings::valueUInt32(const std::string &key) const
auto value = this->value(key); auto value = this->value(key);
if (value.empty()) if (value.empty())
return false; return 0;
char *p = nullptr; char *p = nullptr;
@ -297,18 +297,23 @@ std::vector<std::string> AkVCam::Settings::valueList(const std::string &key,
if (value.empty()) if (value.empty())
return {}; return {};
char *valuePtr = const_cast<char *>(value.c_str());
std::vector<std::string> result; std::vector<std::string> result;
size_t pos = 0;
for (;;) { do {
auto element = strsep(&valuePtr, separators.c_str()); auto index = value.size();
if (!element) for (auto &separator: separators) {
break; 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; return result;
} }
@ -374,16 +379,15 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line,
return element; return element;
} }
auto pairSep = strchr(line.c_str(), '='); if (line.find('=') == std::string::npos) {
if (!pairSep) {
if (ok) if (ok)
*ok = false; *ok = false;
return {}; 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(), '\\', '/'); std::replace(element.key.begin(), element.key.end(), '\\', '/');
if (element.key.empty()) { if (element.key.empty()) {
@ -393,8 +397,7 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line,
return {}; return {};
} }
auto offset = (size_t) (pairSep - line.c_str() + 1); element.value = trimmed(pair.second);
element.value = trimmed(line.substr(offset, line.size() - offset));
element.value = this->parseString(element.value); element.value = this->parseString(element.value);
if (ok) if (ok)

View file

@ -212,3 +212,17 @@ std::vector<std::string> AkVCam::split(const std::string &str, char separator)
return elements; 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};
}

View file

@ -158,6 +158,8 @@ namespace AkVCam
std::string join(const std::vector<std::string> &strs, std::string join(const std::vector<std::string> &strs,
const std::string &separator); const std::string &separator);
std::vector<std::string> split(const std::string &str, char separator); std::vector<std::string> split(const std::string &str, char separator);
std::pair<std::string, std::string> splitOnce(const std::string &str,
const std::string &separator);
} }
#endif // AKVCAMUTILS_UTILS_H #endif // AKVCAMUTILS_UTILS_H

View file

@ -17,6 +17,7 @@
* Web-Site: http://webcamoid.github.io/ * Web-Site: http://webcamoid.github.io/
*/ */
#include <algorithm>
#include <map> #include <map>
#include <sstream> #include <sstream>
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
@ -45,11 +46,6 @@ namespace AkVCam
std::vector<VideoFormat> formats; std::vector<VideoFormat> formats;
std::string broadcaster; std::string broadcaster;
std::vector<std::string> listeners; 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>; 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 deviceDestroy(xpc_connection_t client, xpc_object_t event);
void deviceUpdate(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 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 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 listeners(xpc_connection_t client, xpc_object_t event);
void listener(xpc_connection_t client, xpc_object_t event); void listener(xpc_connection_t client, xpc_object_t event);
void devices(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 description(xpc_connection_t client, xpc_object_t event);
void formats(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 broadcasting(xpc_connection_t client, xpc_object_t event);
void mirroring(xpc_connection_t client, xpc_object_t event); void controlsUpdated(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 listenerAdd(xpc_connection_t client, xpc_object_t event); void listenerAdd(xpc_connection_t client, xpc_object_t event);
void listenerRemove(xpc_connection_t client, xpc_object_t event); void listenerRemove(xpc_connection_t client, xpc_object_t event);
}; };
@ -143,6 +133,7 @@ AkVCam::AssistantPrivate::AssistantPrivate()
{ {
this->m_messageHandlers = { this->m_messageHandlers = {
{AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(AssistantPrivate::frameReady) }, {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_REQUEST_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::requestPort) },
{AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::addPort) }, {AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::addPort) },
{AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::removePort) }, {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_LISTENER , AKVCAM_BIND_FUNC(AssistantPrivate::listener) },
{AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::broadcasting) }, {AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::broadcasting) },
{AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::setBroadcasting)}, {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::setBroadcasting)},
{AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING , AKVCAM_BIND_FUNC(AssistantPrivate::mirroring) }, {AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(AssistantPrivate::controlsUpdated)},
{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) },
}; };
this->loadCameras(); this->loadCameras();
@ -500,113 +484,6 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
xpc_release(reply); 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, void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {
@ -634,6 +511,32 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
xpc_release(reply); 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, void AkVCam::AssistantPrivate::listeners(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {
@ -761,78 +664,24 @@ void AkVCam::AssistantPrivate::broadcasting(xpc_connection_t client,
xpc_release(reply); xpc_release(reply);
} }
void AkVCam::AssistantPrivate::mirroring(xpc_connection_t client, void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client, xpc_object_t event)
xpc_object_t event)
{ {
AkLogFunction(); AkLogFunction();
std::string deviceId = xpc_dictionary_get_string(event, "device"); std::string deviceId = xpc_dictionary_get_string(event, "device");
bool horizontalMirror = false; bool ok = false;
bool verticalMirror = false;
if (this->m_deviceConfigs.count(deviceId) > 0) { if (this->m_deviceConfigs.count(deviceId) > 0) {
horizontalMirror = this->m_deviceConfigs[deviceId].horizontalMirror; auto notification = xpc_copy(event);
verticalMirror = this->m_deviceConfigs[deviceId].verticalMirror;
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); auto reply = xpc_dictionary_create_reply(event);
xpc_dictionary_set_bool(reply, "hmirror", horizontalMirror); xpc_dictionary_set_bool(reply, "status", ok);
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_connection_send_message(client, reply); xpc_connection_send_message(client, reply);
xpc_release(reply); xpc_release(reply);
} }

View file

@ -28,6 +28,7 @@
// General messages // General messages
#define AKVCAM_ASSISTANT_MSG_ISALIVE 0x000 #define AKVCAM_ASSISTANT_MSG_ISALIVE 0x000
#define AKVCAM_ASSISTANT_MSG_FRAME_READY 0x001 #define AKVCAM_ASSISTANT_MSG_FRAME_READY 0x001
#define AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED 0x002
// Assistant messages // Assistant messages
#define AKVCAM_ASSISTANT_MSG_REQUEST_PORT 0x100 #define AKVCAM_ASSISTANT_MSG_REQUEST_PORT 0x100
@ -51,20 +52,12 @@
// Device dynamic properties // Device dynamic properties
#define AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING 0x400 #define AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING 0x400
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING 0x401 #define AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING 0x401
#define AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING 0x402 #define AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED 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
// Connections // Connections
#define AKVCAM_ASSISTANT_MSG_CONNECTIONS 0x500 #define AKVCAM_ASSISTANT_MSG_CONNECTIONS 0x500
#define AKVCAM_ASSISTANT_MSG_SETCONNECTIONS 0x501 #define AKVCAM_ASSISTANT_MSG_SETCONNECTIONS 0x501
namespace AkVCam namespace AkVCam
{ {
using XpcMessage = std::function<void (xpc_connection_t, xpc_object_t)>; using XpcMessage = std::function<void (xpc_connection_t, xpc_object_t)>;

View file

@ -17,6 +17,7 @@
* Web-Site: http://webcamoid.github.io/ * Web-Site: http://webcamoid.github.io/
*/ */
#include <cstring>
#include <CoreFoundation/CFRunLoop.h> #include <CoreFoundation/CFRunLoop.h>
#include <xpc/xpc.h> #include <xpc/xpc.h>
#include <xpc/connection.h> #include <xpc/connection.h>

View file

@ -42,7 +42,8 @@ TEMPLATE = lib
LIBS = \ LIBS = \
-L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \ -L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \
-framework CoreFoundation -framework CoreFoundation \
-framework CoreGraphics
SOURCES = \ SOURCES = \
src/preferences.cpp \ src/preferences.cpp \

View file

@ -17,6 +17,8 @@
* Web-Site: http://webcamoid.github.io/ * Web-Site: http://webcamoid.github.io/
*/ */
#include <algorithm>
#include "preferences.h" #include "preferences.h"
#include "utils.h" #include "utils.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
@ -609,3 +611,16 @@ void AkVCam::Preferences::setLogLevel(int logLevel)
{ {
write("loglevel", 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);
}

View file

@ -20,6 +20,7 @@
#ifndef PREFERENCES_H #ifndef PREFERENCES_H
#define PREFERENCES_H #define PREFERENCES_H
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
@ -79,6 +80,11 @@ namespace AkVCam
const VideoFormat &format, const VideoFormat &format,
int index); int index);
void cameraRemoveFormat(size_t cameraIndex, 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(); std::wstring picture();
void setPicture(const std::wstring &picture); void setPicture(const std::wstring &picture);
int logLevel(); int logLevel();

View file

@ -17,7 +17,97 @@
* Web-Site: http://webcamoid.github.io/ * 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 "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) 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)); 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;
}

View file

@ -20,19 +20,29 @@
#ifndef PLATFORM_UTILS_H #ifndef PLATFORM_UTILS_H
#define PLATFORM_UTILS_H #define PLATFORM_UTILS_H
#include <memory>
#include <string> #include <string>
#include <CoreFoundation/CoreFoundation.h> #include <CoreMediaIO/CMIOHardwarePlugIn.h>
#include <CoreMedia/CMFormatDescription.h>
#include "VCamUtils/src/image/videoformattypes.h"
namespace AkVCam 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::string &str);
std::shared_ptr<CFTypeRef> cfTypeFromStd(const std::wstring &str); std::shared_ptr<CFTypeRef> cfTypeFromStd(const std::wstring &str);
std::shared_ptr<CFTypeRef> cfTypeFromStd(int num); std::shared_ptr<CFTypeRef> cfTypeFromStd(int num);
std::shared_ptr<CFTypeRef> cfTypeFromStd(double num); std::shared_ptr<CFTypeRef> cfTypeFromStd(double num);
std::string stringFromCFType(CFTypeRef cfType); std::string stringFromCFType(CFTypeRef cfType);
std::wstring wstringFromCFType(CFTypeRef cfType); std::wstring wstringFromCFType(CFTypeRef cfType);
std::string realPath(const std::string &path);
VideoFrame loadPicture(const std::string &fileName);
} }
#endif // PLATFORM_UTILS_H #endif // PLATFORM_UTILS_H

View file

@ -17,12 +17,16 @@
* Web-Site: http://webcamoid.github.io/ * Web-Site: http://webcamoid.github.io/
*/ */
#include <algorithm>
#include <codecvt>
#include <fstream> #include <fstream>
#include <locale>
#include <map> #include <map>
#include <sstream> #include <sstream>
#include <algorithm>
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#include <dlfcn.h>
#include <libgen.h>
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
#include <IOSurface/IOSurface.h> #include <IOSurface/IOSurface.h>
#include <CoreMedia/CMFormatDescription.h> #include <CoreMedia/CMFormatDescription.h>
@ -31,6 +35,7 @@
#include "Assistant/src/assistantglobals.h" #include "Assistant/src/assistantglobals.h"
#include "PlatformUtils/src/preferences.h" #include "PlatformUtils/src/preferences.h"
#include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/image/videoframe.h"
#include "VCamUtils/src/ipcbridge.h" #include "VCamUtils/src/ipcbridge.h"
@ -61,6 +66,7 @@ namespace AkVCam
inline void add(IpcBridge *bridge); inline void add(IpcBridge *bridge);
void remove(IpcBridge *bridge); void remove(IpcBridge *bridge);
inline std::vector<IpcBridge *> &bridges(); inline std::vector<IpcBridge *> &bridges();
inline const std::vector<DeviceControl> &controls() const;
// Message handling methods // Message handling methods
void isAlive(xpc_connection_t client, xpc_object_t event); void isAlive(xpc_connection_t client, xpc_object_t event);
@ -68,12 +74,9 @@ namespace AkVCam
void deviceDestroy(xpc_connection_t client, xpc_object_t event); void deviceDestroy(xpc_connection_t client, xpc_object_t event);
void deviceUpdate(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 frameReady(xpc_connection_t client, xpc_object_t event);
void setBroadcasting(xpc_connection_t client, void pictureUpdated(xpc_connection_t client, xpc_object_t event);
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 controlsUpdated(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 listenerAdd(xpc_connection_t client, xpc_object_t event); void listenerAdd(xpc_connection_t client, xpc_object_t event);
void listenerRemove(xpc_connection_t client, xpc_object_t event); void listenerRemove(xpc_connection_t client, xpc_object_t event);
void messageReceived(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; std::string homePath() const;
bool fileExists(const std::wstring &path) const; bool fileExists(const std::wstring &path) const;
bool fileExists(const std::string &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 mkpath(const std::string &path) const;
bool rm(const std::string &path) const; bool rm(const std::string &path) const;
std::wstring locateDriverPath() const; static std::string locatePluginPath();
private: private:
std::vector<IpcBridge *> m_bridges; std::vector<IpcBridge *> m_bridges;
@ -169,7 +171,18 @@ std::wstring AkVCam::IpcBridge::picture() const
void AkVCam::IpcBridge::setPicture(const std::wstring &picture) void AkVCam::IpcBridge::setPicture(const std::wstring &picture)
{ {
AkLogFunction();
Preferences::setPicture(picture); 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 int AkVCam::IpcBridge::logLevel() const
@ -446,139 +459,62 @@ std::string AkVCam::IpcBridge::broadcaster(const std::string &deviceId) const
return broadcaster; return broadcaster;
} }
bool AkVCam::IpcBridge::isHorizontalMirrored(const std::string &deviceId) std::vector<AkVCam::DeviceControl> AkVCam::IpcBridge::controls(const std::string &deviceId)
{ {
AkLogFunction(); AkLogFunction();
auto cameraIndex = Preferences::cameraFromPath(deviceId);
if (!this->d->m_serverMessagePort) if (cameraIndex < 0)
return false; 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); 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()); xpc_dictionary_set_string(dictionary, "device", deviceId.c_str());
auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort,
dictionary); dictionary);
xpc_release(dictionary); xpc_release(dictionary);
auto replyType = xpc_get_type(reply);
if (replyType != XPC_TYPE_DICTIONARY) {
xpc_release(reply); 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) 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 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()) if (driverPath.empty())
return {}; return {};
auto plugin = this->d->fileName(driverPath); auto path = driverPath + "/Contents/MacOS/" CMIO_PLUGIN_NAME;
std::wstring pluginPath = AkLogDebug() << "Plugin binary: " << path << std::endl;
CMIO_PLUGINS_DAL_PATH_L L"/"
+ plugin if (!this->d->fileExists(path))
+ L"/Contents/MacOS/" CMIO_PLUGIN_NAME_L; return {};
std::string path(pluginPath.begin(), pluginPath.end());
auto npids = proc_listpidspath(PROC_ALL_PIDS, auto npids = proc_listpidspath(PROC_ALL_PIDS,
0, 0,
path.c_str(), path.c_str(),
@ -692,7 +630,7 @@ void AkVCam::IpcBridge::removeFormat(const std::string &deviceId, int index)
index); index);
} }
void AkVCam::IpcBridge::update() void AkVCam::IpcBridge::updateDevices()
{ {
AkLogFunction(); AkLogFunction();
@ -705,19 +643,6 @@ void AkVCam::IpcBridge::update()
AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE); AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE);
xpc_connection_send_message(this->d->m_serverMessagePort, dictionary); xpc_connection_send_message(this->d->m_serverMessagePort, dictionary);
xpc_release(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; 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) bool AkVCam::IpcBridge::addListener(const std::string &deviceId)
{ {
AkLogFunction(); AkLogFunction();
@ -993,16 +845,14 @@ AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
this->m_messageHandlers = { this->m_messageHandlers = {
{AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) }, {AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) },
{AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) }, {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) },
{AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED , AKVCAM_BIND_FUNC(IpcBridgePrivate::pictureUpdated) },
{AKVCAM_ASSISTANT_MSG_DEVICE_CREATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceCreate) }, {AKVCAM_ASSISTANT_MSG_DEVICE_CREATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceCreate) },
{AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceDestroy) }, {AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceDestroy) },
{AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceUpdate) }, {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_ADD , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerAdd) },
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerRemove) }, {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_SETBROADCASTING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setBroadcasting)},
{AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setMirror) }, {AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(IpcBridgePrivate::controlsUpdated)},
{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) },
}; };
} }
@ -1038,6 +888,31 @@ std::vector<AkVCam::IpcBridge *> &AkVCam::IpcBridgePrivate::bridges()
return this->m_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, void AkVCam::IpcBridgePrivate::isAlive(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {
@ -1118,6 +993,18 @@ void AkVCam::IpcBridgePrivate::frameReady(xpc_connection_t client,
xpc_release(reply); 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, void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client,
xpc_object_t event) xpc_object_t event)
{ {
@ -1133,7 +1020,7 @@ void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client,
AKVCAM_EMIT(bridge, BroadcastingChanged, deviceId, broadcaster) 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) xpc_object_t event)
{ {
UNUSED(client); UNUSED(client);
@ -1141,61 +1028,23 @@ void AkVCam::IpcBridgePrivate::setMirror(xpc_connection_t client,
std::string deviceId = std::string deviceId =
xpc_dictionary_get_string(event, "device"); xpc_dictionary_get_string(event, "device");
bool horizontalMirror = auto cameraIndex = Preferences::cameraFromPath(deviceId);
xpc_dictionary_get_bool(event, "hmirror"); std::map<std::string, int> controls;
bool verticalMirror =
xpc_dictionary_get_bool(event, "vmirror"); for (auto &control: this->controls())
controls[control.id] =
Preferences::cameraControlValue(cameraIndex, control.id);
for (auto bridge: this->m_bridges) for (auto bridge: this->m_bridges)
AKVCAM_EMIT(bridge, AKVCAM_EMIT(bridge,
MirrorChanged, ControlsChanged,
deviceId, deviceId,
horizontalMirror, controls)
verticalMirror)
}
void AkVCam::IpcBridgePrivate::setScaling(xpc_connection_t client, auto reply = xpc_dictionary_create_reply(event);
xpc_object_t event) xpc_dictionary_set_bool(reply, "status", cameraIndex >= 0);
{ xpc_connection_send_message(client, reply);
UNUSED(client); xpc_release(reply);
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)
} }
void AkVCam::IpcBridgePrivate::listenerAdd(xpc_connection_t client, 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; 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 bool AkVCam::IpcBridgePrivate::mkpath(const std::string &path) const
{ {
if (path.empty()) if (path.empty())
@ -1341,32 +1185,14 @@ bool AkVCam::IpcBridgePrivate::rm(const std::string &path) const
return ok; return ok;
} }
std::wstring AkVCam::IpcBridgePrivate::locateDriverPath() const std::string AkVCam::IpcBridgePrivate::locatePluginPath()
{ {
AkLogFunction(); 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(); return realPath(dirName + "/../..");
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;
} }

View file

@ -44,6 +44,7 @@ LIBS = \
-L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \ -L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \
-L$${OUT_PWD}/../VCamIPC/$${BIN_DIR} -lVCamIPC \ -L$${OUT_PWD}/../VCamIPC/$${BIN_DIR} -lVCamIPC \
-framework CoreFoundation \ -framework CoreFoundation \
-framework CoreGraphics \
-framework CoreMedia \ -framework CoreMedia \
-framework CoreMediaIO \ -framework CoreMediaIO \
-framework CoreVideo \ -framework CoreVideo \
@ -57,7 +58,6 @@ TEMPLATE = lib
HEADERS += \ HEADERS += \
src/plugin.h \ src/plugin.h \
src/plugininterface.h \ src/plugininterface.h \
src/utils.h \
src/device.h \ src/device.h \
src/object.h \ src/object.h \
src/stream.h \ src/stream.h \
@ -69,7 +69,6 @@ HEADERS += \
SOURCES += \ SOURCES += \
src/plugin.cpp \ src/plugin.cpp \
src/plugininterface.cpp \ src/plugininterface.cpp \
src/utils.cpp \
src/device.cpp \ src/device.cpp \
src/object.cpp \ src/object.cpp \
src/stream.cpp \ src/stream.cpp \
@ -88,8 +87,7 @@ RESOURCESPATH = $${CONTENTSPATH}/Resources
INSTALLS += \ INSTALLS += \
targetLib \ targetLib \
infoPlist \ infoPlist
resources
targetLib.files = $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME}) targetLib.files = $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME})
targetLib.path = $${PREFIX}/$${MACBINPATH} targetLib.path = $${PREFIX}/$${MACBINPATH}
@ -99,12 +97,7 @@ infoPlist.files = $$shell_path($${OUT_PWD}/Info.plist)
infoPlist.path = $${PREFIX}/$${CONTENTSPATH} infoPlist.path = $${PREFIX}/$${CONTENTSPATH}
infoPlist.CONFIG += no_check_exist infoPlist.CONFIG += no_check_exist
resources.files = ../../share/TestFrame/TestFrame.bmp
resources.path = $${PREFIX}/$${RESOURCESPATH}
QMAKE_POST_LINK = \ QMAKE_POST_LINK = \
$$sprintf($$QMAKE_MKDIR_CMD, $$shell_path($${OUT_PWD}/../../$${MACBINPATH})) $${CMD_SEP} \ $$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}/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($${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})

View file

@ -18,7 +18,7 @@
*/ */
#include "device.h" #include "device.h"
#include "utils.h" #include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/logger/logger.h" #include "VCamUtils/src/logger/logger.h"
AkVCam::Device::Device(CMIOHardwarePlugInRef pluginInterface, AkVCam::Device::Device(CMIOHardwarePlugInRef pluginInterface,
@ -194,16 +194,28 @@ void AkVCam::Device::frameReady(const AkVCam::VideoFrame &frame)
stream.second->frameReady(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) void AkVCam::Device::setBroadcasting(const std::string &broadcaster)
{ {
for (auto &stream: this->m_streams) for (auto &stream: this->m_streams)
stream.second->setBroadcasting(broadcaster); stream.second->setBroadcasting(broadcaster);
} }
void AkVCam::Device::setMirror(bool horizontalMirror, bool verticalMirror) void AkVCam::Device::setHorizontalMirror(bool horizontalMirror)
{ {
for (auto &stream: this->m_streams) 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) void AkVCam::Device::setScaling(Scaling scaling)

View file

@ -52,8 +52,10 @@ namespace AkVCam
void serverStateChanged(IpcBridge::ServerState state); void serverStateChanged(IpcBridge::ServerState state);
void frameReady(const VideoFrame &frame); void frameReady(const VideoFrame &frame);
void setPicture(const std::string &picture);
void setBroadcasting(const std::string &broadcaster); 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 setScaling(Scaling scaling);
void setAspectRatio(AspectRatio aspectRatio); void setAspectRatio(AspectRatio aspectRatio);
void setSwapRgb(bool swap); void setSwapRgb(bool swap);

View file

@ -18,7 +18,7 @@
*/ */
#include "object.h" #include "object.h"
#include "utils.h" #include "PlatformUtils/src/utils.h"
AkVCam::Object::Object(CMIOHardwarePlugInRef pluginInterface, AkVCam::Object::Object(CMIOHardwarePlugInRef pluginInterface,
Object *parent): Object *parent):

View file

@ -18,7 +18,7 @@
*/ */
#include "objectinterface.h" #include "objectinterface.h"
#include "utils.h" #include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/logger/logger.h" #include "VCamUtils/src/logger/logger.h"
AkVCam::ObjectInterface::ObjectInterface(): AkVCam::ObjectInterface::ObjectInterface():

View file

@ -20,7 +20,7 @@
#include <map> #include <map>
#include "object.h" #include "object.h"
#include "utils.h" #include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
namespace AkVCam namespace AkVCam

View file

@ -18,8 +18,8 @@
*/ */
#include "plugin.h" #include "plugin.h"
#include "utils.h"
#include "PlatformUtils/src/preferences.h" #include "PlatformUtils/src/preferences.h"
#include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/ipcbridge.h" #include "VCamUtils/src/ipcbridge.h"
#include "VCamUtils/src/logger/logger.h" #include "VCamUtils/src/logger/logger.h"

View file

@ -23,9 +23,9 @@
#include <IOKit/audio/IOAudioTypes.h> #include <IOKit/audio/IOAudioTypes.h>
#include "plugininterface.h" #include "plugininterface.h"
#include "utils.h"
#include "Assistant/src/assistantglobals.h" #include "Assistant/src/assistantglobals.h"
#include "PlatformUtils/src/preferences.h" #include "PlatformUtils/src/preferences.h"
#include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/ipcbridge.h" #include "VCamUtils/src/ipcbridge.h"
#include "VCamUtils/src/logger/logger.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.connectDeviceRemoved(this, &PluginInterface::deviceRemoved);
this->d->m_ipcBridge.connectDevicesUpdated(this, &PluginInterface::devicesUpdated); this->d->m_ipcBridge.connectDevicesUpdated(this, &PluginInterface::devicesUpdated);
this->d->m_ipcBridge.connectFrameReady(this, &PluginInterface::frameReady); 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.connectBroadcastingChanged(this, &PluginInterface::setBroadcasting);
this->d->m_ipcBridge.connectMirrorChanged(this, &PluginInterface::setMirror); this->d->m_ipcBridge.connectControlsChanged(this, &PluginInterface::controlsChanged);
this->d->m_ipcBridge.connectScalingChanged(this, &PluginInterface::setScaling);
this->d->m_ipcBridge.connectAspectRatioChanged(this, &PluginInterface::setAspectRatio);
this->d->m_ipcBridge.connectSwapRgbChanged(this, &PluginInterface::setSwapRgb);
} }
AkVCam::PluginInterface::~PluginInterface() AkVCam::PluginInterface::~PluginInterface()
@ -318,6 +316,16 @@ void AkVCam::PluginInterface::frameReady(void *userData,
device->frameReady(frame); 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, void AkVCam::PluginInterface::setBroadcasting(void *userData,
const std::string &deviceId, const std::string &deviceId,
const std::string &broadcaster) const std::string &broadcaster)
@ -332,53 +340,31 @@ void AkVCam::PluginInterface::setBroadcasting(void *userData,
device->setBroadcasting(broadcaster); device->setBroadcasting(broadcaster);
} }
void AkVCam::PluginInterface::setMirror(void *userData, void AkVCam::PluginInterface::controlsChanged(void *userData,
const std::string &deviceId, const std::string &deviceId,
bool horizontalMirror, const std::map<std::string, int> &controls)
bool verticalMirror)
{ {
AkLogFunction(); AkLogFunction();
AkLogInfo() << "Device: " << deviceId << std::endl;
auto self = reinterpret_cast<PluginInterface *>(userData); auto self = reinterpret_cast<PluginInterface *>(userData);
for (auto device: self->m_devices) for (auto device: self->m_devices)
if (device->deviceId() == deviceId) if (device->deviceId() == deviceId) {
device->setMirror(horizontalMirror, verticalMirror); 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, void AkVCam::PluginInterface::addListener(void *userData,
@ -412,6 +398,13 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
device->connectRemoveListener(this, &PluginInterface::removeListener); device->connectRemoveListener(this, &PluginInterface::removeListener);
this->m_devices.push_back(device); 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. // Define device properties.
device->properties().setProperty(kCMIOObjectPropertyName, device->properties().setProperty(kCMIOObjectPropertyName,
description); description);
@ -469,11 +462,11 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
} }
device->setBroadcasting(this->d->m_ipcBridge.broadcaster(deviceId)); device->setBroadcasting(this->d->m_ipcBridge.broadcaster(deviceId));
device->setMirror(this->d->m_ipcBridge.isHorizontalMirrored(deviceId), device->setHorizontalMirror(hflip);
this->d->m_ipcBridge.isVerticalMirrored(deviceId)); device->setVerticalMirror(vflip);
device->setScaling(this->d->m_ipcBridge.scalingMode(deviceId)); device->setScaling(Scaling(scaling));
device->setAspectRatio(this->d->m_ipcBridge.aspectRatioMode(deviceId)); device->setAspectRatio(AspectRatio(aspectRatio));
device->setSwapRgb(this->d->m_ipcBridge.swapRgb(deviceId)); device->setSwapRgb(swapRgb);
return true; return true;
@ -509,11 +502,17 @@ void AkVCam::PluginInterfacePrivate::updateDevices()
{ {
for (auto &device: this->self->m_devices) { for (auto &device: this->self->m_devices) {
device->setBroadcasting(this->m_ipcBridge.broadcaster(device->deviceId())); device->setBroadcasting(this->m_ipcBridge.broadcaster(device->deviceId()));
device->setMirror(this->m_ipcBridge.isHorizontalMirrored(device->deviceId()), auto cameraIndex = Preferences::cameraFromPath(device->deviceId());
this->m_ipcBridge.isVerticalMirrored(device->deviceId())); auto hflip = Preferences::cameraControlValue(cameraIndex, "hflip");
device->setScaling(this->m_ipcBridge.scalingMode(device->deviceId())); auto vflip = Preferences::cameraControlValue(cameraIndex, "vflip");
device->setAspectRatio(this->m_ipcBridge.aspectRatioMode(device->deviceId())); auto scaling = Preferences::cameraControlValue(cameraIndex, "scaling");
device->setSwapRgb(this->m_ipcBridge.swapRgb(device->deviceId())); 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);
} }
} }

View file

@ -58,22 +58,14 @@ namespace AkVCam
static void frameReady(void *userData, static void frameReady(void *userData,
const std::string &deviceId, const std::string &deviceId,
const VideoFrame &frame); const VideoFrame &frame);
static void pictureChanged(void *userData,
const std::string &picture);
static void setBroadcasting(void *userData, static void setBroadcasting(void *userData,
const std::string &deviceId, const std::string &deviceId,
const std::string &broadcaster); const std::string &broadcaster);
static void setMirror(void *userData, static void controlsChanged(void *userData,
const std::string &deviceId, const std::string &deviceId,
bool horizontalMirror, const std::map<std::string, int> &controls);
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);
static void addListener(void *userData, static void addListener(void *userData,
const std::string &deviceId); const std::string &deviceId);
static void removeListener(void *userData, static void removeListener(void *userData,

View file

@ -17,15 +17,18 @@
* Web-Site: http://webcamoid.github.io/ * Web-Site: http://webcamoid.github.io/
*/ */
#include <algorithm>
#include <codecvt> #include <codecvt>
#include <thread> #include <locale>
#include <mutex>
#include <random> #include <random>
#include <thread>
#include <CoreMediaIO/CMIOSampleBuffer.h> #include <CoreMediaIO/CMIOSampleBuffer.h>
#include "stream.h" #include "stream.h"
#include "clock.h" #include "clock.h"
#include "utils.h"
#include "PlatformUtils/src/preferences.h" #include "PlatformUtils/src/preferences.h"
#include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/image/videoframe.h"
#include "VCamUtils/src/logger/logger.h" #include "VCamUtils/src/logger/logger.h"
@ -78,7 +81,7 @@ AkVCam::Stream::Stream(bool registerObject,
if (!picture.empty()) { if (!picture.empty()) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv; 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 = this->d->m_clock =
@ -158,6 +161,20 @@ OSStatus AkVCam::Stream::registerObject(bool regist)
return status; 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) void AkVCam::Stream::setBridge(IpcBridge *bridge)
{ {
this->d->m_bridge = bridge; this->d->m_bridge = bridge;
@ -307,15 +324,24 @@ void AkVCam::Stream::setBroadcasting(const std::string &broadcaster)
this->d->m_mutex.unlock(); this->d->m_mutex.unlock();
} }
void AkVCam::Stream::setMirror(bool horizontalMirror, bool verticalMirror) void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror)
{ {
AkLogFunction(); AkLogFunction();
if (this->d->m_horizontalMirror == horizontalMirror if (this->d->m_horizontalMirror == horizontalMirror)
&& this->d->m_verticalMirror == verticalMirror)
return; return;
this->d->m_horizontalMirror = horizontalMirror; 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->m_verticalMirror = verticalMirror;
this->d->updateTestFrame(); this->d->updateTestFrame();
} }

View file

@ -54,8 +54,10 @@ namespace AkVCam
void serverStateChanged(IpcBridge::ServerState state); void serverStateChanged(IpcBridge::ServerState state);
void frameReady(const VideoFrame &frame); void frameReady(const VideoFrame &frame);
void setPicture(const std::string &picture);
void setBroadcasting(const std::string &broadcaster); 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 setScaling(Scaling scaling);
void setAspectRatio(AspectRatio aspectRatio); void setAspectRatio(AspectRatio aspectRatio);
void setSwapRgb(bool swap); void setSwapRgb(bool swap);

View file

@ -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);
}

View file

@ -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