diff --git a/Manager/Manager.pro b/Manager/Manager.pro index 409606d..b2b779c 100644 --- a/Manager/Manager.pro +++ b/Manager/Manager.pro @@ -59,6 +59,7 @@ win32: LIBS += \ -lshell32 macx: LIBS += \ -L$${OUT_PWD}/../cmio/VCamIPC/$${BIN_DIR} -lVCamIPC \ + -L$${OUT_PWD}/../cmio/PlatformUtils/$${BIN_DIR} -lPlatformUtils \ -framework CoreFoundation \ -framework CoreMedia \ -framework CoreMediaIO \ diff --git a/Manager/src/cmdparser.cpp b/Manager/src/cmdparser.cpp index d99f74f..688e48b 100644 --- a/Manager/src/cmdparser.cpp +++ b/Manager/src/cmdparser.cpp @@ -18,12 +18,14 @@ */ #include +#include #include #include #include -#include #include "cmdparser.h" +#include "VCamUtils/src/ipcbridge.h" +#include "VCamUtils/src/image/videoformat.h" #define AKVCAM_BIND_FUNC(member) \ std::bind(&member, this->d, std::placeholders::_1, std::placeholders::_2) @@ -49,6 +51,8 @@ namespace AkVCam { { public: std::vector m_commands; + IpcBridge m_ipcBridge; + bool m_parseable {false}; std::string basename(const std::string &path); void printFlags(const std::vector &cmdFlags, @@ -57,7 +61,10 @@ 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); std::string fill(const std::string &str, size_t maxSize); + std::wstring fill(const std::wstring &str, size_t maxSize); std::string join(const StringVector &strings, const std::string &separator); CmdParserCommand *parserCommand(const std::string &command); @@ -66,6 +73,9 @@ namespace AkVCam { bool containsFlag(const StringMap &flags, const std::string &command, const std::string &flagAlias); + std::string flagValue(const StringMap &flags, + const std::string &command, + const std::string &flagAlias); int defaultHandler(const StringMap &flags, const StringVector &args); int showHelp(const StringMap &flags, const StringVector &args); @@ -82,6 +92,7 @@ namespace AkVCam { int addFormat(const StringMap &flags, const StringVector &args); int removeFormat(const StringMap &flags, const StringVector &args); int update(const StringMap &flags, const StringVector &args); + int loadSettings(const StringMap &flags, const StringVector &args); int showConnections(const StringMap &flags, const StringVector &args); int connectDevices(const StringMap &flags, @@ -93,6 +104,8 @@ namespace AkVCam { int writeOption(const StringMap &flags, const StringVector &args); int showClients(const StringMap &flags, const StringVector &args); }; + + std::string operator *(const std::string &str, size_t n); } AkVCam::CmdParser::CmdParser() @@ -155,13 +168,17 @@ AkVCam::CmdParser::CmdParser() "INDEX", "Add format at INDEX."); this->addCommand("remove-format", - "INDEX", + "DEVICE INDEX", "Remove device format.", AKVCAM_BIND_FUNC(CmdParserPrivate::removeFormat)); this->addCommand("update", "", "Update devices.", AKVCAM_BIND_FUNC(CmdParserPrivate::update)); + this->addCommand("load", + "SETTINGS.INI", + "Create devices from a setting file.", + AKVCAM_BIND_FUNC(CmdParserPrivate::loadSettings)); this->addCommand("connections", "[DEVICE]", "Show device connections.", @@ -190,10 +207,12 @@ AkVCam::CmdParser::CmdParser() "", "Show clients using the camera.", AKVCAM_BIND_FUNC(CmdParserPrivate::showClients)); + this->d->m_ipcBridge.connectService(); } AkVCam::CmdParser::~CmdParser() { + this->d->m_ipcBridge.disconnectService(); delete this->d; } @@ -416,6 +435,26 @@ size_t AkVCam::CmdParserPrivate::maxFlagsValueLength(const std::vector spaces(maxSize, ' '); + ss << str << std::wstring(spaces.data(), maxSize - str.size()); + + return ss.str(); +} + std::string AkVCam::CmdParserPrivate::join(const StringVector &strings, const std::string &separator) { @@ -491,19 +539,49 @@ bool AkVCam::CmdParserPrivate::containsFlag(const StringMap &flags, return false; } +std::string AkVCam::CmdParserPrivate::flagValue(const AkVCam::StringMap &flags, + const std::string &command, + const std::string &flagAlias) +{ + for (auto &cmd: this->m_commands) + if (cmd.command == command) { + for (auto &flag: cmd.flags) { + auto it = std::find(flag.flags.begin(), + flag.flags.end(), + flagAlias); + + if (it != flag.flags.end()) { + for (auto &f1: flags) + for (auto &f2: flag.flags) + if (f1.first == f2) + return f1.second; + + return {}; + } + } + + return {}; + } + + return {}; +} + int AkVCam::CmdParserPrivate::defaultHandler(const StringMap &flags, const StringVector &args) { if (flags.empty() || this->containsFlag(flags, "", "-h")) return this->showHelp(flags, args); + if (this->containsFlag(flags, "", "-p")) + this->m_parseable = true; + return 0; } int AkVCam::CmdParserPrivate::showHelp(const StringMap &flags, const StringVector &args) { - UNUSED(flags) + UNUSED(flags); std::cout << args[0] << " [OPTIONS...] COMMAND [COMMAND_OPTIONS...] ..." @@ -547,54 +625,378 @@ int AkVCam::CmdParserPrivate::showHelp(const StringMap &flags, int AkVCam::CmdParserPrivate::showDevices(const StringMap &flags, const StringVector &args) { + UNUSED(flags); + UNUSED(args); + + if (this->m_parseable) { + for (auto &device: this->m_ipcBridge.listDevices()) + std::cout << device << std::endl; + } else { + StringVector devicesColumn; + StringVector typesColumn; + WStringVector descriptionsColumn; + + devicesColumn.push_back("Device"); + typesColumn.push_back("Type"); + descriptionsColumn.push_back(L"Description"); + + for (auto &device: this->m_ipcBridge.listDevices()) { + devicesColumn.push_back(device); + typesColumn.push_back(this->m_ipcBridge.deviceType(device) == IpcBridge::DeviceTypeInput? + "Input": + "Output"); + descriptionsColumn.push_back(this->m_ipcBridge.description(device)); + } + + auto devicesColumnSize = this->maxStringLength(devicesColumn); + auto typesColumnSize = this->maxStringLength(typesColumn); + auto descriptionsColumnSize = this->maxStringLength(descriptionsColumn); + + std::cout << this->fill("Device", devicesColumnSize) + << " | " + << this->fill("Type", typesColumnSize) + << " | " + << this->fill("Description", descriptionsColumnSize) + << std::endl; + std::cout << std::string("-") + * (devicesColumnSize + + typesColumnSize + + descriptionsColumnSize + + 6) << std::endl; + + for (auto &device: this->m_ipcBridge.listDevices()) { + std::string type = + this->m_ipcBridge.deviceType(device) == IpcBridge::DeviceTypeInput? + "Input": + "Output"; + std::cout << this->fill(device, devicesColumnSize) + << " | " + << this->fill(type, typesColumnSize) + << " | "; + std::wcout << this->fill(this->m_ipcBridge.description(device), + descriptionsColumnSize) + << std::endl; + } + } + return 0; } int AkVCam::CmdParserPrivate::addDevice(const StringMap &flags, const StringVector &args) { + if (args.size() < 2) { + std::cerr << "Device description not provided." << std::endl; + + return -1; + } + + IpcBridge::DeviceType type = + this->containsFlag(flags, "add-device", "-i")? + IpcBridge::DeviceTypeInput: + IpcBridge::DeviceTypeOutput; + + std::wstring_convert> cv; + auto deviceId = this->m_ipcBridge.addDevice(cv.from_bytes(args[1]), type); + + if (deviceId.empty()) { + std::cerr << "Failed to create device." << std::endl; + + return -1; + } + + if (this->m_parseable) + std::cout << deviceId << std::endl; + else + std::cout << "Device created as " << deviceId << std::endl; + return 0; } int AkVCam::CmdParserPrivate::removeDevice(const StringMap &flags, const StringVector &args) { + UNUSED(flags); + + if (args.size() < 2) { + std::cerr << "Device not provided." << std::endl; + + return -1; + } + + auto devices = this->m_ipcBridge.listDevices(); + auto it = std::find(devices.begin(), devices.end(), args[1]); + + if (it == devices.end()) { + std::cerr << "Device not doesn't exists." << std::endl; + + return -1; + } + + this->m_ipcBridge.removeDevice(args[1]); + return 0; } int AkVCam::CmdParserPrivate::showDeviceType(const StringMap &flags, const StringVector &args) { + UNUSED(flags); + + if (args.size() < 2) { + std::cerr << "Device not provided." << std::endl; + + return -1; + } + + auto devices = this->m_ipcBridge.listDevices(); + auto it = std::find(devices.begin(), devices.end(), args[1]); + + if (it == devices.end()) { + std::cerr << "Device not doesn't exists." << std::endl; + + return -1; + } + + if (this->m_ipcBridge.deviceType(args[1]) == IpcBridge::DeviceTypeInput) + std::cout << "Input" << std::endl; + else + std::cout << "Output" << std::endl; + return 0; } int AkVCam::CmdParserPrivate::showDeviceDescription(const StringMap &flags, const StringVector &args) { + UNUSED(flags); + + if (args.size() < 2) { + std::cerr << "Device not provided." << std::endl; + + return -1; + } + + auto devices = this->m_ipcBridge.listDevices(); + auto it = std::find(devices.begin(), devices.end(), args[1]); + + if (it == devices.end()) { + std::cerr << "Device not doesn't exists." << std::endl; + + return -1; + } + + std::wcout << this->m_ipcBridge.description(args[1]) << std::endl; + return 0; } int AkVCam::CmdParserPrivate::showSupportedFormats(const StringMap &flags, const StringVector &args) { + UNUSED(args); + + auto type = + this->containsFlag(flags, "supported-formats", "-i")? + IpcBridge::DeviceTypeInput: + IpcBridge::DeviceTypeOutput; + auto formats = this->m_ipcBridge.supportedPixelFormats(type); + + if (!this->m_parseable) { + if (type == IpcBridge::DeviceTypeInput) + std::cout << "Input formats:" << std::endl; + else + std::cout << "Output formats:" << std::endl; + + std::cout << std::endl; + } + + for (auto &format: formats) + std::cout << VideoFormat::stringFromFourcc(format) << std::endl; + return 0; } int AkVCam::CmdParserPrivate::showFormats(const StringMap &flags, const StringVector &args) { + UNUSED(flags); + + if (args.size() < 2) { + std::cerr << "Device not provided." << std::endl; + + return -1; + } + + auto devices = this->m_ipcBridge.listDevices(); + auto it = std::find(devices.begin(), devices.end(), args[1]); + + if (it == devices.end()) { + std::cerr << "Device not doesn't exists." << std::endl; + + return -1; + } + + if (this->m_parseable) { + for (auto &format: this->m_ipcBridge.formats(args[1])) + std::cout << VideoFormat::stringFromFourcc(format.fourcc()) + << ' ' + << format.width() + << ' ' + << format.height() + << ' ' + << format.minimumFrameRate().num() + << ' ' + << format.minimumFrameRate().den() + << std::endl; + } else { + int i = 0; + + for (auto &format: this->m_ipcBridge.formats(args[1])) { + std::cout << i + << ": " + << VideoFormat::stringFromFourcc(format.fourcc()) + << ' ' + << format.width() + << 'x' + << format.height() + << ' ' + << format.minimumFrameRate().num() + << '/' + << format.minimumFrameRate().den() + << " FPS" + << std::endl; + i++; + } + } + return 0; } int AkVCam::CmdParserPrivate::addFormat(const StringMap &flags, const StringVector &args) { + if (args.size() < 6) { + std::cerr << "Not enough arguments." << std::endl; + + return -1; + } + + auto deviceId = args[1]; + auto devices = this->m_ipcBridge.listDevices(); + auto dit = std::find(devices.begin(), devices.end(), args[1]); + + if (dit == devices.end()) { + std::cerr << "Device not doesn't exists." << std::endl; + + return -1; + } + + auto format = VideoFormat::fourccFromString(args[2]); + + if (!format) { + std::cerr << "Invalid pixel format." << std::endl; + + return -1; + } + + auto type = this->m_ipcBridge.deviceType(deviceId); + auto formats = this->m_ipcBridge.supportedPixelFormats(type); + auto fit = std::find(formats.begin(), formats.end(), format); + + if (fit == formats.end()) { + if (type == IpcBridge::DeviceTypeInput) + std::cerr << "Input format not supported." << std::endl; + else + std::cerr << "Output format not supported." << std::endl; + + return -1; + } + + char *p = nullptr; + auto width = strtoul(args[3].c_str(), &p, 10); + + if (*p) { + std::cerr << "Width must be an unsigned integer." << std::endl; + + return -1; + } + + p = nullptr; + auto height = strtoul(args[4].c_str(), &p, 10); + + if (*p) { + std::cerr << "Height must be an unsigned integer." << std::endl; + + return -1; + } + + Fraction fps(args[5]); + + if (fps.num() < 1 || fps.den() < 1) { + std::cerr << "Invalid frame rate." << std::endl; + + return -1; + } + + auto indexStr = this->flagValue(flags, "add-format", "-i"); + int index = -1; + + if (!indexStr.empty()) { + p = nullptr; + index = strtoul(indexStr.c_str(), &p, 10); + + if (*p) { + std::cerr << "Index must be an unsigned integer." << std::endl; + + return -1; + } + } + + VideoFormat fmt(format, width, height, {fps}); + this->m_ipcBridge.addFormat(deviceId, fmt, index); + return 0; } int AkVCam::CmdParserPrivate::removeFormat(const StringMap &flags, const StringVector &args) { + if (args.size() < 3) { + std::cerr << "Not enough arguments." << std::endl; + + return -1; + } + + auto deviceId = args[1]; + auto devices = this->m_ipcBridge.listDevices(); + auto dit = std::find(devices.begin(), devices.end(), args[1]); + + if (dit == devices.end()) { + std::cerr << "Device not doesn't exists." << std::endl; + + return -1; + } + + char *p = nullptr; + auto index = strtoul(args[2].c_str(), &p, 10); + + if (*p) { + std::cerr << "Index must be an unsigned integer." << std::endl; + + return -1; + } + + auto formats = this->m_ipcBridge.formats(deviceId); + + if (index >= formats.size()) { + std::cerr << "Index is out of range." << std::endl; + + return -1; + } + + this->m_ipcBridge.removeFormat(deviceId, index); + return 0; } @@ -604,6 +1006,12 @@ int AkVCam::CmdParserPrivate::update(const StringMap &flags, return 0; } +int AkVCam::CmdParserPrivate::loadSettings(const AkVCam::StringMap &flags, + const AkVCam::StringVector &args) +{ + return 0; +} + int AkVCam::CmdParserPrivate::showConnections(const StringMap &flags, const StringVector &args) { @@ -645,3 +1053,13 @@ int AkVCam::CmdParserPrivate::showClients(const StringMap &flags, { return 0; } + +std::string AkVCam::operator *(const std::string &str, size_t n) +{ + std::stringstream ss; + + for (size_t i = 0; i < n; i++) + ss << str; + + return ss.str(); +} diff --git a/Manager/src/cmdparser.h b/Manager/src/cmdparser.h index f3c5dfb..f2ec38d 100644 --- a/Manager/src/cmdparser.h +++ b/Manager/src/cmdparser.h @@ -27,6 +27,7 @@ namespace AkVCam { class CmdParserPrivate; using StringVector = std::vector; + using WStringVector = std::vector; using StringMap = std::map; using ProgramOptionsFunc = std::function; diff --git a/VCamUtils/src/image/videoformat.cpp b/VCamUtils/src/image/videoformat.cpp index 1497ab5..a473754 100644 --- a/VCamUtils/src/image/videoformat.cpp +++ b/VCamUtils/src/image/videoformat.cpp @@ -424,7 +424,7 @@ size_t AkVCam::VideoFormatGlobals::offsetNV(size_t plane, size_t width, size_t h size_t AkVCam::VideoFormatGlobals::byplNV(size_t plane, size_t width) { - UNUSED(plane) + UNUSED(plane); return align32(size_t(width)); } diff --git a/VCamUtils/src/image/videoframe.cpp b/VCamUtils/src/image/videoframe.cpp index f8a9a84..065509d 100644 --- a/VCamUtils/src/image/videoframe.cpp +++ b/VCamUtils/src/image/videoframe.cpp @@ -967,7 +967,7 @@ uint8_t AkVCam::VideoFramePrivate::rgb_v(int r, int g, int b) uint8_t AkVCam::VideoFramePrivate::yuv_r(int y, int u, int v) { - UNUSED(u) + UNUSED(u); int r = (298 * (y - 16) + 409 * (v - 128) + 128) >> 8; return uint8_t(bound(0, r, 255)); @@ -982,7 +982,7 @@ uint8_t AkVCam::VideoFramePrivate::yuv_g(int y, int u, int v) uint8_t AkVCam::VideoFramePrivate::yuv_b(int y, int u, int v) { - UNUSED(v) + UNUSED(v); int b = (298 * (y - 16) + 516 * (u - 128) + 128) >> 8; return uint8_t(bound(0, b, 255)); diff --git a/VCamUtils/src/ipcbridge.h b/VCamUtils/src/ipcbridge.h index e2b0ca3..3e1090d 100644 --- a/VCamUtils/src/ipcbridge.h +++ b/VCamUtils/src/ipcbridge.h @@ -57,6 +57,7 @@ namespace AkVCam const std::string &deviceId) AKVCAM_SIGNAL(DeviceRemoved, const std::string &deviceId) + AKVCAM_SIGNAL(DevicesUpdated, void *unused) AKVCAM_SIGNAL(ListenerAdded, const std::string &deviceId, const std::string &listener) @@ -126,10 +127,10 @@ namespace AkVCam std::wstring description(const std::string &deviceId) const; // Output pixel formats supported by the driver. - std::vector supportedOutputPixelFormats() const; + std::vector supportedPixelFormats(DeviceType type) const; // Default output pixel format of the driver. - PixelFormat defaultOutputPixelFormat() const; + PixelFormat defaultPixelFormat(DeviceType type) const; // Return supported formats for the device. std::vector formats(const std::string &deviceId) const; @@ -164,6 +165,13 @@ namespace AkVCam /* Server */ DeviceType deviceType(const std::string &deviceId); + std::string addDevice(const std::wstring &description, + DeviceType type); + void removeDevice(const std::string &deviceId); + void addFormat(const std::string &deviceId, + const VideoFormat &format, + int index=-1); + void removeFormat(const std::string &deviceId, int index); // Create a device definition. std::string deviceCreate(const std::wstring &description, @@ -185,6 +193,8 @@ namespace AkVCam // Remove all device definitions. bool destroyAllDevices(); + void updateDevices(); + // Start frame transfer to the device. bool deviceStart(const std::string &deviceId, const VideoFormat &format); diff --git a/VCamUtils/src/utils.h b/VCamUtils/src/utils.h index 7921ee0..d117978 100644 --- a/VCamUtils/src/utils.h +++ b/VCamUtils/src/utils.h @@ -25,7 +25,7 @@ #include #ifndef UNUSED - #define UNUSED(x) (void)(x); + #define UNUSED(x) (void)(x) #endif #define GLOBAL_STATIC(type, variableName) \ diff --git a/cmio/Assistant/src/assistant.cpp b/cmio/Assistant/src/assistant.cpp index 03f10dc..febe15e 100644 --- a/cmio/Assistant/src/assistant.cpp +++ b/cmio/Assistant/src/assistant.cpp @@ -84,6 +84,7 @@ namespace AkVCam void deviceCreate(xpc_connection_t client, xpc_object_t event); void deviceDestroyById(const std::string &deviceId); 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); @@ -156,6 +157,7 @@ AkVCam::AssistantPrivate::AssistantPrivate() {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) }, @@ -244,8 +246,8 @@ void AkVCam::AssistantPrivate::stopTimer() void AkVCam::AssistantPrivate::timerTimeout(CFRunLoopTimerRef timer, void *info) { - UNUSED(timer) - UNUSED(info) + UNUSED(timer); + UNUSED(info); AkLogFunction(); CFRunLoopStop(CFRunLoopGetMain()); @@ -388,7 +390,7 @@ void AkVCam::AssistantPrivate::removePortByName(const std::string &portName) void AkVCam::AssistantPrivate::removePort(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); this->removePortByName(xpc_dictionary_get_string(event, "port")); @@ -463,7 +465,7 @@ void AkVCam::AssistantPrivate::deviceDestroyById(const std::string &deviceId) void AkVCam::AssistantPrivate::deviceDestroy(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = xpc_dictionary_get_string(event, "device"); @@ -471,6 +473,19 @@ void AkVCam::AssistantPrivate::deviceDestroy(xpc_connection_t client, Preferences::removeCamera(deviceId); } +void AkVCam::AssistantPrivate::deviceUpdate(xpc_connection_t client, + xpc_object_t event) +{ + UNUSED(client); + AkLogFunction(); + auto notification = xpc_copy(event); + + for (auto &client: this->m_clients) + xpc_connection_send_message(client.second, notification); + + xpc_release(notification); +} + void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client, xpc_object_t event) { @@ -609,8 +624,9 @@ void AkVCam::AssistantPrivate::setSwapRgb(xpc_connection_t client, void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); + auto reply = xpc_dictionary_create_reply(event); bool ok = true; diff --git a/cmio/Assistant/src/assistantglobals.h b/cmio/Assistant/src/assistantglobals.h index 4727424..4658c98 100644 --- a/cmio/Assistant/src/assistantglobals.h +++ b/cmio/Assistant/src/assistantglobals.h @@ -40,6 +40,7 @@ #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 diff --git a/cmio/PlatformUtils/src/preferences.cpp b/cmio/PlatformUtils/src/preferences.cpp index 5f026ff..3497dca 100644 --- a/cmio/PlatformUtils/src/preferences.cpp +++ b/cmio/PlatformUtils/src/preferences.cpp @@ -275,6 +275,30 @@ void AkVCam::Preferences::sync() CFPreferencesAppSynchronize(PREFERENCES_ID); } +std::string AkVCam::Preferences::addDevice(const std::wstring &description, + AkVCam::IpcBridge::DeviceType type) +{ + AkLogFunction(); + auto path = createDevicePath(); + int cameraIndex = readInt("cameras"); + write("cameras", cameraIndex + 1); + write("cameras." + + std::to_string(cameraIndex) + + ".description", + description); + write("cameras." + + std::to_string(cameraIndex) + + ".isinput", + type == IpcBridge::DeviceTypeInput); + write("cameras." + + std::to_string(cameraIndex) + + ".path", + path); + sync(); + + return path; +} + std::string AkVCam::Preferences::addCamera(const std::wstring &description, const std::vector &formats, IpcBridge::DeviceType type) @@ -477,6 +501,73 @@ std::vector AkVCam::Preferences::cameraFormats(size_t camer return formats; } +void AkVCam::Preferences::cameraAddFormat(size_t cameraIndex, + const AkVCam::VideoFormat &format, + int index) +{ + AkLogFunction(); + auto formats = cameraFormats(cameraIndex); + + if (index < 0 || index > int(formats.size())) + index = formats.size(); + + formats.insert(formats.begin() + index, format); + write("cameras." + + std::to_string(cameraIndex) + + ".formats", + int(formats.size())); + + for (size_t i = 0; i < formats.size(); i++) { + auto &format = formats[i]; + auto prefix = "cameras." + + std::to_string(cameraIndex) + + ".formats." + + std::to_string(i); + auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); + write(prefix + ".format", formatStr); + write(prefix + ".width", format.width()); + write(prefix + ".height", format.height()); + write(prefix + ".fps", format.minimumFrameRate().toString()); + } + + sync(); +} + +void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index) +{ + AkLogFunction(); + + if (cameraIndex < 0 || !cameraIsInput(cameraIndex)) + return; + + auto formats = cameraFormats(cameraIndex); + + if (index < 0 || index >= int(formats.size())) + return; + + formats.erase(formats.begin() + index); + + write("cameras." + + std::to_string(cameraIndex) + + ".formats", + int(formats.size())); + + for (size_t i = 0; i < formats.size(); i++) { + auto &format = formats[i]; + auto prefix = "cameras." + + std::to_string(cameraIndex) + + ".formats." + + std::to_string(i); + auto formatStr = VideoFormat::stringFromFourcc(format.fourcc()); + write(prefix + ".format", formatStr); + write(prefix + ".width", format.width()); + write(prefix + ".height", format.height()); + write(prefix + ".fps", format.minimumFrameRate().toString()); + } + + sync(); +} + std::vector AkVCam::Preferences::cameraConnections(size_t cameraIndex) { AkLogFunction(); diff --git a/cmio/PlatformUtils/src/preferences.h b/cmio/PlatformUtils/src/preferences.h index 3abf709..e266642 100644 --- a/cmio/PlatformUtils/src/preferences.h +++ b/cmio/PlatformUtils/src/preferences.h @@ -54,6 +54,8 @@ namespace AkVCam void move(const std::string &keyFrom, const std::string &keyTo); void moveAll(const std::string &keyFrom, const std::string &keyTo); void sync(); + std::string addDevice(const std::wstring &description, + IpcBridge::DeviceType type); std::string addCamera(const std::wstring &description, const std::vector &formats, IpcBridge::DeviceType type); @@ -73,6 +75,10 @@ namespace AkVCam size_t formatsCount(size_t cameraIndex); VideoFormat cameraFormat(size_t cameraIndex, size_t formatIndex); std::vector cameraFormats(size_t cameraIndex); + void cameraAddFormat(size_t cameraIndex, + const VideoFormat &format, + int index); + void cameraRemoveFormat(size_t cameraIndex, int index); std::vector cameraConnections(size_t cameraIndex); std::vector cameraConnections(const std::string &path); } diff --git a/cmio/VCamIPC/src/ipcbridge.mm b/cmio/VCamIPC/src/ipcbridge.mm index d0b30cd..eac4c09 100644 --- a/cmio/VCamIPC/src/ipcbridge.mm +++ b/cmio/VCamIPC/src/ipcbridge.mm @@ -66,6 +66,7 @@ namespace AkVCam void isAlive(xpc_connection_t client, xpc_object_t event); void deviceCreate(xpc_connection_t client, xpc_object_t event); void deviceDestroy(xpc_connection_t client, xpc_object_t event); + void deviceUpdate(xpc_connection_t client, xpc_object_t event); void frameReady(xpc_connection_t client, xpc_object_t event); void setBroadcasting(xpc_connection_t client, xpc_object_t event); @@ -389,8 +390,10 @@ std::wstring AkVCam::IpcBridge::description(const std::string &deviceId) const return description; } -std::vector AkVCam::IpcBridge::supportedOutputPixelFormats() const +std::vector AkVCam::IpcBridge::supportedPixelFormats(DeviceType type) const { + UNUSED(type); + return { PixelFormatRGB32, PixelFormatRGB24, @@ -399,8 +402,10 @@ std::vector AkVCam::IpcBridge::supportedOutputPixelFormats( }; } -AkVCam::PixelFormat AkVCam::IpcBridge::defaultOutputPixelFormat() const +AkVCam::PixelFormat AkVCam::IpcBridge::defaultPixelFormat(DeviceType type) const { + UNUSED(type); + return PixelFormatYUY2; } @@ -699,6 +704,34 @@ AkVCam::IpcBridge::DeviceType AkVCam::IpcBridge::deviceType(const std::string &d DeviceTypeOutput; } +std::string AkVCam::IpcBridge::addDevice(const std::wstring &description, + DeviceType type) +{ + return Preferences::addDevice(description, type); +} + +void AkVCam::IpcBridge::removeDevice(const std::string &deviceId) +{ + Preferences::removeCamera(deviceId); +} + +void AkVCam::IpcBridge::addFormat(const std::string &deviceId, + const AkVCam::VideoFormat &format, + int index) +{ + AkLogFunction(); + Preferences::cameraAddFormat(Preferences::cameraFromPath(deviceId), + format, + index); +} + +void AkVCam::IpcBridge::removeFormat(const std::string &deviceId, int index) +{ + AkLogFunction(); + Preferences::cameraRemoveFormat(Preferences::cameraFromPath(deviceId), + index); +} + std::string AkVCam::IpcBridge::deviceCreate(const std::wstring &description, const std::vector &formats, DeviceType type) @@ -819,10 +852,24 @@ bool AkVCam::IpcBridge::destroyAllDevices() return true; } +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); + +} + bool AkVCam::IpcBridge::deviceStart(const std::string &deviceId, const VideoFormat &format) { - UNUSED(format) + UNUSED(format); AkLogFunction(); if (!this->d->m_serverMessagePort) @@ -1093,6 +1140,7 @@ AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self): {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)}, @@ -1149,7 +1197,7 @@ void AkVCam::IpcBridgePrivate::isAlive(xpc_connection_t client, void AkVCam::IpcBridgePrivate::deviceCreate(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string device = xpc_dictionary_get_string(event, "device"); @@ -1160,7 +1208,7 @@ void AkVCam::IpcBridgePrivate::deviceCreate(xpc_connection_t client, void AkVCam::IpcBridgePrivate::deviceDestroy(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string device = xpc_dictionary_get_string(event, "device"); @@ -1169,10 +1217,21 @@ void AkVCam::IpcBridgePrivate::deviceDestroy(xpc_connection_t client, AKVCAM_EMIT(bridge, DeviceRemoved, device) } +void AkVCam::IpcBridgePrivate::deviceUpdate(xpc_connection_t client, + xpc_object_t event) +{ + UNUSED(client); + UNUSED(event); + AkLogFunction(); + + for (auto bridge: this->m_bridges) + AKVCAM_EMIT(bridge, DevicesUpdated, nullptr) +} + void AkVCam::IpcBridgePrivate::frameReady(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = @@ -1207,7 +1266,7 @@ void AkVCam::IpcBridgePrivate::frameReady(xpc_connection_t client, void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = @@ -1222,7 +1281,7 @@ void AkVCam::IpcBridgePrivate::setBroadcasting(xpc_connection_t client, void AkVCam::IpcBridgePrivate::setMirror(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = @@ -1243,7 +1302,7 @@ void AkVCam::IpcBridgePrivate::setMirror(xpc_connection_t client, void AkVCam::IpcBridgePrivate::setScaling(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = @@ -1258,7 +1317,7 @@ void AkVCam::IpcBridgePrivate::setScaling(xpc_connection_t client, void AkVCam::IpcBridgePrivate::setAspectRatio(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = @@ -1273,7 +1332,7 @@ void AkVCam::IpcBridgePrivate::setAspectRatio(xpc_connection_t client, void AkVCam::IpcBridgePrivate::setSwapRgb(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = @@ -1287,7 +1346,7 @@ void AkVCam::IpcBridgePrivate::setSwapRgb(xpc_connection_t client, void AkVCam::IpcBridgePrivate::listenerAdd(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = xpc_dictionary_get_string(event, "device"); @@ -1300,7 +1359,7 @@ void AkVCam::IpcBridgePrivate::listenerAdd(xpc_connection_t client, void AkVCam::IpcBridgePrivate::listenerRemove(xpc_connection_t client, xpc_object_t event) { - UNUSED(client) + UNUSED(client); AkLogFunction(); std::string deviceId = xpc_dictionary_get_string(event, "device"); diff --git a/cmio/VirtualCamera/src/device.cpp b/cmio/VirtualCamera/src/device.cpp index 4094fe1..7fda526 100644 --- a/cmio/VirtualCamera/src/device.cpp +++ b/cmio/VirtualCamera/src/device.cpp @@ -308,7 +308,7 @@ OSStatus AkVCam::Device::stopStream(CMIOStreamID stream) OSStatus AkVCam::Device::processAVCCommand(CMIODeviceAVCCommand *ioAVCCommand) { - UNUSED(ioAVCCommand) + UNUSED(ioAVCCommand); AkLogFunction(); AkLogDebug() << "STUB" << std::endl; @@ -317,7 +317,7 @@ OSStatus AkVCam::Device::processAVCCommand(CMIODeviceAVCCommand *ioAVCCommand) OSStatus AkVCam::Device::processRS422Command(CMIODeviceRS422Command *ioRS422Command) { - UNUSED(ioRS422Command) + UNUSED(ioRS422Command); AkLogFunction(); AkLogDebug() << "STUB" << std::endl; diff --git a/cmio/VirtualCamera/src/object.cpp b/cmio/VirtualCamera/src/object.cpp index 7b319a1..628b90c 100644 --- a/cmio/VirtualCamera/src/object.cpp +++ b/cmio/VirtualCamera/src/object.cpp @@ -86,7 +86,7 @@ OSStatus AkVCam::Object::createObject() OSStatus AkVCam::Object::registerObject(bool regist) { - UNUSED(regist) + UNUSED(regist); return kCMIOHardwareNoError; } diff --git a/cmio/VirtualCamera/src/objectinterface.cpp b/cmio/VirtualCamera/src/objectinterface.cpp index 816453c..a4249b3 100644 --- a/cmio/VirtualCamera/src/objectinterface.cpp +++ b/cmio/VirtualCamera/src/objectinterface.cpp @@ -171,12 +171,12 @@ OSStatus AkVCam::ObjectInterface::setPropertyData(const CMIOObjectPropertyAddres UInt32 dataSize, const void *data) { + UNUSED(qualifierDataSize); + UNUSED(qualifierData); AkLogFunction(); AkLogInfo() << "Setting property " << enumToString(address->mSelector) << std::endl; - UNUSED(qualifierDataSize) - UNUSED(qualifierData) if (!this->m_properties.setProperty(address->mSelector, dataSize, diff --git a/cmio/VirtualCamera/src/objectproperties.cpp b/cmio/VirtualCamera/src/objectproperties.cpp index daec9ed..371f79c 100644 --- a/cmio/VirtualCamera/src/objectproperties.cpp +++ b/cmio/VirtualCamera/src/objectproperties.cpp @@ -580,13 +580,13 @@ bool AkVCam::ObjectProperties::getProperty(UInt32 property, memset(&callbacks, 0, sizeof(CFArrayCallBacks)); callbacks.retain = [] (CFAllocatorRef allocator, const void *value) -> const void * { - UNUSED(allocator) + UNUSED(allocator); return CFRetain(CMFormatDescriptionRef(value)); }; callbacks.release = [] (CFAllocatorRef allocator, const void *value) { - UNUSED(allocator) + UNUSED(allocator); CFRelease(CMFormatDescriptionRef(value)); }; diff --git a/cmio/VirtualCamera/src/plugin.cpp b/cmio/VirtualCamera/src/plugin.cpp index 324c634..50bfbe4 100644 --- a/cmio/VirtualCamera/src/plugin.cpp +++ b/cmio/VirtualCamera/src/plugin.cpp @@ -26,7 +26,7 @@ extern "C" void *akPluginMain(CFAllocatorRef allocator, CFUUIDRef requestedTypeUUID) { - UNUSED(allocator) + UNUSED(allocator); auto logLevel = AkVCam::Preferences::readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT); AkVCam::Logger::setLogLevel(logLevel); diff --git a/cmio/VirtualCamera/src/plugininterface.cpp b/cmio/VirtualCamera/src/plugininterface.cpp index 217daa7..821e80a 100644 --- a/cmio/VirtualCamera/src/plugininterface.cpp +++ b/cmio/VirtualCamera/src/plugininterface.cpp @@ -157,23 +157,11 @@ AkVCam::PluginInterface::PluginInterface(): this->d->m_ref = 0; this->d->m_reserved = 0; - auto homePath = std::string("/Users/") + getenv("USER"); - - std::stringstream ss; - ss << CMIO_DAEMONS_PATH << "/" << CMIO_ASSISTANT_NAME << ".plist"; - auto daemon = ss.str(); - - if (daemon[0] == '~') - daemon.replace(0, 1, homePath); - - struct stat fileInfo; - - if (stat(daemon.c_str(), &fileInfo) == 0) - this->d->m_ipcBridge.connectService(); - + this->d->m_ipcBridge.connectService(); this->d->m_ipcBridge.connectServerStateChanged(this, &PluginInterface::serverStateChanged); this->d->m_ipcBridge.connectDeviceAdded(this, &PluginInterface::deviceAdded); 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.connectBroadcastingChanged(this, &PluginInterface::setBroadcasting); this->d->m_ipcBridge.connectMirrorChanged(this, &PluginInterface::setMirror); @@ -296,6 +284,31 @@ void AkVCam::PluginInterface::deviceRemoved(void *userData, self->destroyDevice(deviceId); } +void AkVCam::PluginInterface::devicesUpdated(void *userData, void *unused) +{ + UNUSED(unused); + AkLogFunction(); + auto self = reinterpret_cast(userData); + std::vector devices; + + for (auto &device: self->m_devices) { + std::string deviceId; + device->properties().getProperty(kCMIODevicePropertyDeviceUID, + &deviceId); + devices.push_back(deviceId); + } + + for (auto &deviceId: devices) + self->destroyDevice(deviceId); + + for (auto &deviceId: self->d->m_ipcBridge.listDevices()) { + auto description = self->d->m_ipcBridge.description(deviceId); + auto formats = self->d->m_ipcBridge.formats(deviceId); + auto type = self->d->m_ipcBridge.deviceType(deviceId); + self->createDevice(deviceId, description, formats, type); + } +} + void AkVCam::PluginInterface::frameReady(void *userData, const std::string &deviceId, const VideoFrame &frame) diff --git a/cmio/VirtualCamera/src/plugininterface.h b/cmio/VirtualCamera/src/plugininterface.h index de825fd..93e2a39 100644 --- a/cmio/VirtualCamera/src/plugininterface.h +++ b/cmio/VirtualCamera/src/plugininterface.h @@ -54,6 +54,7 @@ namespace AkVCam const std::string &deviceId); static void deviceRemoved(void *userData, const std::string &deviceId); + static void devicesUpdated(void *userData, void *unused); static void frameReady(void *userData, const std::string &deviceId, const VideoFrame &frame); diff --git a/cmio/VirtualCamera/src/stream.cpp b/cmio/VirtualCamera/src/stream.cpp index 2fd5f30..4966343 100644 --- a/cmio/VirtualCamera/src/stream.cpp +++ b/cmio/VirtualCamera/src/stream.cpp @@ -410,7 +410,7 @@ OSStatus AkVCam::Stream::deckStop() OSStatus AkVCam::Stream::deckJog(SInt32 speed) { - UNUSED(speed) + UNUSED(speed); AkLogFunction(); AkLogDebug() << "STUB" << std::endl; @@ -419,8 +419,8 @@ OSStatus AkVCam::Stream::deckJog(SInt32 speed) OSStatus AkVCam::Stream::deckCueTo(Float64 frameNumber, Boolean playOnCue) { - UNUSED(frameNumber) - UNUSED(playOnCue) + UNUSED(frameNumber); + UNUSED(playOnCue); AkLogFunction(); AkLogDebug() << "STUB" << std::endl; @@ -480,7 +480,7 @@ void AkVCam::StreamPrivate::stopTimer() void AkVCam::StreamPrivate::streamLoop(CFRunLoopTimerRef timer, void *info) { - UNUSED(timer) + UNUSED(timer); AkLogFunction(); auto self = reinterpret_cast(info); diff --git a/dshow/VCamIPC/src/ipcbridge.cpp b/dshow/VCamIPC/src/ipcbridge.cpp index df15246..95f581a 100644 --- a/dshow/VCamIPC/src/ipcbridge.cpp +++ b/dshow/VCamIPC/src/ipcbridge.cpp @@ -335,7 +335,7 @@ std::wstring AkVCam::IpcBridge::description(const std::string &deviceId) const return {}; } -std::vector AkVCam::IpcBridge::supportedOutputPixelFormats() const +std::vector AkVCam::IpcBridge::supportedPixelFormats() const { return { PixelFormatRGB32, @@ -348,7 +348,7 @@ std::vector AkVCam::IpcBridge::supportedOutputPixelFormats( }; } -AkVCam::PixelFormat AkVCam::IpcBridge::defaultOutputPixelFormat() const +AkVCam::PixelFormat AkVCam::IpcBridge::defaultPixelFormat() const { return PixelFormatYUY2; }