Removed virtual input device and using standard input streaming instead. Removed unneeded options related to the input device. Added loglevel and placeholder picture options.

This commit is contained in:
Gonzalo Exequiel Pedone 2020-08-21 21:31:45 -03:00
parent 1cea274ccd
commit e8da748d30
No known key found for this signature in database
GPG key ID: B8B09E63E9B85BAF
15 changed files with 278 additions and 603 deletions

View file

@ -26,6 +26,8 @@
#include "cmdparser.h"
#include "VCamUtils/src/ipcbridge.h"
#include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h"
#include "VCamUtils/src/logger/logger.h"
#define AKVCAM_BIND_FUNC(member) \
std::bind(&member, this->d, std::placeholders::_1, std::placeholders::_2)
@ -79,8 +81,6 @@ namespace AkVCam {
int addDevice(const StringMap &flags, const StringVector &args);
int removeDevice(const StringMap &flags, const StringVector &args);
int removeDevices(const StringMap &flags, const StringVector &args);
int showDeviceType(const StringMap &flags,
const StringVector &args);
int showDeviceDescription(const StringMap &flags,
const StringVector &args);
int setDeviceDescription(const StringMap &flags,
@ -93,17 +93,14 @@ namespace AkVCam {
int removeFormats(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,
const StringVector &args);
int disconnectDevices(const StringMap &flags,
const StringVector &args);
int showBroadcasters(const StringMap &flags,
const StringVector &args);
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 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);
};
@ -129,12 +126,6 @@ AkVCam::CmdParser::CmdParser()
"DESCRIPTION",
"Add a new device.",
AKVCAM_BIND_FUNC(CmdParserPrivate::addDevice));
this->addFlags("add-device",
{"-i", "--input"},
"Add an input device.");
this->addFlags("add-device",
{"-o", "--output"},
"Add an output device.");
this->addCommand("remove-device",
"DEVICE",
"Remove a device.",
@ -143,10 +134,6 @@ AkVCam::CmdParser::CmdParser()
"",
"Remove all devices.",
AKVCAM_BIND_FUNC(CmdParserPrivate::removeDevices));
this->addCommand("type",
"DEVICE",
"Show device type.",
AKVCAM_BIND_FUNC(CmdParserPrivate::showDeviceType));
this->addCommand("description",
"DEVICE",
"Show device description.",
@ -193,22 +180,10 @@ AkVCam::CmdParser::CmdParser()
"SETTINGS.INI",
"Create devices from a setting file.",
AKVCAM_BIND_FUNC(CmdParserPrivate::loadSettings));
this->addCommand("connections",
"DEVICE",
"Show device connections.",
AKVCAM_BIND_FUNC(CmdParserPrivate::showConnections));
this->addCommand("connect",
"INPUT_DEVICE OUTPUTDEVICE [OUTPUT_DEVICE ...]",
"Connect devices.",
AKVCAM_BIND_FUNC(CmdParserPrivate::connectDevices));
this->addCommand("disconnect",
"INPUT_DEVICE OUTPUTDEVICE",
"Disconnect devices.",
AKVCAM_BIND_FUNC(CmdParserPrivate::disconnectDevices));
this->addCommand("broadcasters",
"DEVICE",
"Show devices that sending frames to the device.",
AKVCAM_BIND_FUNC(CmdParserPrivate::showBroadcasters));
this->addCommand("stream",
"DEVICE FORMAT WIDTH HEIGHT",
"Read frames from stdin and send them to the device.",
AKVCAM_BIND_FUNC(CmdParserPrivate::stream));
this->addCommand("controls",
"DEVICE",
"Show device controls.",
@ -221,6 +196,22 @@ AkVCam::CmdParser::CmdParser()
"DEVICE CONTROL VALUE",
"Write device control value.",
AKVCAM_BIND_FUNC(CmdParserPrivate::writeControl));
this->addCommand("picture",
"",
"Placeholder picture to show when no streaming.",
AKVCAM_BIND_FUNC(CmdParserPrivate::picture));
this->addCommand("set-picture",
"FILE",
"Set placeholder picture.",
AKVCAM_BIND_FUNC(CmdParserPrivate::setPicture));
this->addCommand("loglevel",
"",
"Show current debugging level.",
AKVCAM_BIND_FUNC(CmdParserPrivate::logLevel));
this->addCommand("set-loglevel",
"LEVEL",
"Set debugging level.",
AKVCAM_BIND_FUNC(CmdParserPrivate::setLogLevel));
this->addCommand("clients",
"",
"Show clients using the camera.",
@ -619,45 +610,30 @@ int AkVCam::CmdParserPrivate::showDevices(const StringMap &flags,
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: devices) {
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 << fill("Device", devicesColumnSize)
<< " | "
<< fill("Type", typesColumnSize)
<< " | "
<< fill("Description", descriptionsColumnSize)
<< std::endl;
std::cout << std::string("-")
* (devicesColumnSize
+ typesColumnSize
+ descriptionsColumnSize
+ 6) << std::endl;
+ 4) << std::endl;
for (auto &device: devices) {
std::string type =
this->m_ipcBridge.deviceType(device) == IpcBridge::DeviceTypeInput?
"Input":
"Output";
std::cout << fill(device, devicesColumnSize)
<< " | "
<< fill(type, typesColumnSize)
<< " | ";
std::wcout << fill(this->m_ipcBridge.description(device),
descriptionsColumnSize)
@ -671,19 +647,16 @@ int AkVCam::CmdParserPrivate::showDevices(const StringMap &flags,
int AkVCam::CmdParserPrivate::addDevice(const StringMap &flags,
const StringVector &args)
{
UNUSED(flags);
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<std::codecvt_utf8_utf16<wchar_t>> cv;
auto deviceId = this->m_ipcBridge.addDevice(cv.from_bytes(args[1]), type);
auto deviceId = this->m_ipcBridge.addDevice(cv.from_bytes(args[1]));
if (deviceId.empty()) {
std::cerr << "Failed to create device." << std::endl;
@ -738,35 +711,6 @@ int AkVCam::CmdParserPrivate::removeDevices(const AkVCam::StringMap &flags,
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 deviceId = args[1];
auto devices = this->m_ipcBridge.devices();
auto it = std::find(devices.begin(), devices.end(), deviceId);
if (it == devices.end()) {
std::cerr << "'" << deviceId << "' 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)
{
@ -827,12 +771,12 @@ int AkVCam::CmdParserPrivate::showSupportedFormats(const StringMap &flags,
auto type =
this->containsFlag(flags, "supported-formats", "-i")?
IpcBridge::DeviceTypeInput:
IpcBridge::DeviceTypeOutput;
IpcBridge::StreamTypeInput:
IpcBridge::StreamTypeOutput;
auto formats = this->m_ipcBridge.supportedPixelFormats(type);
if (!this->m_parseable) {
if (type == IpcBridge::DeviceTypeInput)
if (type == IpcBridge::StreamTypeInput)
std::cout << "Input formats:" << std::endl;
else
std::cout << "Output formats:" << std::endl;
@ -930,15 +874,11 @@ int AkVCam::CmdParserPrivate::addFormat(const StringMap &flags,
return -1;
}
auto type = this->m_ipcBridge.deviceType(deviceId);
auto formats = this->m_ipcBridge.supportedPixelFormats(type);
auto formats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
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;
std::cerr << "Format not supported." << std::endl;
return -1;
}
@ -1074,12 +1014,12 @@ int AkVCam::CmdParserPrivate::loadSettings(const AkVCam::StringMap &flags,
return 0;
}
int AkVCam::CmdParserPrivate::showConnections(const StringMap &flags,
const StringVector &args)
int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
if (args.size() < 2) {
if (args.size() < 5) {
std::cerr << "Not enough arguments." << std::endl;
return -1;
@ -1095,153 +1035,72 @@ int AkVCam::CmdParserPrivate::showConnections(const StringMap &flags,
return -1;
}
for (auto &connectedDevice: this->m_ipcBridge.connections(deviceId))
std::cout << connectedDevice << std::endl;
auto format = VideoFormat::fourccFromString(args[2]);
return 0;
}
int AkVCam::CmdParserPrivate::connectDevices(const StringMap &flags,
const StringVector &args)
{
UNUSED(flags);
if (args.size() < 3) {
std::cerr << "Not enough arguments." << std::endl;
if (!format) {
std::cerr << "Invalid pixel format." << std::endl;
return -1;
}
auto inputDevice = args[1];
auto devices = this->m_ipcBridge.devices();
auto it = std::find(devices.begin(), devices.end(), inputDevice);
auto formats = this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
auto fit = std::find(formats.begin(), formats.end(), format);
if (it == devices.end()) {
std::cerr << inputDevice << " doesn't exists." << std::endl;
if (fit == formats.end()) {
std::cerr << "Format not supported." << std::endl;
return -1;
}
if (this->m_ipcBridge.deviceType(inputDevice) != IpcBridge::DeviceTypeInput) {
std::cerr << inputDevice << " is not an input." << std::endl;
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;
}
auto outputDevices = this->m_ipcBridge.connections(inputDevice);
p = nullptr;
auto height = strtoul(args[4].c_str(), &p, 10);
for (size_t i = 2; i < args.size(); i++) {
auto &outputDevice = args[i];
auto it = std::find(devices.begin(), devices.end(), outputDevice);
if (it == devices.end()) {
std::cerr << outputDevice << " doesn't exists." << std::endl;
if (*p) {
std::cerr << "Height must be an unsigned integer." << std::endl;
return -1;
}
if (this->m_ipcBridge.deviceType(outputDevice) != IpcBridge::DeviceTypeOutput) {
std::cerr << outputDevice << " is not an output." << std::endl;
VideoFormat fmt(format, width, height, {{30, 1}});
if (!this->m_ipcBridge.deviceStart(deviceId, fmt)) {
std::cerr << "Can't start stream." << std::endl;
return -1;
}
auto cit = std::find(outputDevices.begin(),
outputDevices.end(),
outputDevice);
static bool exit = false;
auto signalHandler = [] (int) {
exit = true;
};
signal(SIGINT, signalHandler);
signal(SIGTERM, signalHandler);
if (cit == outputDevices.end())
outputDevices.push_back(outputDevice);
AkVCam::VideoFrame frame(fmt);
size_t bufferSize = 0;
do {
std::cin.read(reinterpret_cast<char *>(frame.data().data()
+ bufferSize),
frame.data().size() - bufferSize);
bufferSize += std::cin.gcount();
if (bufferSize == frame.data().size()) {
this->m_ipcBridge.write(deviceId, frame);
bufferSize = 0;
}
} while (!std::cin.eof() && !exit);
this->m_ipcBridge.setConnections(inputDevice, outputDevices);
return 0;
}
int AkVCam::CmdParserPrivate::disconnectDevices(const StringMap &flags,
const StringVector &args)
{
UNUSED(flags);
if (args.size() < 3) {
std::cerr << "Not enough arguments." << std::endl;
return -1;
}
auto inputDevice = args[1];
auto devices = this->m_ipcBridge.devices();
auto it = std::find(devices.begin(), devices.end(), inputDevice);
if (it == devices.end()) {
std::cerr << inputDevice << " doesn't exists." << std::endl;
return -1;
}
if (this->m_ipcBridge.deviceType(inputDevice) != IpcBridge::DeviceTypeInput) {
std::cerr << inputDevice << " is not an input." << std::endl;
return -1;
}
auto outputDevices = this->m_ipcBridge.connections(inputDevice);
auto &outputDevice = args[2];
auto dit = std::find(devices.begin(), devices.end(), outputDevice);
if (dit == devices.end()) {
std::cerr << outputDevice << " doesn't exists." << std::endl;
return -1;
}
if (this->m_ipcBridge.deviceType(outputDevice) != IpcBridge::DeviceTypeOutput) {
std::cerr << outputDevice << " is not an output." << std::endl;
return -1;
}
auto cit = std::find(outputDevices.begin(),
outputDevices.end(),
outputDevice);
if (cit == outputDevices.end())
outputDevices.push_back(outputDevice);
this->m_ipcBridge.setConnections(inputDevice, outputDevices);
return 0;
}
int AkVCam::CmdParserPrivate::showBroadcasters(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
if (args.size() < 2) {
std::cerr << "Not enough arguments." << std::endl;
return -1;
}
auto ouputDevice = args[1];
auto devices = this->m_ipcBridge.devices();
auto it = std::find(devices.begin(), devices.end(), ouputDevice);
if (it == devices.end()) {
std::cerr << ouputDevice << " doesn't exists." << std::endl;
return -1;
}
if (this->m_ipcBridge.deviceType(ouputDevice) != IpcBridge::DeviceTypeOutput) {
std::cerr << ouputDevice << " is not an output." << std::endl;
return -1;
}
std::cout << this->m_ipcBridge.broadcaster(ouputDevice) << std::endl;
this->m_ipcBridge.deviceStop(deviceId);
return 0;
}
@ -1264,6 +1123,73 @@ int AkVCam::CmdParserPrivate::writeControl(const StringMap &flags,
return 0;
}
int AkVCam::CmdParserPrivate::picture(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
UNUSED(args);
std::wcout << this->m_ipcBridge.picture() << std::endl;
return 0;
}
int AkVCam::CmdParserPrivate::setPicture(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
if (args.size() < 2) {
std::cerr << "Not enough arguments." << std::endl;
return -1;
}
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
this->m_ipcBridge.setPicture(cv.from_bytes(args[1]));
return 0;
}
int AkVCam::CmdParserPrivate::logLevel(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
UNUSED(args);
auto level = this->m_ipcBridge.logLevel();
if (this->m_parseable)
std::cout << level << std::endl;
else
std::cout << AkVCam::Logger::levelToString(level) << std::endl;
return 0;
}
int AkVCam::CmdParserPrivate::setLogLevel(const AkVCam::StringMap &flags,
const AkVCam::StringVector &args)
{
UNUSED(flags);
if (args.size() < 2) {
std::cerr << "Not enough arguments." << std::endl;
return -1;
}
auto levelStr = args[1];
char *p = nullptr;
auto level = strtol(levelStr.c_str(), &p, 10);
if (*p)
level = AkVCam::Logger::levelFromString(levelStr);
this->m_ipcBridge.setLogLevel(level);
return 0;
}
int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
const StringVector &args)
{

View file

@ -42,10 +42,10 @@ namespace AkVCam
ServerStateGone
};
enum DeviceType
enum StreamType
{
DeviceTypeOutput,
DeviceTypeInput
StreamTypeOutput,
StreamTypeInput
};
AKVCAM_SIGNAL(ServerStateChanged,
@ -104,6 +104,11 @@ namespace AkVCam
std::string driver() const;
bool setDriver(const std::string &driver);
std::wstring picture() const;
void setPicture(const std::wstring &picture);
int logLevel() const;
void setLogLevel(int logLevel);
// Configure method to be used for executing commands with elevated
// privileges.
std::vector<std::string> availableRootMethods() const;
@ -129,10 +134,10 @@ namespace AkVCam
const std::wstring &description);
// Output pixel formats supported by the driver.
std::vector<PixelFormat> supportedPixelFormats(DeviceType type) const;
std::vector<PixelFormat> supportedPixelFormats(StreamType type) const;
// Default output pixel format of the driver.
PixelFormat defaultPixelFormat(DeviceType type) const;
PixelFormat defaultPixelFormat(StreamType type) const;
// Return supported formats for the device.
std::vector<VideoFormat> formats(const std::string &deviceId) const;
@ -168,19 +173,13 @@ namespace AkVCam
/* Server */
DeviceType deviceType(const std::string &deviceId);
std::string addDevice(const std::wstring &description,
DeviceType type);
std::string addDevice(const std::wstring &description);
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);
void update();
std::vector<std::string> connections(const std::string &deviceId);
void setConnections(const std::string &deviceId,
const std::vector<std::string> &connectedDevices);
void updateDevices();
// Start frame transfer to the device.

View file

@ -37,7 +37,7 @@ namespace AkVCam
int logLevel {AKVCAM_LOGLEVEL_DEFAULT};
std::fstream stream;
static std::string logLevelString(int logLevel)
static const std::map<int, std::string> &logLevelStrMap()
{
static std::map<int, std::string> llsMap {
{AKVCAM_LOGLEVEL_DEFAULT , "default" },
@ -51,10 +51,7 @@ namespace AkVCam
{AKVCAM_LOGLEVEL_DEBUG , "debug" },
};
if (llsMap.count(logLevel) < 1)
return {};
return llsMap[logLevel];
return llsMap;
}
};
@ -115,7 +112,7 @@ std::string AkVCam::Logger::header(int logLevel, const std::string file, int lin
<< " ("
<< line
<< ")] "
<< LoggerPrivate::logLevelString(logLevel) << ": ";
<< levelToString(logLevel) << ": ";
return ss.str();
}
@ -139,3 +136,25 @@ std::ostream &AkVCam::Logger::log(int logLevel)
return loggerPrivate()->stream;
}
int AkVCam::Logger::levelFromString(const std::string &level)
{
auto &llsMap = LoggerPrivate::logLevelStrMap();
for (auto it = llsMap.begin(); it != llsMap.end(); it++)
if (it->second == level)
return it->first;
return AKVCAM_LOGLEVEL_DEFAULT;
}
std::string AkVCam::Logger::levelToString(int level)
{
auto &llsMap = LoggerPrivate::logLevelStrMap();
for (auto it = llsMap.begin(); it != llsMap.end(); it++)
if (it->first == level)
return it->second;
return {};
}

View file

@ -62,6 +62,8 @@ namespace AkVCam
void setLogLevel(int logLevel);
std::string header(int logLevel, const std::string file, int line);
std::ostream &log(int logLevel);
int levelFromString(const std::string &level);
std::string levelToString(int level);
}
}

View file

@ -39,16 +39,12 @@
namespace AkVCam
{
struct AssistantDevice
{
std::wstring description;
std::vector<VideoFormat> formats;
std::vector<std::string> connections;
std::string broadcaster;
std::vector<std::string> listeners;
IpcBridge::DeviceType type;
bool horizontalMirror {false};
bool verticalMirror {false};
Scaling scaling {ScalingFast};
@ -103,8 +99,6 @@ namespace AkVCam
void swapRgb(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 connections(xpc_connection_t client, xpc_object_t event);
void setConnections(xpc_connection_t client, xpc_object_t event);
};
}
@ -172,8 +166,6 @@ AkVCam::AssistantPrivate::AssistantPrivate()
{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_CONNECTIONS , AKVCAM_BIND_FUNC(AssistantPrivate::connections) },
{AKVCAM_ASSISTANT_MSG_SETCONNECTIONS , AKVCAM_BIND_FUNC(AssistantPrivate::setConnections) },
};
this->loadCameras();
@ -263,8 +255,6 @@ void AkVCam::AssistantPrivate::loadCameras()
this->m_deviceConfigs[path].description =
Preferences::cameraDescription(i);
this->m_deviceConfigs[path].formats = Preferences::cameraFormats(i);
this->m_deviceConfigs[path].connections =
Preferences::cameraConnections(i);
}
}
@ -419,12 +409,8 @@ void AkVCam::AssistantPrivate::deviceCreate(xpc_connection_t client,
formats.push_back(VideoFormat {fourcc, width, height, {frameRate}});
}
auto type = xpc_dictionary_get_bool(event, "isinput")?
IpcBridge::DeviceTypeInput:
IpcBridge::DeviceTypeOutput;
auto deviceId = Preferences::addCamera(description, formats, type);
auto deviceId = Preferences::addCamera(description, formats);
this->m_deviceConfigs[deviceId] = {};
this->m_deviceConfigs[deviceId].type = type;
this->m_deviceConfigs[deviceId].description = description;
this->m_deviceConfigs[deviceId].formats = formats;
@ -910,44 +896,3 @@ void AkVCam::AssistantPrivate::listenerRemove(xpc_connection_t client,
xpc_connection_send_message(client, reply);
xpc_release(reply);
}
void AkVCam::AssistantPrivate::connections(xpc_connection_t client,
xpc_object_t event)
{
AkLogFunction();
std::string deviceId = xpc_dictionary_get_string(event, "device");
auto listeners = xpc_array_create(nullptr, 0);
if (this->m_deviceConfigs.count(deviceId) > 0)
for (auto &listener: this->m_deviceConfigs[deviceId].connections) {
auto listenerObj = xpc_string_create(listener.c_str());
xpc_array_append_value(listeners, listenerObj);
}
AkLogInfo() << "Device: " << deviceId << std::endl;
AkLogInfo() << "Connections: " << xpc_array_get_count(listeners) << std::endl;
auto reply = xpc_dictionary_create_reply(event);
xpc_dictionary_set_value(reply, "connections", listeners);
xpc_connection_send_message(client, reply);
xpc_release(reply);
}
void AkVCam::AssistantPrivate::setConnections(xpc_connection_t client,
xpc_object_t event)
{
AkLogFunction();
std::string deviceId = xpc_dictionary_get_string(event, "device");
auto connectionsList = xpc_dictionary_get_array(event, "connections");
std::vector<std::string> connections;
for (size_t i = 0; i < xpc_array_get_count(connectionsList); i++)
connections.push_back(xpc_array_get_string(connectionsList, i));
this->m_deviceConfigs[deviceId].connections = connections;
AkLogInfo() << "Device: " << deviceId << std::endl;
AkLogInfo() << "Connections: " << connections.size() << std::endl;
auto reply = xpc_dictionary_create_reply(event);
xpc_dictionary_set_bool(reply, "status", true);
xpc_connection_send_message(client, reply);
xpc_release(reply);
}

View file

@ -30,8 +30,7 @@ GLOBAL_STATIC(AkVCam::Assistant, assistant)
int main(int argc, char **argv)
{
auto loglevel =
AkVCam::Preferences::readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT);
auto loglevel = AkVCam::Preferences::logLevel();
AkVCam::Logger::setLogLevel(loglevel);
AkLogDebug() << "Creating Service: " << CMIO_ASSISTANT_NAME << std::endl;
auto server =

View file

@ -282,8 +282,7 @@ void AkVCam::Preferences::sync()
CFPreferencesAppSynchronize(PREFERENCES_ID);
}
std::string AkVCam::Preferences::addDevice(const std::wstring &description,
AkVCam::IpcBridge::DeviceType type)
std::string AkVCam::Preferences::addDevice(const std::wstring &description)
{
AkLogFunction();
auto path = createDevicePath();
@ -293,10 +292,6 @@ std::string AkVCam::Preferences::addDevice(const std::wstring &description,
+ std::to_string(cameraIndex)
+ ".description",
description);
write("cameras."
+ std::to_string(cameraIndex)
+ ".isinput",
type == IpcBridge::DeviceTypeInput);
write("cameras."
+ std::to_string(cameraIndex)
+ ".path",
@ -307,16 +302,14 @@ std::string AkVCam::Preferences::addDevice(const std::wstring &description,
}
std::string AkVCam::Preferences::addCamera(const std::wstring &description,
const std::vector<VideoFormat> &formats,
IpcBridge::DeviceType type)
const std::vector<VideoFormat> &formats)
{
return addCamera("", description, formats, type);
return addCamera("", description, formats);
}
std::string AkVCam::Preferences::addCamera(const std::string &path,
const std::wstring &description,
const std::vector<VideoFormat> &formats,
IpcBridge::DeviceType type)
const std::vector<VideoFormat> &formats)
{
AkLogFunction();
@ -330,10 +323,6 @@ std::string AkVCam::Preferences::addCamera(const std::string &path,
+ std::to_string(cameraIndex)
+ ".description",
description);
write("cameras."
+ std::to_string(cameraIndex)
+ ".isinput",
type == IpcBridge::DeviceTypeInput);
write("cameras."
+ std::to_string(cameraIndex)
+ ".path",
@ -442,11 +431,6 @@ bool AkVCam::Preferences::cameraExists(const std::string &path)
return false;
}
bool AkVCam::Preferences::cameraIsInput(size_t cameraIndex)
{
return readBool("cameras." + std::to_string(cameraIndex) + ".isinput");
}
std::wstring AkVCam::Preferences::cameraDescription(size_t cameraIndex)
{
if (cameraIndex >= camerasCount())
@ -578,9 +562,6 @@ void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index)
{
AkLogFunction();
if (!cameraIsInput(cameraIndex))
return;
auto formats = cameraFormats(cameraIndex);
if (index < 0 || index >= int(formats.size()))
@ -609,74 +590,22 @@ void AkVCam::Preferences::cameraRemoveFormat(size_t cameraIndex, int index)
sync();
}
std::vector<std::string> AkVCam::Preferences::cameraConnections(size_t cameraIndex)
std::wstring AkVCam::Preferences::picture()
{
AkLogFunction();
std::vector<std::string> cameraConnections;
if (cameraIsInput(cameraIndex)) {
auto connections = readStringList("cameras."
+ std::to_string(cameraIndex)
+ ".connections");
for (auto &connection: connections) {
if (connection.empty())
continue;
size_t pos = 0;
auto outputIndex = std::stoi(connection, &pos);
if (pos == connection.size())
cameraConnections.push_back(cameraPath(outputIndex));
}
} else {
for (size_t i = 0; i < camerasCount(); i++)
if (cameraIsInput(i)) {
auto connections = readStringList("cameras."
+ std::to_string(i)
+ ".connections");
for (auto &connection: connections) {
if (connection.empty())
continue;
size_t pos = 0;
auto outputIndex = std::stoi(connection, &pos);
if (pos == connection.size() && size_t(outputIndex) == cameraIndex)
cameraConnections.push_back(cameraPath(i));
}
}
}
return cameraConnections;
return readWString("picture");
}
void AkVCam::Preferences::cameraSetConnections(size_t cameraIndex,
const std::vector<std::string> &connectedDevices)
void AkVCam::Preferences::setPicture(const std::wstring &picture)
{
AkLogFunction();
if (!cameraIsInput(cameraIndex))
return;
std::vector<std::string> connections;
for (auto &connection: connectedDevices) {
if (connection.empty())
continue;
auto outputIndex = cameraFromPath(connection);
if (outputIndex < 0)
continue;
if (cameraIsInput(outputIndex))
continue;
connections.push_back(std::to_string(outputIndex));
}
write("cameras." + std::to_string(cameraIndex) + ".connections",
connections);
write("picture", picture);
}
int AkVCam::Preferences::logLevel()
{
return readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT);
}
void AkVCam::Preferences::setLogLevel(int logLevel)
{
write("loglevel", logLevel);
}

View file

@ -24,8 +24,6 @@
#include <vector>
#include <CoreFoundation/CoreFoundation.h>
#include "VCamUtils/src/ipcbridge.h"
namespace AkVCam
{
class VideoFormat;
@ -55,21 +53,17 @@ 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 addDevice(const std::wstring &description);
std::string addCamera(const std::wstring &description,
const std::vector<VideoFormat> &formats,
IpcBridge::DeviceType type);
const std::vector<VideoFormat> &formats);
std::string addCamera(const std::string &path,
const std::wstring &description,
const std::vector<VideoFormat> &formats,
IpcBridge::DeviceType type);
const std::vector<VideoFormat> &formats);
void removeCamera(const std::string &path);
size_t camerasCount();
std::string createDevicePath();
int cameraFromPath(const std::string &path);
bool cameraExists(const std::string &path);
bool cameraIsInput(size_t cameraIndex);
std::wstring cameraDescription(size_t cameraIndex);
void cameraSetDescription(size_t cameraIndex,
const std::wstring &description);
@ -85,9 +79,10 @@ namespace AkVCam
const VideoFormat &format,
int index);
void cameraRemoveFormat(size_t cameraIndex, int index);
std::vector<std::string> cameraConnections(size_t cameraIndex);
void cameraSetConnections(size_t cameraIndex,
const std::vector<std::string> &connectedDevices);
std::wstring picture();
void setPicture(const std::wstring &picture);
int logLevel();
void setLogLevel(int logLevel);
}
}

View file

@ -104,6 +104,8 @@ AkVCam::IpcBridge::IpcBridge()
{
AkLogFunction();
this->d = new IpcBridgePrivate(this);
auto loglevel = AkVCam::Preferences::logLevel();
AkVCam::Logger::setLogLevel(loglevel);
ipcBridgePrivate().add(this);
this->registerPeer();
}
@ -159,6 +161,28 @@ bool AkVCam::IpcBridge::setDriver(const std::string &driver)
return driver == "AkVirtualCamera";
}
std::wstring AkVCam::IpcBridge::picture() const
{
return Preferences::picture();
}
void AkVCam::IpcBridge::setPicture(const std::wstring &picture)
{
Preferences::setPicture(picture);
}
int AkVCam::IpcBridge::logLevel() const
{
return Preferences::logLevel();
}
void AkVCam::IpcBridge::setLogLevel(int logLevel)
{
Preferences::setLogLevel(logLevel);
Logger::setLogLevel(logLevel);
}
std::vector<std::string> AkVCam::IpcBridge::availableRootMethods() const
{
return {"osascript"};
@ -356,9 +380,9 @@ void AkVCam::IpcBridge::setDescription(const std::string &deviceId,
return Preferences::cameraSetDescription(cameraIndex, description);
}
std::vector<AkVCam::PixelFormat> AkVCam::IpcBridge::supportedPixelFormats(DeviceType type) const
std::vector<AkVCam::PixelFormat> AkVCam::IpcBridge::supportedPixelFormats(StreamType type) const
{
if (type == DeviceTypeInput)
if (type == StreamTypeInput)
return {PixelFormatRGB24};
return {
@ -369,9 +393,9 @@ std::vector<AkVCam::PixelFormat> AkVCam::IpcBridge::supportedPixelFormats(Device
};
}
AkVCam::PixelFormat AkVCam::IpcBridge::defaultPixelFormat(DeviceType type) const
AkVCam::PixelFormat AkVCam::IpcBridge::defaultPixelFormat(StreamType type) const
{
return type == DeviceTypeInput?
return type == StreamTypeInput?
PixelFormatRGB24:
PixelFormatYUY2;
}
@ -641,19 +665,9 @@ std::string AkVCam::IpcBridge::clientExe(uint64_t pid) const
return {path};
}
AkVCam::IpcBridge::DeviceType AkVCam::IpcBridge::deviceType(const std::string &deviceId)
std::string AkVCam::IpcBridge::addDevice(const std::wstring &description)
{
auto cameraIndex = Preferences::cameraFromPath(deviceId);
return Preferences::cameraIsInput(cameraIndex)?
DeviceTypeInput:
DeviceTypeOutput;
}
std::string AkVCam::IpcBridge::addDevice(const std::wstring &description,
DeviceType type)
{
return Preferences::addDevice(description, type);
return Preferences::addDevice(description);
}
void AkVCam::IpcBridge::removeDevice(const std::string &deviceId)
@ -693,18 +707,6 @@ void AkVCam::IpcBridge::update()
xpc_release(dictionary);
}
std::vector<std::string> AkVCam::IpcBridge::connections(const std::string &deviceId)
{
return Preferences::cameraConnections(Preferences::cameraFromPath(deviceId));
}
void AkVCam::IpcBridge::setConnections(const std::string &deviceId,
const std::vector<std::string> &connectedDevices)
{
Preferences::cameraSetConnections(Preferences::cameraFromPath(deviceId),
connectedDevices);
}
void AkVCam::IpcBridge::updateDevices()
{
AkLogFunction();

View file

@ -27,9 +27,8 @@ extern "C" void *akPluginMain(CFAllocatorRef allocator,
CFUUIDRef requestedTypeUUID)
{
UNUSED(allocator);
auto logLevel =
AkVCam::Preferences::readInt("loglevel", AKVCAM_LOGLEVEL_DEFAULT);
AkVCam::Logger::setLogLevel(logLevel);
auto loglevel = AkVCam::Preferences::logLevel();
AkVCam::Logger::setLogLevel(loglevel);
if (AkVCam::Logger::logLevel() > AKVCAM_LOGLEVEL_DEFAULT) {
// Turn on lights

View file

@ -269,9 +269,7 @@ void AkVCam::PluginInterface::deviceAdded(void *userData,
auto self = reinterpret_cast<PluginInterface *>(userData);
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);
self->createDevice(deviceId, description, formats);
}
void AkVCam::PluginInterface::deviceRemoved(void *userData,
@ -304,8 +302,7 @@ void AkVCam::PluginInterface::devicesUpdated(void *userData, void *unused)
for (auto &deviceId: self->d->m_ipcBridge.devices()) {
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);
self->createDevice(deviceId, description, formats);
}
}
@ -315,13 +312,9 @@ void AkVCam::PluginInterface::frameReady(void *userData,
{
AkLogFunction();
auto self = reinterpret_cast<PluginInterface *>(userData);
auto cameraIndex = Preferences::cameraFromPath(deviceId);
auto connections = Preferences::cameraConnections(cameraIndex);
for (auto device: self->m_devices)
if (std::find(connections.begin(),
connections.end(),
device->deviceId()) != connections.end())
if (device->deviceId() == deviceId)
device->frameReady(frame);
}
@ -406,8 +399,7 @@ void AkVCam::PluginInterface::removeListener(void *userData,
bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
const std::wstring &description,
const std::vector<VideoFormat> &formats,
IpcBridge::DeviceType type)
const std::vector<VideoFormat> &formats)
{
AkLogFunction();
StreamPtr stream;
@ -460,10 +452,7 @@ bool AkVCam::PluginInterface::createDevice(const std::string &deviceId,
stream->setBridge(&this->d->m_ipcBridge);
stream->setFormats(formats);
stream->properties().setProperty(kCMIOStreamPropertyDirection,
UInt32(type == IpcBridge::DeviceTypeOutput?
Stream::Output:
Stream::Input));
stream->properties().setProperty(kCMIOStreamPropertyDirection, 0);
if (device->registerStreams() != kCMIOHardwareNoError) {
device->registerStreams(false);

View file

@ -80,8 +80,7 @@ namespace AkVCam
const std::string &deviceId);
bool createDevice(const std::string &deviceId,
const std::wstring &description,
const std::vector<VideoFormat> &formats,
IpcBridge::DeviceType type);
const std::vector<VideoFormat> &formats);
void destroyDevice(const std::string &deviceId);
friend struct PluginInterfacePrivate;

View file

@ -17,6 +17,7 @@
* Web-Site: http://webcamoid.github.io/
*/
#include <codecvt>
#include <thread>
#include <random>
#include <CoreMediaIO/CMIOSampleBuffer.h>
@ -24,6 +25,7 @@
#include "stream.h"
#include "clock.h"
#include "utils.h"
#include "PlatformUtils/src/preferences.h"
#include "VCamUtils/src/image/videoformat.h"
#include "VCamUtils/src/image/videoframe.h"
#include "VCamUtils/src/logger/logger.h"
@ -59,7 +61,6 @@ namespace AkVCam
void stopTimer();
static void streamLoop(CFRunLoopTimerRef timer, void *info);
void sendFrame(const VideoFrame &frame);
void requestFrame();
void updateTestFrame();
VideoFrame applyAdjusts(const VideoFrame &frame);
VideoFrame randomFrame();
@ -73,10 +74,12 @@ AkVCam::Stream::Stream(bool registerObject,
this->d = new StreamPrivate(this);
this->m_className = "Stream";
this->m_classID = kCMIOStreamClassID;
this->d->m_testFrame.load(CMIO_PLUGINS_DAL_PATH
"/"
CMIO_PLUGIN_NAME
".plugin/Contents/Resources/TestFrame.bmp");
auto picture = Preferences::picture();
if (!picture.empty()) {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> cv;
this->d->m_testFrame.load(cv.to_bytes(picture));
}
this->d->m_clock =
std::make_shared<Clock>("CMIO::VirtualCamera::Stream",
@ -224,29 +227,13 @@ bool AkVCam::Stream::start()
if (this->d->m_running)
return false;
UInt32 direction = 0;
this->properties().getProperty(kCMIOStreamPropertyDirection, &direction);
if (Direction(direction) == Output) {
this->d->updateTestFrame();
this->d->m_currentFrame = this->d->m_testFrameAdapted;
}
this->d->m_sequence = 0;
memset(&this->d->m_pts, 0, sizeof(CMTime));
this->d->m_running = this->d->startTimer();
AkLogInfo() << "Running: " << this->d->m_running << std::endl;
if (Direction(direction) == Input) {
std::string deviceId;
this->m_parent->properties().getProperty(kCMIODevicePropertyDeviceUID,
&deviceId);
VideoFormat format;
this->m_properties.getProperty(kCMIOStreamPropertyFormatDescription,
&format);
this->d->m_bridge->deviceStart(deviceId, format);
}
return this->d->m_running;
}
@ -257,16 +244,6 @@ void AkVCam::Stream::stop()
if (!this->d->m_running)
return;
UInt32 direction = 0;
this->properties().getProperty(kCMIOStreamPropertyDirection, &direction);
if (Direction(direction) == Input) {
std::string deviceId;
this->m_parent->properties().getProperty(kCMIODevicePropertyDeviceUID,
&deviceId);
this->d->m_bridge->deviceStop(deviceId);
}
this->d->m_running = false;
this->d->stopTimer();
this->d->m_currentFrame.clear();
@ -491,17 +468,10 @@ void AkVCam::StreamPrivate::streamLoop(CFRunLoopTimerRef timer, void *info)
self->m_mutex.lock();
UInt32 direction = 0;
self->self->m_properties.getProperty(kCMIOStreamPropertyDirection, &direction);
if (Stream::Direction(direction) == Stream::Output) {
if (self->m_currentFrame.format().size() < 1)
self->sendFrame(self->randomFrame());
else
self->sendFrame(self->m_currentFrame);
} else {
self->requestFrame();
}
self->m_mutex.unlock();
}
@ -599,97 +569,6 @@ void AkVCam::StreamPrivate::sendFrame(const VideoFrame &frame)
this->m_queueAlteredRefCon);
}
void AkVCam::StreamPrivate::requestFrame()
{
AkLogFunction();
if (this->m_queue->fullness() >= 1.0f)
return;
bool resync = false;
auto hostTime = CFAbsoluteTimeGetCurrent();
auto pts = CMTimeMake(int64_t(hostTime), 1e9);
auto ptsDiff = CMTimeGetSeconds(CMTimeSubtract(this->m_pts, pts));
if (CMTimeCompare(pts, this->m_pts) == 0)
return;
Float64 fps = 0;
this->self->m_properties.getProperty(kCMIOStreamPropertyFrameRate, &fps);
if (CMTIME_IS_INVALID(this->m_pts)
|| ptsDiff < 0
|| ptsDiff > 2. / fps) {
this->m_pts = pts;
resync = true;
}
CMIOStreamClockPostTimingEvent(this->m_pts,
UInt64(hostTime),
resync,
this->m_clock->ref());
if (!this->m_queueAltered)
return;
this->m_queueAltered(this->self->m_objectID,
nullptr,
this->m_queueAlteredRefCon);
for (;;) {
auto videoBuffer = this->m_queue->dequeue();
if (!videoBuffer)
break;
// Read frame data.
auto imageBuffer = CMSampleBufferGetImageBuffer(videoBuffer);
auto dataBuffer = CMSampleBufferGetDataBuffer(videoBuffer);
auto formatDesc = CMSampleBufferGetFormatDescription(videoBuffer);
auto fourCC = CMFormatDescriptionGetMediaSubType(formatDesc);
auto size = CMVideoFormatDescriptionGetDimensions(formatDesc);
Float64 fps = 0;
this->self->m_properties.getProperty(kCMIOStreamPropertyFrameRate, &fps);
VideoFormat videoFormat(formatFromCM(fourCC),
size.width,
size.height,
{{int64_t(std::round(fps)), 1}});
VideoFrame videoFrame(videoFormat);
if (imageBuffer) {
size_t dataSize = CVPixelBufferGetDataSize(imageBuffer);
CVPixelBufferLockBaseAddress(imageBuffer, 0);
void *data = CVPixelBufferGetBaseAddress(imageBuffer);
memcpy(videoFrame.data().data(),
data,
std::min(videoFrame.data().size(), dataSize));
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
} else if (dataBuffer) {
size_t lengthAtOffset = 0;
size_t dataSize = 0;
char *data = nullptr;
CMBlockBufferGetDataPointer(dataBuffer,
0,
&lengthAtOffset,
&dataSize,
&data);
memcpy(videoFrame.data().data(),
data,
std::min(videoFrame.data().size(), dataSize));
}
CFRelease(videoBuffer);
std::string deviceId;
this->self->properties().getProperty(kCMIODevicePropertyDeviceUID,
&deviceId);
this->m_bridge->write(deviceId, videoFrame);
}
auto duration = CMTimeMake(1e3, int32_t(1e3 * fps));
this->m_pts = CMTimeAdd(this->m_pts, duration);
}
void AkVCam::StreamPrivate::updateTestFrame()
{
this->m_testFrameAdapted = this->applyAdjusts(this->m_testFrame);

View file

@ -38,12 +38,6 @@ namespace AkVCam
class Stream: public Object
{
public:
enum Direction
{
Output,
Input
};
Stream(bool registerObject=false, Object *m_parent=nullptr);
Stream(const Stream &other) = delete;
~Stream();

View file

@ -658,8 +658,7 @@ bool AkVCam::IpcBridge::canApply(AkVCam::IpcBridge::Operation operation) const
}
std::string AkVCam::IpcBridge::deviceCreate(const std::wstring &description,
const std::vector<VideoFormat> &formats,
DeviceType type)
const std::vector<VideoFormat> &formats)
{
AkLogFunction();