diff --git a/Manager/src/cmdparser.cpp b/Manager/src/cmdparser.cpp index a29b770..9272f81 100644 --- a/Manager/src/cmdparser.cpp +++ b/Manager/src/cmdparser.cpp @@ -21,10 +21,12 @@ #include #include #include +#include #include #include "cmdparser.h" #include "VCamUtils/src/ipcbridge.h" +#include "VCamUtils/src/settings.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/logger/logger.h" @@ -33,6 +35,9 @@ std::bind(&member, this->d, std::placeholders::_1, std::placeholders::_2) namespace AkVCam { + using StringMatrix = std::vector; + using VideoFormatMatrix = std::vector>; + struct CmdParserFlags { StringVector flags; @@ -56,6 +61,7 @@ namespace AkVCam { IpcBridge m_ipcBridge; bool m_parseable {false}; + static const std::map &typeStrMap(); std::string basename(const std::string &path); void printFlags(const std::vector &cmdFlags, size_t indent); @@ -63,8 +69,13 @@ namespace AkVCam { size_t maxArgumentsLength(); size_t maxFlagsLength(const std::vector &flags); size_t maxFlagsValueLength(const std::vector &flags); - size_t maxStringLength(const StringVector &strings); - size_t maxStringLength(const WStringVector &strings); + size_t maxColumnLength(const StringVector &table, + size_t width, + size_t column); + std::vector maxColumnsLength(const StringVector &table, + size_t width); + void drawTableHLine(const std::vector &columnsLength); + void drawTable(const StringVector &table, size_t width); CmdParserCommand *parserCommand(const std::string &command); const CmdParserFlags *parserFlag(const std::vector &cmdFlags, const std::string &flag); @@ -96,12 +107,26 @@ namespace AkVCam { int stream(const StringMap &flags, const StringVector &args); int showControls(const StringMap &flags, const StringVector &args); int readControl(const StringMap &flags, const StringVector &args); - int writeControl(const StringMap &flags, const StringVector &args); + int writeControls(const StringMap &flags, const StringVector &args); int picture(const StringMap &flags, const StringVector &args); int setPicture(const StringMap &flags, const StringVector &args); int logLevel(const StringMap &flags, const StringVector &args); int setLogLevel(const StringMap &flags, const StringVector &args); int showClients(const StringMap &flags, const StringVector &args); + void loadGenerals(Settings &settings); + VideoFormatMatrix readFormats(Settings &settings); + std::vector 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 readDeviceFormats(Settings &settings, + const VideoFormatMatrix &availableFormats); }; std::string operator *(const std::string &str, size_t n); @@ -192,10 +217,31 @@ AkVCam::CmdParser::CmdParser() "DEVICE CONTROL", "Read device control.", AKVCAM_BIND_FUNC(CmdParserPrivate::readControl)); - this->addCommand("set-control", - "DEVICE CONTROL VALUE", - "Write device control value.", - AKVCAM_BIND_FUNC(CmdParserPrivate::writeControl)); + this->addFlags("get-control", + {"-c", "--description"}, + "Show control description."); + this->addFlags("get-control", + {"-t", "--type"}, + "Show control type."); + this->addFlags("get-control", + {"-m", "--min"}, + "Show minimum value for the control."); + this->addFlags("get-control", + {"-M", "--max"}, + "Show maximum value for the control."); + this->addFlags("get-control", + {"-s", "--step"}, + "Show increment/decrement step for the control."); + this->addFlags("get-control", + {"-d", "--default"}, + "Show default value for the control."); + this->addFlags("get-control", + {"-l", "--menu"}, + "Show options of a memu type control."); + this->addCommand("set-controls", + "DEVICE CONTROL_1=VALUE CONTROL_2=VALUE...", + "Write device controls values.", + AKVCAM_BIND_FUNC(CmdParserPrivate::writeControls)); this->addCommand("picture", "", "Placeholder picture to show when no streaming.", @@ -352,6 +398,17 @@ void AkVCam::CmdParser::addFlags(const std::string &command, this->addFlags(command, flags, "", helpString); } +const std::map &AkVCam::CmdParserPrivate::typeStrMap() +{ + static const std::map typeStr { + {ControlTypeInteger, "Integer"}, + {ControlTypeBoolean, "Boolean"}, + {ControlTypeMenu , "Menu" }, + }; + + return typeStr; +} + std::string AkVCam::CmdParserPrivate::basename(const std::string &path) { auto rit = @@ -393,10 +450,8 @@ void AkVCam::CmdParserPrivate::printFlags(const std::vector &cmd std::cout << std::string(spaces.data(), indent) << fill(allFlags, maxFlagsLen); - if (maxFlagsValueLen > 0) { - std::cout << " " - << fill(flag.value, maxFlagsValueLen); - } + if (maxFlagsValueLen > 0) + std::cout << " " << fill(flag.value, maxFlagsValueLen); std::cout << " " << flag.helpString @@ -444,24 +499,64 @@ size_t AkVCam::CmdParserPrivate::maxFlagsValueLength(const std::vector AkVCam::CmdParserPrivate::maxColumnsLength(const AkVCam::StringVector &table, + size_t width) { - size_t length = 0; + std::vector lengths; - for (auto &str: strings) - length = std::max(str.size(), length); + for (size_t x = 0; x < width; x++) + lengths.push_back(this->maxColumnLength(table, width, x)); - return length; + return lengths; +} + +void AkVCam::CmdParserPrivate::drawTableHLine(const std::vector &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) @@ -556,8 +651,8 @@ int AkVCam::CmdParserPrivate::showHelp(const StringMap &flags, UNUSED(flags); std::cout << args[0] - << " [OPTIONS...] COMMAND [COMMAND_OPTIONS...] ..." - << std::endl; + << " [OPTIONS...] COMMAND [COMMAND_OPTIONS...] ..." + << std::endl; std::cout << std::endl; std::cout << "AkVirtualCamera virtual device manager." << std::endl; std::cout << std::endl; @@ -609,36 +704,21 @@ int AkVCam::CmdParserPrivate::showDevices(const StringMap &flags, for (auto &device: devices) std::cout << device << std::endl; } else { - StringVector devicesColumn; - WStringVector descriptionsColumn; - - devicesColumn.push_back("Device"); - descriptionsColumn.push_back(L"Description"); + std::vector table { + "Device", + "Description" + }; + auto columns = table.size(); + std::wstring_convert> cv; for (auto &device: devices) { - devicesColumn.push_back(device); - descriptionsColumn.push_back(this->m_ipcBridge.description(device)); + table.push_back(device); + auto description = + cv.to_bytes(this->m_ipcBridge.description(device)); + table.push_back(description); } - auto devicesColumnSize = this->maxStringLength(devicesColumn); - auto descriptionsColumnSize = this->maxStringLength(descriptionsColumn); - - std::cout << fill("Device", devicesColumnSize) - << " | " - << fill("Description", descriptionsColumnSize) - << std::endl; - std::cout << std::string("-") - * (devicesColumnSize - + descriptionsColumnSize - + 4) << std::endl; - - for (auto &device: devices) { - std::cout << fill(device, devicesColumnSize) - << " | "; - std::wcout << fill(this->m_ipcBridge.description(device), - descriptionsColumnSize) - << std::endl; - } + this->drawTable(table, columns); } return 0; @@ -1003,7 +1083,7 @@ int AkVCam::CmdParserPrivate::update(const StringMap &flags, { UNUSED(flags); UNUSED(args); - this->m_ipcBridge.update(); + this->m_ipcBridge.updateDevices(); return 0; } @@ -1011,6 +1091,30 @@ int AkVCam::CmdParserPrivate::update(const StringMap &flags, int AkVCam::CmdParserPrivate::loadSettings(const AkVCam::StringMap &flags, const AkVCam::StringVector &args) { + UNUSED(flags); + + if (args.size() < 2) { + std::cerr << "Settings file not provided." << std::endl; + + return -1; + } + + Settings settings; + + if (!settings.load(args[1])) { + std::cerr << "Settings file not valid." << std::endl; + + return -1; + } + + this->loadGenerals(settings); + auto devices = this->m_ipcBridge.devices(); + + for (auto &device: devices) + this->m_ipcBridge.removeDevice(device); + + this->createDevices(settings, this->readFormats(settings)); + return 0; } @@ -1106,20 +1210,281 @@ int AkVCam::CmdParserPrivate::stream(const AkVCam::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 table { + "Control", + "Description", + "Type", + "Minimum", + "Maximum", + "Step", + "Default", + "Value" + }; + auto columns = table.size(); + + for (auto &control: this->m_ipcBridge.controls(deviceId)) { + table.push_back(control.id); + table.push_back(control.description); + table.push_back(typeStr[control.type]); + table.push_back(std::to_string(control.minimum)); + table.push_back(std::to_string(control.maximum)); + table.push_back(std::to_string(control.step)); + table.push_back(std::to_string(control.defaultValue)); + table.push_back(std::to_string(control.value)); + } + + this->drawTable(table, columns); + } + return 0; } int AkVCam::CmdParserPrivate::readControl(const StringMap &flags, - const StringVector &args) -{ - return 0; -} - -int AkVCam::CmdParserPrivate::writeControl(const StringMap &flags, const StringVector &args) { + UNUSED(flags); + + if (args.size() < 3) { + std::cerr << "Not enough arguments." << std::endl; + + return -1; + } + + auto deviceId = args[1]; + auto devices = this->m_ipcBridge.devices(); + auto dit = std::find(devices.begin(), devices.end(), deviceId); + + if (dit == devices.end()) { + std::cerr << "'" << deviceId << "' doesn't exists." << std::endl; + + return -1; + } + + for (auto &control: this->m_ipcBridge.controls(deviceId)) + if (control.description == args[2]) { + if (flags.empty()) { + std::cout << control.value << std::endl; + } else { + if (this->containsFlag(flags, "get-control", "-c")) { + auto typeStr = typeStrMap(); + std::cout << control.description << std::endl; + } + if (this->containsFlag(flags, "get-control", "-t")) { + auto typeStr = typeStrMap(); + std::cout << typeStr[control.type] << std::endl; + } + + if (this->containsFlag(flags, "get-control", "-m")) { + std::cout << control.minimum << std::endl; + } + + if (this->containsFlag(flags, "get-control", "-M")) { + std::cout << control.maximum << std::endl; + } + + if (this->containsFlag(flags, "get-control", "-s")) { + std::cout << control.step << std::endl; + } + + if (this->containsFlag(flags, "get-control", "-d")) { + std::cout << control.defaultValue << std::endl; + } + + if (this->containsFlag(flags, "get-control", "-l")) { + for (size_t i = 0; i< control.menu.size(); i++) + if (this->m_parseable) + std::cout << control.menu[i] << std::endl; + else + std::cout << i << ": " << control.menu[i] << std::endl; + } + } + + return 0; + } + + std::cerr << "'" << args[2] << "' control not available." << std::endl; + + return -1; +} + +int AkVCam::CmdParserPrivate::writeControls(const StringMap &flags, + const StringVector &args) +{ + UNUSED(flags); + + if (args.size() < 3) { + std::cerr << "Not enough arguments." << std::endl; + + return -1; + } + + auto deviceId = args[1]; + auto devices = this->m_ipcBridge.devices(); + auto dit = std::find(devices.begin(), devices.end(), deviceId); + + if (dit == devices.end()) { + std::cerr << "'" << deviceId << "' doesn't exists." << std::endl; + + return -1; + } + + std::map controls; + + for (size_t i = 2; i < args.size(); i++) { + if (args[i].find('=') == std::string::npos) { + std::cerr << "Argumment " + << i + << " is not in the form KEY=VALUE." + << std::endl; + + return -1; + } + + auto pair = splitOnce(args[i], "="); + + if (pair.first.empty()) { + std::cerr << "Key for argumment " + << i + << " is emty." + << std::endl; + + return -1; + } + + auto key = trimmed(pair.first); + auto value = trimmed(pair.second); + bool found = false; + + for (auto &control: this->m_ipcBridge.controls(deviceId)) + if (control.id == key) { + switch (control.type) { + case ControlTypeInteger: { + char *p = nullptr; + auto val = strtol(value.c_str(), &p, 10); + + if (*p) { + std::cerr << "Value at argument " + << i + << " must be an integer." + << std::endl; + + return -1; + } + + controls[key] = val; + + break; + } + + case ControlTypeBoolean: { + std::locale loc; + std::transform(value.begin(), + value.end(), + value.begin(), + [&loc](char c) { + return std::tolower(c, loc); + }); + + if (value == "0" || value == "false") { + controls[key] = 0; + } else if (value == "1" || value == "true") { + controls[key] = 1; + } else { + std::cerr << "Value at argument " + << i + << " must be a boolean." + << std::endl; + + return -1; + } + + break; + } + + case ControlTypeMenu: { + char *p = nullptr; + auto val = strtoul(value.c_str(), &p, 10); + + if (*p) { + auto it = std::find(control.menu.begin(), + control.menu.end(), + value); + + if (it == control.menu.end()) { + std::cerr << "Value at argument " + << i + << " is not valid." + << std::endl; + + return -1; + } + + controls[key] = it - control.menu.begin(); + } else { + if (val >= control.menu.size()) { + std::cerr << "Value at argument " + << i + << " is out of range." + << std::endl; + + return -1; + } + + controls[key] = val; + } + + break; + } + + default: + break; + } + + found = true; + + break; + } + + if (!found) { + std::cerr << "No such '" + << key + << "' control in argument " + << i + << "." + << std::endl; + + return -1; + } + } + + this->m_ipcBridge.setControls(deviceId, controls); + return 0; } @@ -1195,7 +1560,6 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags, { UNUSED(flags); UNUSED(args); - auto clients = this->m_ipcBridge.clientsPids(); if (clients.empty()) @@ -1208,38 +1572,217 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags, << this->m_ipcBridge.clientExe(pid) << std::endl; } else { - StringVector pidsColumn; - StringVector exesColumn; - - pidsColumn.push_back("Pid"); - exesColumn.push_back("Executable"); + std::vector table { + "Pid", + "Executable" + }; + auto columns = table.size(); for (auto &pid: clients) { - pidsColumn.push_back(std::to_string(pid)); - exesColumn.push_back(this->m_ipcBridge.clientExe(pid)); + table.push_back(std::to_string(pid)); + table.push_back(this->m_ipcBridge.clientExe(pid)); } - auto pidsColumnSize = this->maxStringLength(pidsColumn); - auto exesColumnSize = this->maxStringLength(exesColumn); - - std::cout << fill("Pid", pidsColumnSize) - << " | " - << fill("Executable", exesColumnSize) - << std::endl; - std::cout << std::string("-") - * (pidsColumnSize + exesColumnSize + 4) - << std::endl; - - for (auto &pid: clients) - std::cout << fill(std::to_string(pid), pidsColumnSize) - << " | " - << fill(this->m_ipcBridge.clientExe(pid), exesColumnSize) - << std::endl; + this->drawTable(table, columns); } return 0; } +void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings) +{ + settings.beginGroup("General"); + + if (settings.contains("default_frame")) { + auto defaultFrame = settings.value("default_frame"); + std::wstring_convert> 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::CmdParserPrivate::readFormat(Settings &settings) +{ + std::vector 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> 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::CmdParserPrivate::readDeviceFormats(Settings &settings, + const VideoFormatMatrix &availableFormats) +{ + std::vector formats; + auto formatsIndex = settings.valueList("formats", ","); + + for (auto &indexStr: formatsIndex) { + char *p = nullptr; + auto index = strtoul(indexStr.c_str(), &p, 10); + + if (*p) + continue; + + index--; + + if (index >= availableFormats.size()) + continue; + + for (auto &format: availableFormats[index]) + formats.push_back(format); + } + + return formats; +} + std::string AkVCam::operator *(const std::string &str, size_t n) { std::stringstream ss; diff --git a/Manager/src/cmdparser.h b/Manager/src/cmdparser.h index f2ec38d..9ad2ae2 100644 --- a/Manager/src/cmdparser.h +++ b/Manager/src/cmdparser.h @@ -20,8 +20,9 @@ #ifndef CMDPARSER_H #define CMDPARSER_H -#include +#include #include +#include #include namespace AkVCam { diff --git a/VCamUtils/src/ipcbridge.h b/VCamUtils/src/ipcbridge.h index db83bf0..f0aa1f7 100644 --- a/VCamUtils/src/ipcbridge.h +++ b/VCamUtils/src/ipcbridge.h @@ -33,6 +33,26 @@ namespace AkVCam class VideoFormat; class VideoFrame; + enum ControlType + { + ControlTypeInteger, + ControlTypeBoolean, + ControlTypeMenu, + }; + + struct DeviceControl + { + std::string id; + std::string description; + ControlType type; + int minimum; + int maximum; + int step; + int defaultValue; + int value; + std::vector menu; + }; + class IpcBridge { public: @@ -53,6 +73,8 @@ namespace AkVCam AKVCAM_SIGNAL(FrameReady, const std::string &deviceId, const VideoFrame &frame) + AKVCAM_SIGNAL(PictureChanged, + const std::string &picture) AKVCAM_SIGNAL(DeviceAdded, const std::string &deviceId) AKVCAM_SIGNAL(DeviceRemoved, @@ -67,19 +89,9 @@ namespace AkVCam AKVCAM_SIGNAL(BroadcastingChanged, const std::string &deviceId, const std::string &broadcaster) - AKVCAM_SIGNAL(MirrorChanged, + AKVCAM_SIGNAL(ControlsChanged, const std::string &deviceId, - bool horizontalMirror, - bool verticalMirror) - AKVCAM_SIGNAL(ScalingChanged, - const std::string &deviceId, - Scaling scaling) - AKVCAM_SIGNAL(AspectRatioChanged, - const std::string &deviceId, - AspectRatio aspectRatio) - AKVCAM_SIGNAL(SwapRgbChanged, - const std::string &deviceId, - bool swap) + const std::map &controls) public: IpcBridge(); @@ -147,20 +159,9 @@ namespace AkVCam // Return return the status of the device. std::string broadcaster(const std::string &deviceId) const; - // Device is horizontal mirrored, - bool isHorizontalMirrored(const std::string &deviceId); - - // Device is vertical mirrored, - bool isVerticalMirrored(const std::string &deviceId); - - // Scaling mode for frames shown in clients. - Scaling scalingMode(const std::string &deviceId); - - // Aspect ratio mode for frames shown in clients. - AspectRatio aspectRatioMode(const std::string &deviceId); - - // Check if red and blue channels are swapped. - bool swapRgb(const std::string &deviceId); + std::vector controls(const std::string &deviceId); + void setControls(const std::string &deviceId, + const std::map &controls); // Returns the clients that are capturing from a virtual camera. std::vector listeners(const std::string &deviceId); @@ -179,7 +180,6 @@ namespace AkVCam const VideoFormat &format, int index=-1); void removeFormat(const std::string &deviceId, int index); - void update(); void updateDevices(); // Start frame transfer to the device. @@ -193,22 +193,6 @@ namespace AkVCam bool write(const std::string &deviceId, const VideoFrame &frame); - // Set mirroring options for device, - void setMirroring(const std::string &deviceId, - bool horizontalMirrored, - bool verticalMirrored); - - // Set scaling options for device. - void setScaling(const std::string &deviceId, - Scaling scaling); - - // Set aspect ratio options for device. - void setAspectRatio(const std::string &deviceId, - AspectRatio aspectRatio); - - // Swap red and blue channels. - void setSwapRgb(const std::string &deviceId, bool swap); - /* Client */ // Increment the count of device listeners diff --git a/VCamUtils/src/logger/logger.cpp b/VCamUtils/src/logger/logger.cpp index 77c8694..b1af5ec 100644 --- a/VCamUtils/src/logger/logger.cpp +++ b/VCamUtils/src/logger/logger.cpp @@ -125,14 +125,14 @@ std::ostream &AkVCam::Logger::log(int logLevel) return dummy; if (loggerPrivate()->fileName.empty()) - return std::cout; + return std::cerr; if (!loggerPrivate()->stream.is_open()) loggerPrivate()->stream.open(loggerPrivate()->fileName, std::ios_base::out | std::ios_base::app); if (!loggerPrivate()->stream.is_open()) - return std::cout; + return std::cerr; return loggerPrivate()->stream; } diff --git a/VCamUtils/src/settings.cpp b/VCamUtils/src/settings.cpp index 1884dde..20c0cb6 100644 --- a/VCamUtils/src/settings.cpp +++ b/VCamUtils/src/settings.cpp @@ -270,7 +270,7 @@ int32_t AkVCam::Settings::valueInt32(const std::string &key) const auto value = this->value(key); if (value.empty()) - return false; + return 0; char *p = nullptr; @@ -282,7 +282,7 @@ uint32_t AkVCam::Settings::valueUInt32(const std::string &key) const auto value = this->value(key); if (value.empty()) - return false; + return 0; char *p = nullptr; @@ -297,17 +297,22 @@ std::vector AkVCam::Settings::valueList(const std::string &key, if (value.empty()) return {}; - char *valuePtr = const_cast(value.c_str()); std::vector result; + size_t pos = 0; - for (;;) { - auto element = strsep(&valuePtr, separators.c_str()); + do { + auto index = value.size(); - if (!element) - break; + for (auto &separator: separators) { + auto it = std::find(value.begin() + pos, value.end(), separator); - result.push_back(trimmed(element)); - } + if (size_t(it - value.begin()) < index) + index = it - value.begin(); + } + + result.push_back(trimmed(value.substr(pos, index - pos))); + pos = index + 1; + } while (pos < value.size()); return result; } @@ -374,16 +379,15 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line, return element; } - auto pairSep = strchr(line.c_str(), '='); - - if (!pairSep) { + if (line.find('=') == std::string::npos) { if (ok) *ok = false; return {}; } - element.key = trimmed(line.substr(0, (size_t) (pairSep - line.c_str()))); + auto pair = splitOnce(line, "="); + element.key = trimmed(pair.first); std::replace(element.key.begin(), element.key.end(), '\\', '/'); if (element.key.empty()) { @@ -393,8 +397,7 @@ AkVCam::SettingsElement AkVCam::SettingsPrivate::parse(const std::string &line, return {}; } - auto offset = (size_t) (pairSep - line.c_str() + 1); - element.value = trimmed(line.substr(offset, line.size() - offset)); + element.value = trimmed(pair.second); element.value = this->parseString(element.value); if (ok) diff --git a/VCamUtils/src/utils.cpp b/VCamUtils/src/utils.cpp index 97c8fe8..a5e8bae 100644 --- a/VCamUtils/src/utils.cpp +++ b/VCamUtils/src/utils.cpp @@ -212,3 +212,17 @@ std::vector AkVCam::split(const std::string &str, char separator) return elements; } + +std::pair 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}; +} diff --git a/VCamUtils/src/utils.h b/VCamUtils/src/utils.h index 76e1003..1d5fe28 100644 --- a/VCamUtils/src/utils.h +++ b/VCamUtils/src/utils.h @@ -158,6 +158,8 @@ namespace AkVCam std::string join(const std::vector &strs, const std::string &separator); std::vector split(const std::string &str, char separator); + std::pair splitOnce(const std::string &str, + const std::string &separator); } #endif // AKVCAMUTILS_UTILS_H diff --git a/cmio/Assistant/src/assistant.cpp b/cmio/Assistant/src/assistant.cpp index 509a359..d32d1d7 100644 --- a/cmio/Assistant/src/assistant.cpp +++ b/cmio/Assistant/src/assistant.cpp @@ -17,6 +17,7 @@ * Web-Site: http://webcamoid.github.io/ */ +#include #include #include #include @@ -45,11 +46,6 @@ namespace AkVCam std::vector formats; std::string broadcaster; std::vector listeners; - bool horizontalMirror {false}; - bool verticalMirror {false}; - Scaling scaling {ScalingFast}; - AspectRatio aspectRatio {AspectRatioIgnore}; - bool swapRgb {false}; }; using AssistantPeers = std::map; @@ -82,21 +78,15 @@ namespace AkVCam void deviceDestroy(xpc_connection_t client, xpc_object_t event); void deviceUpdate(xpc_connection_t client, xpc_object_t event); void setBroadcasting(xpc_connection_t client, xpc_object_t event); - void setMirroring(xpc_connection_t client, xpc_object_t event); - void setScaling(xpc_connection_t client, xpc_object_t event); - void setAspectRatio(xpc_connection_t client, xpc_object_t event); - void setSwapRgb(xpc_connection_t client, xpc_object_t event); void frameReady(xpc_connection_t client, xpc_object_t event); + void pictureUpdated(xpc_connection_t client, xpc_object_t event); void listeners(xpc_connection_t client, xpc_object_t event); void listener(xpc_connection_t client, xpc_object_t event); void devices(xpc_connection_t client, xpc_object_t event); void description(xpc_connection_t client, xpc_object_t event); void formats(xpc_connection_t client, xpc_object_t event); void broadcasting(xpc_connection_t client, xpc_object_t event); - void mirroring(xpc_connection_t client, xpc_object_t event); - void scaling(xpc_connection_t client, xpc_object_t event); - void aspectRatio(xpc_connection_t client, xpc_object_t event); - void swapRgb(xpc_connection_t client, xpc_object_t event); + void controlsUpdated(xpc_connection_t client, xpc_object_t event); void listenerAdd(xpc_connection_t client, xpc_object_t event); void listenerRemove(xpc_connection_t client, xpc_object_t event); }; @@ -142,30 +132,24 @@ void AkVCam::Assistant::messageReceived(xpc_connection_t client, AkVCam::AssistantPrivate::AssistantPrivate() { this->m_messageHandlers = { - {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(AssistantPrivate::frameReady) }, - {AKVCAM_ASSISTANT_MSG_REQUEST_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::requestPort) }, - {AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::addPort) }, - {AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::removePort) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_CREATE , AKVCAM_BIND_FUNC(AssistantPrivate::deviceCreate) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY , AKVCAM_BIND_FUNC(AssistantPrivate::deviceDestroy) }, - {AKVCAM_ASSISTANT_MSG_DEVICES , AKVCAM_BIND_FUNC(AssistantPrivate::devices) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_DESCRIPTION , AKVCAM_BIND_FUNC(AssistantPrivate::description) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_FORMATS , AKVCAM_BIND_FUNC(AssistantPrivate::formats) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(AssistantPrivate::deviceUpdate) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(AssistantPrivate::listenerAdd) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE, AKVCAM_BIND_FUNC(AssistantPrivate::listenerRemove) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS , AKVCAM_BIND_FUNC(AssistantPrivate::listeners) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER , AKVCAM_BIND_FUNC(AssistantPrivate::listener) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::broadcasting) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING, AKVCAM_BIND_FUNC(AssistantPrivate::setBroadcasting)}, - {AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING , AKVCAM_BIND_FUNC(AssistantPrivate::mirroring) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(AssistantPrivate::setMirroring) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SCALING , AKVCAM_BIND_FUNC(AssistantPrivate::scaling) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING , AKVCAM_BIND_FUNC(AssistantPrivate::setScaling) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO , AKVCAM_BIND_FUNC(AssistantPrivate::aspectRatio) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO , AKVCAM_BIND_FUNC(AssistantPrivate::setAspectRatio) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB , AKVCAM_BIND_FUNC(AssistantPrivate::swapRgb) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB , AKVCAM_BIND_FUNC(AssistantPrivate::setSwapRgb) }, + {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(AssistantPrivate::frameReady) }, + {AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED , AKVCAM_BIND_FUNC(AssistantPrivate::pictureUpdated) }, + {AKVCAM_ASSISTANT_MSG_REQUEST_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::requestPort) }, + {AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::addPort) }, + {AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(AssistantPrivate::removePort) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_CREATE , AKVCAM_BIND_FUNC(AssistantPrivate::deviceCreate) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY , AKVCAM_BIND_FUNC(AssistantPrivate::deviceDestroy) }, + {AKVCAM_ASSISTANT_MSG_DEVICES , AKVCAM_BIND_FUNC(AssistantPrivate::devices) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_DESCRIPTION , AKVCAM_BIND_FUNC(AssistantPrivate::description) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_FORMATS , AKVCAM_BIND_FUNC(AssistantPrivate::formats) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(AssistantPrivate::deviceUpdate) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(AssistantPrivate::listenerAdd) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , AKVCAM_BIND_FUNC(AssistantPrivate::listenerRemove) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS , AKVCAM_BIND_FUNC(AssistantPrivate::listeners) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER , AKVCAM_BIND_FUNC(AssistantPrivate::listener) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::broadcasting) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(AssistantPrivate::setBroadcasting)}, + {AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(AssistantPrivate::controlsUpdated)}, }; this->loadCameras(); @@ -500,113 +484,6 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client, xpc_release(reply); } -void AkVCam::AssistantPrivate::setMirroring(xpc_connection_t client, - xpc_object_t event) -{ - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - bool horizontalMirror = xpc_dictionary_get_bool(event, "hmirror"); - bool verticalMirror = xpc_dictionary_get_bool(event, "vmirror"); - bool ok = false; - - if (this->m_deviceConfigs.count(deviceId) > 0) - if (this->m_deviceConfigs[deviceId].horizontalMirror != horizontalMirror - || this->m_deviceConfigs[deviceId].verticalMirror != verticalMirror) { - this->m_deviceConfigs[deviceId].horizontalMirror = horizontalMirror; - this->m_deviceConfigs[deviceId].verticalMirror = verticalMirror; - auto notification = xpc_copy(event); - - for (auto &client: this->m_clients) - xpc_connection_send_message(client.second, notification); - - xpc_release(notification); - ok = true; - } - - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_bool(reply, "status", ok); - xpc_connection_send_message(client, reply); - xpc_release(reply); -} - -void AkVCam::AssistantPrivate::setScaling(xpc_connection_t client, - xpc_object_t event) -{ - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - auto scaling = Scaling(xpc_dictionary_get_int64(event, "scaling")); - bool ok = false; - - if (this->m_deviceConfigs.count(deviceId) > 0) - if (this->m_deviceConfigs[deviceId].scaling != scaling) { - this->m_deviceConfigs[deviceId].scaling = scaling; - auto notification = xpc_copy(event); - - for (auto &client: this->m_clients) - xpc_connection_send_message(client.second, notification); - - xpc_release(notification); - ok = true; - } - - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_bool(reply, "status", ok); - xpc_connection_send_message(client, reply); - xpc_release(reply); -} - -void AkVCam::AssistantPrivate::setAspectRatio(xpc_connection_t client, - xpc_object_t event) -{ - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - auto aspectRatio = AspectRatio(xpc_dictionary_get_int64(event, "aspect")); - bool ok = false; - - if (this->m_deviceConfigs.count(deviceId) > 0) - if (this->m_deviceConfigs[deviceId].aspectRatio != aspectRatio) { - this->m_deviceConfigs[deviceId].aspectRatio = aspectRatio; - auto notification = xpc_copy(event); - - for (auto &client: this->m_clients) - xpc_connection_send_message(client.second, notification); - - xpc_release(notification); - ok = true; - } - - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_bool(reply, "status", ok); - xpc_connection_send_message(client, reply); - xpc_release(reply); -} - -void AkVCam::AssistantPrivate::setSwapRgb(xpc_connection_t client, - xpc_object_t event) -{ - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - auto swapRgb = xpc_dictionary_get_bool(event, "swap"); - bool ok = false; - - if (this->m_deviceConfigs.count(deviceId) > 0) - if (this->m_deviceConfigs[deviceId].swapRgb != swapRgb) { - this->m_deviceConfigs[deviceId].swapRgb = swapRgb; - auto notification = xpc_copy(event); - - for (auto &client: this->m_clients) - xpc_connection_send_message(client.second, notification); - - xpc_release(notification); - ok = true; - } - - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_bool(reply, "status", ok); - xpc_connection_send_message(client, reply); - xpc_release(reply); -} - void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client, xpc_object_t event) { @@ -634,6 +511,32 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client, xpc_release(reply); } +void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client, + xpc_object_t event) +{ + UNUSED(client); + AkLogFunction(); + auto reply = xpc_dictionary_create_reply(event); + bool ok = true; + + for (auto &client: this->m_clients) { + auto reply = xpc_connection_send_message_with_reply_sync(client.second, + event); + auto replyType = xpc_get_type(reply); + bool isOk = false; + + if (replyType == XPC_TYPE_DICTIONARY) + isOk = xpc_dictionary_get_bool(reply, "status"); + + ok &= isOk; + xpc_release(reply); + } + + xpc_dictionary_set_bool(reply, "status", ok); + xpc_connection_send_message(client, reply); + xpc_release(reply); +} + void AkVCam::AssistantPrivate::listeners(xpc_connection_t client, xpc_object_t event) { @@ -761,78 +664,24 @@ void AkVCam::AssistantPrivate::broadcasting(xpc_connection_t client, xpc_release(reply); } -void AkVCam::AssistantPrivate::mirroring(xpc_connection_t client, - xpc_object_t event) +void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client, xpc_object_t event) { AkLogFunction(); std::string deviceId = xpc_dictionary_get_string(event, "device"); - bool horizontalMirror = false; - bool verticalMirror = false; + bool ok = false; if (this->m_deviceConfigs.count(deviceId) > 0) { - horizontalMirror = this->m_deviceConfigs[deviceId].horizontalMirror; - verticalMirror = this->m_deviceConfigs[deviceId].verticalMirror; + auto notification = xpc_copy(event); + + for (auto &client: this->m_clients) + xpc_connection_send_message(client.second, notification); + + xpc_release(notification); + ok = true; } - AkLogInfo() << "Device: " << deviceId << std::endl; - AkLogInfo() << "Horizontal mirror: " << horizontalMirror << std::endl; - AkLogInfo() << "Vertical mirror: " << verticalMirror << std::endl; auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_bool(reply, "hmirror", horizontalMirror); - xpc_dictionary_set_bool(reply, "vmirror", verticalMirror); - xpc_connection_send_message(client, reply); - xpc_release(reply); -} - -void AkVCam::AssistantPrivate::scaling(xpc_connection_t client, xpc_object_t event) -{ - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - Scaling scaling = ScalingFast; - - if (this->m_deviceConfigs.count(deviceId) > 0) - scaling = this->m_deviceConfigs[deviceId].scaling; - - AkLogInfo() << "Device: " << deviceId << std::endl; - AkLogInfo() << "Scaling: " << scaling << std::endl; - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_int64(reply, "scaling", scaling); - xpc_connection_send_message(client, reply); - xpc_release(reply); -} - -void AkVCam::AssistantPrivate::aspectRatio(xpc_connection_t client, - xpc_object_t event) -{ - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - AspectRatio aspectRatio = AspectRatioIgnore; - - if (this->m_deviceConfigs.count(deviceId) > 0) - aspectRatio = this->m_deviceConfigs[deviceId].aspectRatio; - - AkLogInfo() << "Device: " << deviceId << std::endl; - AkLogInfo() << "Aspect ratio: " << aspectRatio << std::endl; - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_int64(reply, "aspect", aspectRatio); - xpc_connection_send_message(client, reply); - xpc_release(reply); -} - -void AkVCam::AssistantPrivate::swapRgb(xpc_connection_t client, - xpc_object_t event) -{ - AkLogFunction(); - std::string deviceId = xpc_dictionary_get_string(event, "device"); - bool swapRgb = false; - - if (this->m_deviceConfigs.count(deviceId) > 0) - swapRgb = this->m_deviceConfigs[deviceId].swapRgb; - - AkLogInfo() << "Device: " << deviceId << std::endl; - AkLogInfo() << "Swap RGB: " << swapRgb << std::endl; - auto reply = xpc_dictionary_create_reply(event); - xpc_dictionary_set_bool(reply, "swap", swapRgb); + xpc_dictionary_set_bool(reply, "status", ok); xpc_connection_send_message(client, reply); xpc_release(reply); } diff --git a/cmio/Assistant/src/assistantglobals.h b/cmio/Assistant/src/assistantglobals.h index 4658c98..58bcf2a 100644 --- a/cmio/Assistant/src/assistantglobals.h +++ b/cmio/Assistant/src/assistantglobals.h @@ -26,44 +26,37 @@ #define AKVCAM_ASSISTANT_CLIENT_NAME "org.webcamoid.cmio.AkVCam.Client" // General messages -#define AKVCAM_ASSISTANT_MSG_ISALIVE 0x000 -#define AKVCAM_ASSISTANT_MSG_FRAME_READY 0x001 +#define AKVCAM_ASSISTANT_MSG_ISALIVE 0x000 +#define AKVCAM_ASSISTANT_MSG_FRAME_READY 0x001 +#define AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED 0x002 // Assistant messages -#define AKVCAM_ASSISTANT_MSG_REQUEST_PORT 0x100 -#define AKVCAM_ASSISTANT_MSG_ADD_PORT 0x101 -#define AKVCAM_ASSISTANT_MSG_REMOVE_PORT 0x102 +#define AKVCAM_ASSISTANT_MSG_REQUEST_PORT 0x100 +#define AKVCAM_ASSISTANT_MSG_ADD_PORT 0x101 +#define AKVCAM_ASSISTANT_MSG_REMOVE_PORT 0x102 // Device control and information -#define AKVCAM_ASSISTANT_MSG_DEVICES 0x200 -#define AKVCAM_ASSISTANT_MSG_DEVICE_CREATE 0x201 -#define AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY 0x202 -#define AKVCAM_ASSISTANT_MSG_DEVICE_DESCRIPTION 0x203 -#define AKVCAM_ASSISTANT_MSG_DEVICE_FORMATS 0x204 -#define AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE 0x205 +#define AKVCAM_ASSISTANT_MSG_DEVICES 0x200 +#define AKVCAM_ASSISTANT_MSG_DEVICE_CREATE 0x201 +#define AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY 0x202 +#define AKVCAM_ASSISTANT_MSG_DEVICE_DESCRIPTION 0x203 +#define AKVCAM_ASSISTANT_MSG_DEVICE_FORMATS 0x204 +#define AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE 0x205 // Device listeners controls -#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS 0x300 -#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER 0x301 -#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD 0x302 -#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE 0x303 +#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS 0x300 +#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER 0x301 +#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD 0x302 +#define AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE 0x303 // Device dynamic properties -#define AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING 0x400 -#define AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING 0x401 -#define AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING 0x402 -#define AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING 0x403 -#define AKVCAM_ASSISTANT_MSG_DEVICE_SCALING 0x404 -#define AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING 0x405 -#define AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO 0x406 -#define AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO 0x407 -#define AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB 0x408 -#define AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB 0x409 +#define AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING 0x400 +#define AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING 0x401 +#define AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED 0x402 // Connections -#define AKVCAM_ASSISTANT_MSG_CONNECTIONS 0x500 -#define AKVCAM_ASSISTANT_MSG_SETCONNECTIONS 0x501 - +#define AKVCAM_ASSISTANT_MSG_CONNECTIONS 0x500 +#define AKVCAM_ASSISTANT_MSG_SETCONNECTIONS 0x501 namespace AkVCam { diff --git a/cmio/Assistant/src/main.cpp b/cmio/Assistant/src/main.cpp index 7564d51..4fd5399 100644 --- a/cmio/Assistant/src/main.cpp +++ b/cmio/Assistant/src/main.cpp @@ -17,6 +17,7 @@ * Web-Site: http://webcamoid.github.io/ */ +#include #include #include #include diff --git a/cmio/PlatformUtils/PlatformUtils.pro b/cmio/PlatformUtils/PlatformUtils.pro index 4fbbdcf..39b3886 100644 --- a/cmio/PlatformUtils/PlatformUtils.pro +++ b/cmio/PlatformUtils/PlatformUtils.pro @@ -42,7 +42,8 @@ TEMPLATE = lib LIBS = \ -L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \ - -framework CoreFoundation + -framework CoreFoundation \ + -framework CoreGraphics SOURCES = \ src/preferences.cpp \ diff --git a/cmio/PlatformUtils/src/preferences.cpp b/cmio/PlatformUtils/src/preferences.cpp index 1687809..4a8f858 100644 --- a/cmio/PlatformUtils/src/preferences.cpp +++ b/cmio/PlatformUtils/src/preferences.cpp @@ -17,6 +17,8 @@ * Web-Site: http://webcamoid.github.io/ */ +#include + #include "preferences.h" #include "utils.h" #include "VCamUtils/src/image/videoformat.h" @@ -609,3 +611,16 @@ void AkVCam::Preferences::setLogLevel(int logLevel) { write("loglevel", logLevel); } + +int AkVCam::Preferences::cameraControlValue(size_t cameraIndex, + const std::string &key) +{ + return readInt("cameras." + std::to_string(cameraIndex) + ".controls." + key); +} + +void AkVCam::Preferences::cameraSetControlValue(size_t cameraIndex, + const std::string &key, + int value) +{ + write("cameras." + std::to_string(cameraIndex) + ".controls." + key, value); +} diff --git a/cmio/PlatformUtils/src/preferences.h b/cmio/PlatformUtils/src/preferences.h index aa54383..ee046fb 100644 --- a/cmio/PlatformUtils/src/preferences.h +++ b/cmio/PlatformUtils/src/preferences.h @@ -20,6 +20,7 @@ #ifndef PREFERENCES_H #define PREFERENCES_H +#include #include #include #include @@ -79,6 +80,11 @@ namespace AkVCam const VideoFormat &format, int index); void cameraRemoveFormat(size_t cameraIndex, int index); + int cameraControlValue(size_t cameraIndex, + const std::string &key); + void cameraSetControlValue(size_t cameraIndex, + const std::string &key, + int value); std::wstring picture(); void setPicture(const std::wstring &picture); int logLevel(); diff --git a/cmio/PlatformUtils/src/utils.cpp b/cmio/PlatformUtils/src/utils.cpp index 9c8eef1..5c90124 100644 --- a/cmio/PlatformUtils/src/utils.cpp +++ b/cmio/PlatformUtils/src/utils.cpp @@ -17,7 +17,97 @@ * Web-Site: http://webcamoid.github.io/ */ +#include +#include +#include +#include +#import +#import + #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 *formatsTable() + { + static const std::map 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(&uuid1); + auto puuid2 = reinterpret_cast(&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(&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 AkVCam::cfTypeFromStd(const std::string &str) { @@ -135,3 +225,174 @@ std::wstring AkVCam::wstringFromCFType(CFTypeRef cfType) return std::wstring(cstr, size_t(len)); } + +std::string AkVCam::realPath(const std::string &path) +{ + char resolvedPath[PATH_MAX]; + memset(resolvedPath, 0, PATH_MAX); + ::realpath(path.c_str(), resolvedPath); + + char realPath[PATH_MAX]; + memset(realPath, 0, PATH_MAX); + readlink(resolvedPath, realPath, PATH_MAX); + + if (strlen(realPath) < 1) + return {resolvedPath}; + + return {realPath}; +} + +AkVCam::VideoFrame AkVCam::loadPicture(const std::string &fileName) +{ + AkLogFunction(); + VideoFrame frame; + + if (frame.load(fileName)) { + AkLogInfo() << "Picture loaded as BMP" << std::endl; + + return frame; + } + + auto fileDataProvider = CGDataProviderCreateWithFilename(fileName.c_str()); + + if (!fileDataProvider) { + AkLogError() << "Can't create a data provider for '" + << fileName + << "'" + << std::endl; + + return {}; + } + + // Check if the file is a PNG and open it. + auto cgImage = CGImageCreateWithPNGDataProvider(fileDataProvider, + nullptr, + true, + kCGRenderingIntentDefault); + + // If the file is not a PNG, try opening as JPEG. + if (!cgImage) { + AkLogWarning() << "Can't read '" + << fileName + << "' as a PNG image." + << std::endl; + cgImage = CGImageCreateWithJPEGDataProvider(fileDataProvider, + nullptr, + true, + kCGRenderingIntentDefault); + } + + CGDataProviderRelease(fileDataProvider); + + // The file format is not supported, fail. + if (!cgImage) { + AkLogError() << "Can't read '" + << fileName + << "' as a JPEG image." + << std::endl; + + return {}; + } + + FourCC format = 0; + + if (CGImageGetBitsPerComponent(cgImage) == 8) { + if (CGImageGetBitsPerPixel(cgImage) == 24) + format = PixelFormatRGB24; + else if (CGImageGetBitsPerPixel(cgImage) == 32) { + format = PixelFormatRGB32; + } + } + + auto width = CGImageGetWidth(cgImage); + auto height = CGImageGetHeight(cgImage); + + if (format == 0 || width < 1 || height < 1) { + AkLogError() << "Invalid picture format: " + << "BPC=" + << CGImageGetBitsPerComponent(cgImage) + << "BPP=" + << CGImageGetBitsPerPixel(cgImage) + << " " + << width + << "x" + << height + << std::endl; + CGImageRelease(cgImage); + + return {}; + } + + VideoFormat videoFormat(PixelFormatRGB24, width, height); + frame = VideoFrame(videoFormat); + + auto imageDataProvider = CGImageGetDataProvider(cgImage); + + if (!imageDataProvider) { + AkLogError() << "Can't get data provider for picture." << std::endl; + CGImageRelease(cgImage); + + return {}; + } + + auto data = CGDataProviderCopyData(imageDataProvider); + + if (!data) { + AkLogError() << "Can't copy data from image provider." << std::endl; + CGImageRelease(cgImage); + + return {}; + } + + auto lineSize = CGImageGetBytesPerRow(cgImage); + + if (CGImageGetBitsPerPixel(cgImage) == 24) { + for (int y = 0; y < videoFormat.height(); y++) { + auto srcLine = reinterpret_cast(CFDataGetBytePtr(data) + y * lineSize); + auto dstLine = reinterpret_cast(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(CFDataGetBytePtr(data) + y * lineSize); + auto dstLine = reinterpret_cast(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(CFDataGetBytePtr(data) + y * lineSize); + auto dstLine = reinterpret_cast(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; +} diff --git a/cmio/PlatformUtils/src/utils.h b/cmio/PlatformUtils/src/utils.h index 194f243..ce6b6fb 100644 --- a/cmio/PlatformUtils/src/utils.h +++ b/cmio/PlatformUtils/src/utils.h @@ -20,19 +20,29 @@ #ifndef PLATFORM_UTILS_H #define PLATFORM_UTILS_H +#include #include -#include +#include +#include + +#include "VCamUtils/src/image/videoformattypes.h" namespace AkVCam { - class VideoFormat; + class VideoFrame; + bool uuidEqual(const REFIID &uuid1, const CFUUIDRef uuid2); + std::string enumToString(UInt32 value); + FourCharCode formatToCM(PixelFormat format); + PixelFormat formatFromCM(FourCharCode format); std::shared_ptr cfTypeFromStd(const std::string &str); std::shared_ptr cfTypeFromStd(const std::wstring &str); std::shared_ptr cfTypeFromStd(int num); std::shared_ptr cfTypeFromStd(double num); std::string stringFromCFType(CFTypeRef cfType); std::wstring wstringFromCFType(CFTypeRef cfType); + std::string realPath(const std::string &path); + VideoFrame loadPicture(const std::string &fileName); } #endif // PLATFORM_UTILS_H diff --git a/cmio/VCamIPC/src/ipcbridge.mm b/cmio/VCamIPC/src/ipcbridge.mm index 3264178..409f240 100644 --- a/cmio/VCamIPC/src/ipcbridge.mm +++ b/cmio/VCamIPC/src/ipcbridge.mm @@ -17,12 +17,16 @@ * Web-Site: http://webcamoid.github.io/ */ +#include +#include #include +#include #include #include -#include #include #include +#include +#include #include #include #include @@ -31,6 +35,7 @@ #include "Assistant/src/assistantglobals.h" #include "PlatformUtils/src/preferences.h" +#include "PlatformUtils/src/utils.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/ipcbridge.h" @@ -61,6 +66,7 @@ namespace AkVCam inline void add(IpcBridge *bridge); void remove(IpcBridge *bridge); inline std::vector &bridges(); + inline const std::vector &controls() const; // Message handling methods void isAlive(xpc_connection_t client, xpc_object_t event); @@ -68,12 +74,9 @@ namespace AkVCam void deviceDestroy(xpc_connection_t client, xpc_object_t event); void deviceUpdate(xpc_connection_t client, xpc_object_t event); void frameReady(xpc_connection_t client, xpc_object_t event); - void setBroadcasting(xpc_connection_t client, - xpc_object_t event); - void setMirror(xpc_connection_t client, xpc_object_t event); - void setScaling(xpc_connection_t client, xpc_object_t event); - void setAspectRatio(xpc_connection_t client, xpc_object_t event); - void setSwapRgb(xpc_connection_t client, xpc_object_t event); + void pictureUpdated(xpc_connection_t client, xpc_object_t event); + void setBroadcasting(xpc_connection_t client, xpc_object_t event); + void controlsUpdated(xpc_connection_t client, xpc_object_t event); void listenerAdd(xpc_connection_t client, xpc_object_t event); void listenerRemove(xpc_connection_t client, xpc_object_t event); void messageReceived(xpc_connection_t client, xpc_object_t event); @@ -83,10 +86,9 @@ namespace AkVCam std::string homePath() const; bool fileExists(const std::wstring &path) const; bool fileExists(const std::string &path) const; - std::wstring fileName(const std::wstring &path) const; bool mkpath(const std::string &path) const; bool rm(const std::string &path) const; - std::wstring locateDriverPath() const; + static std::string locatePluginPath(); private: std::vector m_bridges; @@ -169,7 +171,18 @@ std::wstring AkVCam::IpcBridge::picture() const void AkVCam::IpcBridge::setPicture(const std::wstring &picture) { + AkLogFunction(); Preferences::setPicture(picture); + + if (!this->d->m_serverMessagePort) + return; + + std::wstring_convert> cv; + auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); + xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED); + xpc_dictionary_set_string(dictionary, "picture", cv.to_bytes(picture).c_str()); + xpc_connection_send_message(this->d->m_serverMessagePort, dictionary); + xpc_release(dictionary); } int AkVCam::IpcBridge::logLevel() const @@ -446,139 +459,62 @@ std::string AkVCam::IpcBridge::broadcaster(const std::string &deviceId) const return broadcaster; } -bool AkVCam::IpcBridge::isHorizontalMirrored(const std::string &deviceId) +std::vector AkVCam::IpcBridge::controls(const std::string &deviceId) { AkLogFunction(); + auto cameraIndex = Preferences::cameraFromPath(deviceId); - if (!this->d->m_serverMessagePort) - return false; + if (cameraIndex < 0) + return {}; - 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); + std::vector controls; - if (replyType != XPC_TYPE_DICTIONARY) { - xpc_release(reply); - - return false; + for (auto &control: this->d->controls()) { + controls.push_back(control); + controls.back().value = + Preferences::cameraControlValue(cameraIndex, control.id); } - bool horizontalMirror = xpc_dictionary_get_bool(reply, "hmirror"); - xpc_release(reply); - - return horizontalMirror; + return controls; } -bool AkVCam::IpcBridge::isVerticalMirrored(const std::string &deviceId) +void AkVCam::IpcBridge::setControls(const std::string &deviceId, + const std::map &controls) { AkLogFunction(); + auto cameraIndex = Preferences::cameraFromPath(deviceId); - if (!this->d->m_serverMessagePort) - return false; + if (cameraIndex < 0) + return; + + bool updated = false; + + for (auto &control: this->d->controls()) { + auto oldValue = + Preferences::cameraControlValue(cameraIndex, control.id); + + if (controls.count(control.id)) { + auto newValue = controls.at(control.id); + + if (newValue != oldValue) { + Preferences::cameraSetControlValue(cameraIndex, + control.id, + newValue); + updated = true; + } + } + } + + if (!this->d->m_serverMessagePort || !updated) + return; auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING); + xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED); xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, dictionary); xpc_release(dictionary); - auto replyType = xpc_get_type(reply); - - if (replyType != XPC_TYPE_DICTIONARY) { - xpc_release(reply); - - return false; - } - - bool 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 AkVCam::IpcBridge::listeners(const std::string &deviceId) @@ -618,17 +554,19 @@ std::vector AkVCam::IpcBridge::listeners(const std::string &deviceI std::vector AkVCam::IpcBridge::clientsPids() const { - auto driverPath = this->d->locateDriverPath(); + AkLogFunction(); + auto driverPath = this->d->locatePluginPath(); + AkLogDebug() << "Plugin path: " << driverPath << std::endl; if (driverPath.empty()) return {}; - auto plugin = this->d->fileName(driverPath); - std::wstring pluginPath = - CMIO_PLUGINS_DAL_PATH_L L"/" - + plugin - + L"/Contents/MacOS/" CMIO_PLUGIN_NAME_L; - std::string path(pluginPath.begin(), pluginPath.end()); + auto path = driverPath + "/Contents/MacOS/" CMIO_PLUGIN_NAME; + AkLogDebug() << "Plugin binary: " << path << std::endl; + + if (!this->d->fileExists(path)) + return {}; + auto npids = proc_listpidspath(PROC_ALL_PIDS, 0, path.c_str(), @@ -692,7 +630,7 @@ void AkVCam::IpcBridge::removeFormat(const std::string &deviceId, int index) index); } -void AkVCam::IpcBridge::update() +void AkVCam::IpcBridge::updateDevices() { AkLogFunction(); @@ -705,19 +643,6 @@ void AkVCam::IpcBridge::update() AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE); xpc_connection_send_message(this->d->m_serverMessagePort, dictionary); xpc_release(dictionary); -} - -void AkVCam::IpcBridge::updateDevices() -{ - AkLogFunction(); - - if (!this->d->m_serverMessagePort) - return; - - auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE); - xpc_connection_send_message(this->d->m_serverMessagePort, dictionary); - xpc_release(dictionary); } @@ -856,79 +781,6 @@ bool AkVCam::IpcBridge::write(const std::string &deviceId, return true; } -void AkVCam::IpcBridge::setMirroring(const std::string &deviceId, - bool horizontalMirrored, - bool verticalMirrored) -{ - AkLogFunction(); - - if (!this->d->m_serverMessagePort) - return; - - auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING); - xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); - xpc_dictionary_set_bool(dictionary, "hmirror", horizontalMirrored); - xpc_dictionary_set_bool(dictionary, "vmirror", verticalMirrored); - auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, - dictionary); - xpc_release(dictionary); - xpc_release(reply); -} - -void AkVCam::IpcBridge::setScaling(const std::string &deviceId, - Scaling scaling) -{ - AkLogFunction(); - - if (!this->d->m_serverMessagePort) - return; - - auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING); - xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); - xpc_dictionary_set_int64(dictionary, "scaling", scaling); - auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, - dictionary); - xpc_release(dictionary); - xpc_release(reply); -} - -void AkVCam::IpcBridge::setAspectRatio(const std::string &deviceId, - AspectRatio aspectRatio) -{ - AkLogFunction(); - - if (!this->d->m_serverMessagePort) - return; - - auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO); - xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); - xpc_dictionary_set_int64(dictionary, "aspect", aspectRatio); - auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, - dictionary); - xpc_release(dictionary); - xpc_release(reply); -} - -void AkVCam::IpcBridge::setSwapRgb(const std::string &deviceId, bool swap) -{ - AkLogFunction(); - - if (!this->d->m_serverMessagePort) - return; - - auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0); - xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB); - xpc_dictionary_set_string(dictionary, "device", deviceId.c_str()); - xpc_dictionary_set_bool(dictionary, "swap", swap); - auto reply = xpc_connection_send_message_with_reply_sync(this->d->m_serverMessagePort, - dictionary); - xpc_release(dictionary); - xpc_release(reply); -} - bool AkVCam::IpcBridge::addListener(const std::string &deviceId) { AkLogFunction(); @@ -991,18 +843,16 @@ AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self): m_serverMessagePort(nullptr) { this->m_messageHandlers = { - {AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) }, - {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_CREATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceCreate) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceDestroy) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceUpdate) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerAdd) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE, AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerRemove) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING, AKVCAM_BIND_FUNC(IpcBridgePrivate::setBroadcasting)}, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setMirror) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setScaling) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO , AKVCAM_BIND_FUNC(IpcBridgePrivate::setAspectRatio) }, - {AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB , AKVCAM_BIND_FUNC(IpcBridgePrivate::setSwapRgb) }, + {AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) }, + {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) }, + {AKVCAM_ASSISTANT_MSG_PICTURE_UPDATED , AKVCAM_BIND_FUNC(IpcBridgePrivate::pictureUpdated) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_CREATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceCreate) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_DESTROY , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceDestroy) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_UPDATE , AKVCAM_BIND_FUNC(IpcBridgePrivate::deviceUpdate) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerAdd) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerRemove) }, + {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setBroadcasting)}, + {AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(IpcBridgePrivate::controlsUpdated)}, }; } @@ -1038,6 +888,31 @@ std::vector &AkVCam::IpcBridgePrivate::bridges() return this->m_bridges; } +const std::vector &AkVCam::IpcBridgePrivate::controls() const +{ + static const std::vector scalingMenu { + "Fast", + "Linear" + }; + static const std::vector aspectRatioMenu { + "Ignore", + "Keep", + "Expanding" + }; + static const auto scalingMax = int(scalingMenu.size()) - 1; + static const auto aspectRatioMax = int(aspectRatioMenu.size()) - 1; + + static const std::vector controls { + {"hflip" , "Horizontal Mirror", ControlTypeBoolean, 0, 1 , 1, 0, 0, {} }, + {"vflip" , "Vertical Mirror" , ControlTypeBoolean, 0, 1 , 1, 0, 0, {} }, + {"scaling" , "Scaling" , ControlTypeMenu , 0, scalingMax , 1, 0, 0, scalingMenu }, + {"aspect_ratio", "Aspect Ratio" , ControlTypeMenu , 0, aspectRatioMax, 1, 0, 0, aspectRatioMenu}, + {"swap_rgb" , "Swap RGB" , ControlTypeBoolean, 0, 1 , 1, 0, 0, {} }, + }; + + return controls; +} + void AkVCam::IpcBridgePrivate::isAlive(xpc_connection_t client, xpc_object_t event) { @@ -1118,6 +993,18 @@ void AkVCam::IpcBridgePrivate::frameReady(xpc_connection_t client, xpc_release(reply); } +void AkVCam::IpcBridgePrivate::pictureUpdated(xpc_connection_t client, + xpc_object_t event) +{ + UNUSED(client); + AkLogFunction(); + + std::string picture = xpc_dictionary_get_string(event, "picture"); + + for (auto bridge: this->m_bridges) + AKVCAM_EMIT(bridge, PictureChanged, picture) +} + void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client, xpc_object_t event) { @@ -1133,69 +1020,31 @@ void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client, AKVCAM_EMIT(bridge, BroadcastingChanged, deviceId, broadcaster) } -void AkVCam::IpcBridgePrivate::setMirror(xpc_connection_t client, - xpc_object_t event) +void AkVCam::IpcBridgePrivate::controlsUpdated(xpc_connection_t client, + xpc_object_t event) { UNUSED(client); 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"); + auto cameraIndex = Preferences::cameraFromPath(deviceId); + std::map controls; + + for (auto &control: this->controls()) + controls[control.id] = + Preferences::cameraControlValue(cameraIndex, control.id); for (auto bridge: this->m_bridges) AKVCAM_EMIT(bridge, - MirrorChanged, + ControlsChanged, deviceId, - horizontalMirror, - verticalMirror) -} + controls) -void AkVCam::IpcBridgePrivate::setScaling(xpc_connection_t client, - xpc_object_t event) -{ - UNUSED(client); - AkLogFunction(); - - std::string deviceId = - xpc_dictionary_get_string(event, "device"); - auto scaling = - Scaling(xpc_dictionary_get_int64(event, "scaling")); - - for (auto bridge: this->m_bridges) - AKVCAM_EMIT(bridge, ScalingChanged, deviceId, scaling) -} - -void AkVCam::IpcBridgePrivate::setAspectRatio(xpc_connection_t client, - xpc_object_t event) -{ - UNUSED(client); - AkLogFunction(); - - std::string deviceId = - xpc_dictionary_get_string(event, "device"); - auto aspectRatio = - AspectRatio(xpc_dictionary_get_int64(event, "aspect")); - - for (auto bridge: this->m_bridges) - AKVCAM_EMIT(bridge, AspectRatioChanged, deviceId, aspectRatio) -} - -void AkVCam::IpcBridgePrivate::setSwapRgb(xpc_connection_t client, - xpc_object_t event) -{ - UNUSED(client); - AkLogFunction(); - - std::string deviceId = - xpc_dictionary_get_string(event, "device"); - auto swap = xpc_dictionary_get_bool(event, "swap"); - - for (auto bridge: this->m_bridges) - AKVCAM_EMIT(bridge, SwapRgbChanged, deviceId, swap) + auto reply = xpc_dictionary_create_reply(event); + xpc_dictionary_set_bool(reply, "status", cameraIndex >= 0); + xpc_connection_send_message(client, reply); + xpc_release(reply); } void AkVCam::IpcBridgePrivate::listenerAdd(xpc_connection_t client, @@ -1280,11 +1129,6 @@ bool AkVCam::IpcBridgePrivate::fileExists(const std::string &path) const return stat(path.c_str(), &stats) == 0; } -std::wstring AkVCam::IpcBridgePrivate::fileName(const std::wstring &path) const -{ - return path.substr(path.rfind(L'/') + 1); -} - bool AkVCam::IpcBridgePrivate::mkpath(const std::string &path) const { if (path.empty()) @@ -1341,32 +1185,14 @@ bool AkVCam::IpcBridgePrivate::rm(const std::string &path) const return ok; } -std::wstring AkVCam::IpcBridgePrivate::locateDriverPath() const +std::string AkVCam::IpcBridgePrivate::locatePluginPath() { AkLogFunction(); - std::wstring driverPath; + Dl_info info; + memset(&info, 0, sizeof(Dl_info)); + dladdr(reinterpret_cast(&AkVCam::IpcBridgePrivate::locatePluginPath), + &info); + std::string dirName = dirname(const_cast(info.dli_fname)); - for (auto it = this->driverPaths()->rbegin(); - it != this->driverPaths()->rend(); - it++) { - auto path = *it; - path = replace(path, L"\\", L"/"); - - if (path.back() != L'/') - path += L'/'; - - path += CMIO_PLUGIN_NAME_L L".plugin"; - - if (!this->fileExists(path + L"/Contents/MacOS/" CMIO_PLUGIN_NAME_L)) - continue; - - if (!this->fileExists(path + L"/Contents/Resources/" CMIO_PLUGIN_ASSISTANT_NAME_L)) - continue; - - driverPath = path; - - break; - } - - return driverPath; + return realPath(dirName + "/../.."); } diff --git a/cmio/VirtualCamera/VirtualCamera.pro b/cmio/VirtualCamera/VirtualCamera.pro index e725b60..ece6e8d 100644 --- a/cmio/VirtualCamera/VirtualCamera.pro +++ b/cmio/VirtualCamera/VirtualCamera.pro @@ -44,6 +44,7 @@ LIBS = \ -L$${OUT_PWD}/../../VCamUtils/$${BIN_DIR} -lVCamUtils \ -L$${OUT_PWD}/../VCamIPC/$${BIN_DIR} -lVCamIPC \ -framework CoreFoundation \ + -framework CoreGraphics \ -framework CoreMedia \ -framework CoreMediaIO \ -framework CoreVideo \ @@ -57,7 +58,6 @@ TEMPLATE = lib HEADERS += \ src/plugin.h \ src/plugininterface.h \ - src/utils.h \ src/device.h \ src/object.h \ src/stream.h \ @@ -69,7 +69,6 @@ HEADERS += \ SOURCES += \ src/plugin.cpp \ src/plugininterface.cpp \ - src/utils.cpp \ src/device.cpp \ src/object.cpp \ src/stream.cpp \ @@ -88,8 +87,7 @@ RESOURCESPATH = $${CONTENTSPATH}/Resources INSTALLS += \ targetLib \ - infoPlist \ - resources + infoPlist targetLib.files = $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME}) targetLib.path = $${PREFIX}/$${MACBINPATH} @@ -99,12 +97,7 @@ infoPlist.files = $$shell_path($${OUT_PWD}/Info.plist) infoPlist.path = $${PREFIX}/$${CONTENTSPATH} infoPlist.CONFIG += no_check_exist -resources.files = ../../share/TestFrame/TestFrame.bmp -resources.path = $${PREFIX}/$${RESOURCESPATH} - QMAKE_POST_LINK = \ $$sprintf($$QMAKE_MKDIR_CMD, $$shell_path($${OUT_PWD}/../../$${MACBINPATH})) $${CMD_SEP} \ - $$sprintf($$QMAKE_MKDIR_CMD, $$shell_path($${OUT_PWD}/../../$${RESOURCESPATH})) $${CMD_SEP} \ $(COPY) $$shell_path($${OUT_PWD}/Info.plist) $$shell_path($${OUT_PWD}/../../$${CONTENTSPATH}) $${CMD_SEP} \ - $(COPY) $$shell_path($${OUT_PWD}/$${BIN_DIR}/lib$${CMIO_PLUGIN_NAME}.$${QMAKE_EXTENSION_SHLIB}) $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME}) $${CMD_SEP} \ - $(COPY) $$shell_path($${PWD}/../../share/TestFrame/TestFrame.bmp) $$shell_path($${OUT_PWD}/../../$${RESOURCESPATH}) + $(COPY) $$shell_path($${OUT_PWD}/$${BIN_DIR}/lib$${CMIO_PLUGIN_NAME}.$${QMAKE_EXTENSION_SHLIB}) $$shell_path($${OUT_PWD}/../../$${MACBINPATH}/$${CMIO_PLUGIN_NAME}) $${CMD_SEP} diff --git a/cmio/VirtualCamera/src/device.cpp b/cmio/VirtualCamera/src/device.cpp index 7fda526..4037726 100644 --- a/cmio/VirtualCamera/src/device.cpp +++ b/cmio/VirtualCamera/src/device.cpp @@ -18,7 +18,7 @@ */ #include "device.h" -#include "utils.h" +#include "PlatformUtils/src/utils.h" #include "VCamUtils/src/logger/logger.h" AkVCam::Device::Device(CMIOHardwarePlugInRef pluginInterface, @@ -194,16 +194,28 @@ void AkVCam::Device::frameReady(const AkVCam::VideoFrame &frame) stream.second->frameReady(frame); } +void AkVCam::Device::setPicture(const std::string &picture) +{ + for (auto &stream: this->m_streams) + stream.second->setPicture(picture); +} + void AkVCam::Device::setBroadcasting(const std::string &broadcaster) { for (auto &stream: this->m_streams) stream.second->setBroadcasting(broadcaster); } -void AkVCam::Device::setMirror(bool horizontalMirror, bool verticalMirror) +void AkVCam::Device::setHorizontalMirror(bool horizontalMirror) { for (auto &stream: this->m_streams) - stream.second->setMirror(horizontalMirror, verticalMirror); + stream.second->setHorizontalMirror(horizontalMirror); +} + +void AkVCam::Device::setVerticalMirror(bool verticalMirror) +{ + for (auto &stream: this->m_streams) + stream.second->setVerticalMirror(verticalMirror); } void AkVCam::Device::setScaling(Scaling scaling) diff --git a/cmio/VirtualCamera/src/device.h b/cmio/VirtualCamera/src/device.h index 4a73045..a827efe 100644 --- a/cmio/VirtualCamera/src/device.h +++ b/cmio/VirtualCamera/src/device.h @@ -52,8 +52,10 @@ namespace AkVCam void serverStateChanged(IpcBridge::ServerState state); void frameReady(const VideoFrame &frame); + void setPicture(const std::string &picture); void setBroadcasting(const std::string &broadcaster); - void setMirror(bool horizontalMirror, bool verticalMirror); + void setHorizontalMirror(bool horizontalMirror); + void setVerticalMirror(bool verticalMirror); void setScaling(Scaling scaling); void setAspectRatio(AspectRatio aspectRatio); void setSwapRgb(bool swap); diff --git a/cmio/VirtualCamera/src/object.cpp b/cmio/VirtualCamera/src/object.cpp index 628b90c..3fa4b84 100644 --- a/cmio/VirtualCamera/src/object.cpp +++ b/cmio/VirtualCamera/src/object.cpp @@ -18,7 +18,7 @@ */ #include "object.h" -#include "utils.h" +#include "PlatformUtils/src/utils.h" AkVCam::Object::Object(CMIOHardwarePlugInRef pluginInterface, Object *parent): diff --git a/cmio/VirtualCamera/src/objectinterface.cpp b/cmio/VirtualCamera/src/objectinterface.cpp index a4249b3..fe0ca55 100644 --- a/cmio/VirtualCamera/src/objectinterface.cpp +++ b/cmio/VirtualCamera/src/objectinterface.cpp @@ -18,7 +18,7 @@ */ #include "objectinterface.h" -#include "utils.h" +#include "PlatformUtils/src/utils.h" #include "VCamUtils/src/logger/logger.h" AkVCam::ObjectInterface::ObjectInterface(): diff --git a/cmio/VirtualCamera/src/objectproperties.cpp b/cmio/VirtualCamera/src/objectproperties.cpp index 371f79c..7a32c18 100644 --- a/cmio/VirtualCamera/src/objectproperties.cpp +++ b/cmio/VirtualCamera/src/objectproperties.cpp @@ -20,7 +20,7 @@ #include #include "object.h" -#include "utils.h" +#include "PlatformUtils/src/utils.h" #include "VCamUtils/src/image/videoformat.h" namespace AkVCam diff --git a/cmio/VirtualCamera/src/plugin.cpp b/cmio/VirtualCamera/src/plugin.cpp index af6c7c3..0513310 100644 --- a/cmio/VirtualCamera/src/plugin.cpp +++ b/cmio/VirtualCamera/src/plugin.cpp @@ -18,8 +18,8 @@ */ #include "plugin.h" -#include "utils.h" #include "PlatformUtils/src/preferences.h" +#include "PlatformUtils/src/utils.h" #include "VCamUtils/src/ipcbridge.h" #include "VCamUtils/src/logger/logger.h" diff --git a/cmio/VirtualCamera/src/plugininterface.cpp b/cmio/VirtualCamera/src/plugininterface.cpp index 52180bf..8f27516 100644 --- a/cmio/VirtualCamera/src/plugininterface.cpp +++ b/cmio/VirtualCamera/src/plugininterface.cpp @@ -23,9 +23,9 @@ #include #include "plugininterface.h" -#include "utils.h" #include "Assistant/src/assistantglobals.h" #include "PlatformUtils/src/preferences.h" +#include "PlatformUtils/src/utils.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/ipcbridge.h" #include "VCamUtils/src/logger/logger.h" @@ -163,11 +163,9 @@ AkVCam::PluginInterface::PluginInterface(): this->d->m_ipcBridge.connectDeviceRemoved(this, &PluginInterface::deviceRemoved); this->d->m_ipcBridge.connectDevicesUpdated(this, &PluginInterface::devicesUpdated); this->d->m_ipcBridge.connectFrameReady(this, &PluginInterface::frameReady); + this->d->m_ipcBridge.connectPictureChanged(this, &PluginInterface::pictureChanged); this->d->m_ipcBridge.connectBroadcastingChanged(this, &PluginInterface::setBroadcasting); - this->d->m_ipcBridge.connectMirrorChanged(this, &PluginInterface::setMirror); - this->d->m_ipcBridge.connectScalingChanged(this, &PluginInterface::setScaling); - this->d->m_ipcBridge.connectAspectRatioChanged(this, &PluginInterface::setAspectRatio); - this->d->m_ipcBridge.connectSwapRgbChanged(this, &PluginInterface::setSwapRgb); + this->d->m_ipcBridge.connectControlsChanged(this, &PluginInterface::controlsChanged); } AkVCam::PluginInterface::~PluginInterface() @@ -318,6 +316,16 @@ void AkVCam::PluginInterface::frameReady(void *userData, device->frameReady(frame); } +void AkVCam::PluginInterface::pictureChanged(void *userData, + const std::string &picture) +{ + AkLogFunction(); + auto self = reinterpret_cast(userData); + + for (auto device: self->m_devices) + device->setPicture(picture); +} + void AkVCam::PluginInterface::setBroadcasting(void *userData, const std::string &deviceId, const std::string &broadcaster) @@ -332,53 +340,31 @@ void AkVCam::PluginInterface::setBroadcasting(void *userData, device->setBroadcasting(broadcaster); } -void AkVCam::PluginInterface::setMirror(void *userData, - const std::string &deviceId, - bool horizontalMirror, - bool verticalMirror) +void AkVCam::PluginInterface::controlsChanged(void *userData, + const std::string &deviceId, + const std::map &controls) { AkLogFunction(); + AkLogInfo() << "Device: " << deviceId << std::endl; auto self = reinterpret_cast(userData); for (auto device: self->m_devices) - if (device->deviceId() == deviceId) - device->setMirror(horizontalMirror, verticalMirror); -} + if (device->deviceId() == deviceId) { + if (controls.count("hflip")) + device->setHorizontalMirror(controls.at("hflip")); -void AkVCam::PluginInterface::setScaling(void *userData, - const std::string &deviceId, - Scaling scaling) -{ - AkLogFunction(); - auto self = reinterpret_cast(userData); + if (controls.count("vflip")) + device->setHorizontalMirror(controls.at("vflip")); - for (auto device: self->m_devices) - if (device->deviceId() == deviceId) - device->setScaling(scaling); -} + if (controls.count("scaling")) + device->setScaling(Scaling(controls.at("scaling"))); -void AkVCam::PluginInterface::setAspectRatio(void *userData, - const std::string &deviceId, - AspectRatio aspectRatio) -{ - AkLogFunction(); - auto self = reinterpret_cast(userData); + if (controls.count("aspect_ratio")) + device->setAspectRatio(AspectRatio(controls.at("aspect_ratio"))); - 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(userData); - - for (auto device: self->m_devices) - if (device->deviceId() == deviceId) - device->setSwapRgb(swap); + if (controls.count("swap_rgb")) + device->setSwapRgb(controls.at("swap_rgb")); + } } void AkVCam::PluginInterface::addListener(void *userData, @@ -412,6 +398,13 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId, device->connectRemoveListener(this, &PluginInterface::removeListener); this->m_devices.push_back(device); + auto cameraIndex = Preferences::cameraFromPath(deviceId); + auto hflip = Preferences::cameraControlValue(cameraIndex, "hflip"); + auto vflip = Preferences::cameraControlValue(cameraIndex, "vflip"); + auto scaling = Preferences::cameraControlValue(cameraIndex, "scaling"); + auto aspectRatio = Preferences::cameraControlValue(cameraIndex, "aspect_ratio"); + auto swapRgb = Preferences::cameraControlValue(cameraIndex, "swap_rgb"); + // Define device properties. device->properties().setProperty(kCMIOObjectPropertyName, description); @@ -469,11 +462,11 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId, } device->setBroadcasting(this->d->m_ipcBridge.broadcaster(deviceId)); - device->setMirror(this->d->m_ipcBridge.isHorizontalMirrored(deviceId), - this->d->m_ipcBridge.isVerticalMirrored(deviceId)); - device->setScaling(this->d->m_ipcBridge.scalingMode(deviceId)); - device->setAspectRatio(this->d->m_ipcBridge.aspectRatioMode(deviceId)); - device->setSwapRgb(this->d->m_ipcBridge.swapRgb(deviceId)); + device->setHorizontalMirror(hflip); + device->setVerticalMirror(vflip); + device->setScaling(Scaling(scaling)); + device->setAspectRatio(AspectRatio(aspectRatio)); + device->setSwapRgb(swapRgb); return true; @@ -509,11 +502,17 @@ void AkVCam::PluginInterfacePrivate::updateDevices() { for (auto &device: this->self->m_devices) { device->setBroadcasting(this->m_ipcBridge.broadcaster(device->deviceId())); - device->setMirror(this->m_ipcBridge.isHorizontalMirrored(device->deviceId()), - this->m_ipcBridge.isVerticalMirrored(device->deviceId())); - device->setScaling(this->m_ipcBridge.scalingMode(device->deviceId())); - device->setAspectRatio(this->m_ipcBridge.aspectRatioMode(device->deviceId())); - device->setSwapRgb(this->m_ipcBridge.swapRgb(device->deviceId())); + auto cameraIndex = Preferences::cameraFromPath(device->deviceId()); + auto hflip = Preferences::cameraControlValue(cameraIndex, "hflip"); + auto vflip = Preferences::cameraControlValue(cameraIndex, "vflip"); + auto scaling = Preferences::cameraControlValue(cameraIndex, "scaling"); + auto aspectRatio = Preferences::cameraControlValue(cameraIndex, "aspect_ratio"); + auto swapRgb = Preferences::cameraControlValue(cameraIndex, "swap_rgb"); + device->setHorizontalMirror(hflip); + device->setVerticalMirror(vflip); + device->setScaling(Scaling(scaling)); + device->setAspectRatio(AspectRatio(aspectRatio)); + device->setSwapRgb(swapRgb); } } diff --git a/cmio/VirtualCamera/src/plugininterface.h b/cmio/VirtualCamera/src/plugininterface.h index 3f72b18..cdfd206 100644 --- a/cmio/VirtualCamera/src/plugininterface.h +++ b/cmio/VirtualCamera/src/plugininterface.h @@ -58,22 +58,14 @@ namespace AkVCam static void frameReady(void *userData, const std::string &deviceId, const VideoFrame &frame); + static void pictureChanged(void *userData, + const std::string &picture); static void setBroadcasting(void *userData, const std::string &deviceId, const std::string &broadcaster); - static void setMirror(void *userData, - const std::string &deviceId, - bool horizontalMirror, - bool verticalMirror); - static void setScaling(void *userData, - const std::string &deviceId, - Scaling scaling); - static void setAspectRatio(void *userData, - const std::string &deviceId, - AspectRatio aspectRatio); - static void setSwapRgb(void *userData, - const std::string &deviceId, - bool swap); + static void controlsChanged(void *userData, + const std::string &deviceId, + const std::map &controls); static void addListener(void *userData, const std::string &deviceId); static void removeListener(void *userData, diff --git a/cmio/VirtualCamera/src/stream.cpp b/cmio/VirtualCamera/src/stream.cpp index d1302b1..754376e 100644 --- a/cmio/VirtualCamera/src/stream.cpp +++ b/cmio/VirtualCamera/src/stream.cpp @@ -17,15 +17,18 @@ * Web-Site: http://webcamoid.github.io/ */ +#include #include -#include +#include +#include #include +#include #include #include "stream.h" #include "clock.h" -#include "utils.h" #include "PlatformUtils/src/preferences.h" +#include "PlatformUtils/src/utils.h" #include "VCamUtils/src/image/videoformat.h" #include "VCamUtils/src/image/videoframe.h" #include "VCamUtils/src/logger/logger.h" @@ -78,7 +81,7 @@ AkVCam::Stream::Stream(bool registerObject, if (!picture.empty()) { std::wstring_convert> cv; - this->d->m_testFrame.load(cv.to_bytes(picture)); + this->d->m_testFrame = loadPicture(cv.to_bytes(picture)); } this->d->m_clock = @@ -158,6 +161,20 @@ OSStatus AkVCam::Stream::registerObject(bool regist) return status; } +void AkVCam::Stream::setPicture(const std::string &picture) +{ + AkLogFunction(); + AkLogDebug() << "Picture: " << picture; + this->d->m_testFrame = loadPicture(picture); + this->d->updateTestFrame(); + this->d->m_mutex.lock(); + + if (this->d->m_broadcaster.empty()) + this->d->m_currentFrame = this->d->m_testFrameAdapted; + + this->d->m_mutex.unlock(); +} + void AkVCam::Stream::setBridge(IpcBridge *bridge) { this->d->m_bridge = bridge; @@ -307,15 +324,24 @@ void AkVCam::Stream::setBroadcasting(const std::string &broadcaster) this->d->m_mutex.unlock(); } -void AkVCam::Stream::setMirror(bool horizontalMirror, bool verticalMirror) +void AkVCam::Stream::setHorizontalMirror(bool horizontalMirror) { AkLogFunction(); - if (this->d->m_horizontalMirror == horizontalMirror - && this->d->m_verticalMirror == verticalMirror) + if (this->d->m_horizontalMirror == horizontalMirror) return; this->d->m_horizontalMirror = horizontalMirror; + this->d->updateTestFrame(); +} + +void AkVCam::Stream::setVerticalMirror(bool verticalMirror) +{ + AkLogFunction(); + + if (this->d->m_verticalMirror == verticalMirror) + return; + this->d->m_verticalMirror = verticalMirror; this->d->updateTestFrame(); } diff --git a/cmio/VirtualCamera/src/stream.h b/cmio/VirtualCamera/src/stream.h index b246bd8..21f65e2 100644 --- a/cmio/VirtualCamera/src/stream.h +++ b/cmio/VirtualCamera/src/stream.h @@ -54,8 +54,10 @@ namespace AkVCam void serverStateChanged(IpcBridge::ServerState state); void frameReady(const VideoFrame &frame); + void setPicture(const std::string &picture); void setBroadcasting(const std::string &broadcaster); - void setMirror(bool horizontalMirror, bool verticalMirror); + void setHorizontalMirror(bool horizontalMirror); + void setVerticalMirror(bool verticalMirror); void setScaling(Scaling scaling); void setAspectRatio(AspectRatio aspectRatio); void setSwapRgb(bool swap); diff --git a/cmio/VirtualCamera/src/utils.cpp b/cmio/VirtualCamera/src/utils.cpp deleted file mode 100644 index 4ca472f..0000000 --- a/cmio/VirtualCamera/src/utils.cpp +++ /dev/null @@ -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 . - * - * Web-Site: http://webcamoid.github.io/ - */ - -#include -#include -#include - -#include "utils.h" - -inline const std::map *formatsTable() -{ - static const std::map 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(&uuid1); - auto puuid2 = reinterpret_cast(&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(&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); -} diff --git a/cmio/VirtualCamera/src/utils.h b/cmio/VirtualCamera/src/utils.h deleted file mode 100644 index a082b4b..0000000 --- a/cmio/VirtualCamera/src/utils.h +++ /dev/null @@ -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 . - * - * Web-Site: http://webcamoid.github.io/ - */ - -#ifndef UTILS_H -#define UTILS_H - -#include -#include -#include - -#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