2020-07-11 17:22:23 +00:00
|
|
|
/* akvirtualcamera, virtual camera for Mac and Windows.
|
|
|
|
* Copyright (C) 2020 Gonzalo Exequiel Pedone
|
|
|
|
*
|
|
|
|
* akvirtualcamera is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* akvirtualcamera is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with akvirtualcamera. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Web-Site: http://webcamoid.github.io/
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <algorithm>
|
2021-06-21 21:25:48 +00:00
|
|
|
#include <cerrno>
|
2021-02-06 04:45:40 +00:00
|
|
|
#include <chrono>
|
2021-05-24 22:47:39 +00:00
|
|
|
#include <cmath>
|
2020-07-28 00:43:21 +00:00
|
|
|
#include <codecvt>
|
2020-10-09 15:57:13 +00:00
|
|
|
#include <csignal>
|
2021-02-06 04:45:40 +00:00
|
|
|
#include <cstring>
|
2020-07-11 17:22:23 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <functional>
|
2020-09-09 15:21:03 +00:00
|
|
|
#include <locale>
|
2020-07-11 17:22:23 +00:00
|
|
|
#include <sstream>
|
2021-02-06 04:45:40 +00:00
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <io.h>
|
|
|
|
#endif
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
#include "cmdparser.h"
|
2020-07-28 00:43:21 +00:00
|
|
|
#include "VCamUtils/src/ipcbridge.h"
|
2020-09-09 15:21:03 +00:00
|
|
|
#include "VCamUtils/src/settings.h"
|
2021-02-19 22:52:28 +00:00
|
|
|
#include "VCamUtils/src/videoformat.h"
|
|
|
|
#include "VCamUtils/src/videoframe.h"
|
2020-10-06 16:07:27 +00:00
|
|
|
#include "VCamUtils/src/logger.h"
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
#define AKVCAM_BIND_FUNC(member) \
|
|
|
|
std::bind(&member, this->d, std::placeholders::_1, std::placeholders::_2)
|
|
|
|
|
|
|
|
namespace AkVCam {
|
2020-09-09 15:21:03 +00:00
|
|
|
using StringMatrix = std::vector<StringVector>;
|
|
|
|
using VideoFormatMatrix = std::vector<std::vector<VideoFormat>>;
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
struct CmdParserFlags
|
|
|
|
{
|
|
|
|
StringVector flags;
|
|
|
|
std::string value;
|
|
|
|
std::string helpString;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdParserCommand
|
|
|
|
{
|
|
|
|
std::string command;
|
|
|
|
std::string arguments;
|
|
|
|
std::string helpString;
|
|
|
|
ProgramOptionsFunc func;
|
|
|
|
std::vector<CmdParserFlags> flags;
|
2021-05-24 22:47:39 +00:00
|
|
|
bool advanced {false};
|
|
|
|
|
|
|
|
CmdParserCommand();
|
|
|
|
CmdParserCommand(const std::string &command,
|
|
|
|
const std::string &arguments,
|
|
|
|
const std::string &helpString,
|
|
|
|
const ProgramOptionsFunc &func,
|
2021-05-25 19:53:30 +00:00
|
|
|
const std::vector<CmdParserFlags> &flags,
|
2021-05-24 22:47:39 +00:00
|
|
|
bool advanced);
|
2020-07-11 17:22:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class CmdParserPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
std::vector<CmdParserCommand> m_commands;
|
2020-07-28 00:43:21 +00:00
|
|
|
IpcBridge m_ipcBridge;
|
|
|
|
bool m_parseable {false};
|
2020-07-11 17:22:23 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
static const std::map<ControlType, std::string> &typeStrMap();
|
2020-07-11 17:22:23 +00:00
|
|
|
std::string basename(const std::string &path);
|
|
|
|
void printFlags(const std::vector<CmdParserFlags> &cmdFlags,
|
|
|
|
size_t indent);
|
2021-05-24 22:47:39 +00:00
|
|
|
size_t maxCommandLength(bool showAdvancedHelp);
|
|
|
|
size_t maxArgumentsLength(bool showAdvancedHelp);
|
2020-07-11 17:22:23 +00:00
|
|
|
size_t maxFlagsLength(const std::vector<CmdParserFlags> &flags);
|
|
|
|
size_t maxFlagsValueLength(const std::vector<CmdParserFlags> &flags);
|
2020-09-09 15:21:03 +00:00
|
|
|
size_t maxColumnLength(const StringVector &table,
|
|
|
|
size_t width,
|
|
|
|
size_t column);
|
|
|
|
std::vector<size_t> maxColumnsLength(const StringVector &table,
|
|
|
|
size_t width);
|
2021-06-16 19:49:23 +00:00
|
|
|
void drawTableHLine(const std::vector<size_t> &columnsLength,
|
|
|
|
bool toStdErr=false);
|
|
|
|
void drawTable(const StringVector &table,
|
|
|
|
size_t width,
|
|
|
|
bool toStdErr=false);
|
2020-07-11 17:22:23 +00:00
|
|
|
CmdParserCommand *parserCommand(const std::string &command);
|
|
|
|
const CmdParserFlags *parserFlag(const std::vector<CmdParserFlags> &cmdFlags,
|
|
|
|
const std::string &flag);
|
|
|
|
bool containsFlag(const StringMap &flags,
|
|
|
|
const std::string &command,
|
|
|
|
const std::string &flagAlias);
|
2020-07-28 00:43:21 +00:00
|
|
|
std::string flagValue(const StringMap &flags,
|
|
|
|
const std::string &command,
|
|
|
|
const std::string &flagAlias);
|
2020-07-11 17:22:23 +00:00
|
|
|
int defaultHandler(const StringMap &flags,
|
|
|
|
const StringVector &args);
|
|
|
|
int showHelp(const StringMap &flags, const StringVector &args);
|
|
|
|
int showDevices(const StringMap &flags, const StringVector &args);
|
|
|
|
int addDevice(const StringMap &flags, const StringVector &args);
|
|
|
|
int removeDevice(const StringMap &flags, const StringVector &args);
|
2020-08-10 18:37:33 +00:00
|
|
|
int removeDevices(const StringMap &flags, const StringVector &args);
|
2020-07-11 17:22:23 +00:00
|
|
|
int showDeviceDescription(const StringMap &flags,
|
|
|
|
const StringVector &args);
|
2020-08-10 18:37:33 +00:00
|
|
|
int setDeviceDescription(const StringMap &flags,
|
|
|
|
const StringVector &args);
|
2020-07-11 17:22:23 +00:00
|
|
|
int showSupportedFormats(const StringMap &flags,
|
|
|
|
const StringVector &args);
|
2021-02-06 04:45:40 +00:00
|
|
|
int showDefaultFormat(const StringMap &flags,
|
|
|
|
const StringVector &args);
|
2020-07-11 17:22:23 +00:00
|
|
|
int showFormats(const StringMap &flags, const StringVector &args);
|
|
|
|
int addFormat(const StringMap &flags, const StringVector &args);
|
|
|
|
int removeFormat(const StringMap &flags, const StringVector &args);
|
2020-08-10 18:37:33 +00:00
|
|
|
int removeFormats(const StringMap &flags, const StringVector &args);
|
2020-07-11 17:22:23 +00:00
|
|
|
int update(const StringMap &flags, const StringVector &args);
|
2020-07-28 00:43:21 +00:00
|
|
|
int loadSettings(const StringMap &flags, const StringVector &args);
|
2020-08-22 00:31:45 +00:00
|
|
|
int stream(const StringMap &flags, const StringVector &args);
|
2021-02-06 04:45:40 +00:00
|
|
|
int listenEvents(const StringMap &flags, const StringVector &args);
|
2020-08-10 18:37:33 +00:00
|
|
|
int showControls(const StringMap &flags, const StringVector &args);
|
|
|
|
int readControl(const StringMap &flags, const StringVector &args);
|
2020-09-09 15:21:03 +00:00
|
|
|
int writeControls(const StringMap &flags, const StringVector &args);
|
2020-08-22 00:31:45 +00:00
|
|
|
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);
|
2020-07-11 17:22:23 +00:00
|
|
|
int showClients(const StringMap &flags, const StringVector &args);
|
2021-02-06 04:45:40 +00:00
|
|
|
int dumpInfo(const StringMap &flags, const StringVector &args);
|
2021-05-24 22:47:39 +00:00
|
|
|
int hacks(const StringMap &flags, const StringVector &args);
|
|
|
|
int hackInfo(const StringMap &flags, const StringVector &args);
|
|
|
|
int hack(const StringMap &flags, const StringVector &args);
|
2020-09-09 15:21:03 +00:00
|
|
|
void loadGenerals(Settings &settings);
|
|
|
|
VideoFormatMatrix readFormats(Settings &settings);
|
|
|
|
std::vector<VideoFormat> readFormat(Settings &settings);
|
|
|
|
StringMatrix matrixCombine(const StringMatrix &matrix);
|
|
|
|
void matrixCombineP(const StringMatrix &matrix,
|
|
|
|
size_t index,
|
|
|
|
StringVector combined,
|
|
|
|
StringMatrix &combinations);
|
|
|
|
void createDevices(Settings &settings,
|
|
|
|
const VideoFormatMatrix &availableFormats);
|
|
|
|
void createDevice(Settings &settings,
|
|
|
|
const VideoFormatMatrix &availableFormats);
|
|
|
|
std::vector<VideoFormat> readDeviceFormats(Settings &settings,
|
|
|
|
const VideoFormatMatrix &availableFormats);
|
2020-07-11 17:22:23 +00:00
|
|
|
};
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
std::string operator *(const std::string &str, size_t n);
|
2021-02-06 04:45:40 +00:00
|
|
|
std::string operator *(size_t n, const std::string &str);
|
2020-07-11 17:22:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AkVCam::CmdParser::CmdParser()
|
|
|
|
{
|
|
|
|
this->d = new CmdParserPrivate();
|
2021-06-09 20:37:36 +00:00
|
|
|
auto logFile = this->d->m_ipcBridge.logPath("AkVCamManager");
|
|
|
|
AkLogInfo() << "Sending debug output to " << logFile << std::endl;
|
|
|
|
AkVCam::Logger::setLogFile(logFile);
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
this->d->m_commands.push_back({});
|
|
|
|
this->setDefaultFuntion(AKVCAM_BIND_FUNC(CmdParserPrivate::defaultHandler));
|
|
|
|
this->addFlags("",
|
2020-08-22 00:31:45 +00:00
|
|
|
{"-h", "--help"},
|
2020-07-11 17:22:23 +00:00
|
|
|
"Show help.");
|
2021-05-24 22:47:39 +00:00
|
|
|
this->addFlags("",
|
|
|
|
{"--help-all"},
|
|
|
|
"Show advanced help.");
|
2021-02-06 04:45:40 +00:00
|
|
|
this->addFlags("",
|
|
|
|
{"-v", "--version"},
|
|
|
|
"Show program version.");
|
2020-07-11 17:22:23 +00:00
|
|
|
this->addFlags("",
|
2020-08-22 00:31:45 +00:00
|
|
|
{"-p", "--parseable"},
|
2020-07-11 17:22:23 +00:00
|
|
|
"Show parseable output.");
|
|
|
|
this->addCommand("devices",
|
|
|
|
"",
|
|
|
|
"List devices.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::showDevices));
|
|
|
|
this->addCommand("add-device",
|
|
|
|
"DESCRIPTION",
|
|
|
|
"Add a new device.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::addDevice));
|
2021-06-19 16:45:59 +00:00
|
|
|
this->addFlags("add-device",
|
|
|
|
{"-i", "--id"},
|
|
|
|
"DEVICEID",
|
|
|
|
"Create device as DEVICEID.");
|
2020-07-11 17:22:23 +00:00
|
|
|
this->addCommand("remove-device",
|
|
|
|
"DEVICE",
|
|
|
|
"Remove a device.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::removeDevice));
|
2020-08-10 18:37:33 +00:00
|
|
|
this->addCommand("remove-devices",
|
|
|
|
"",
|
|
|
|
"Remove all devices.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::removeDevices));
|
|
|
|
this->addCommand("description",
|
2020-07-11 17:22:23 +00:00
|
|
|
"DEVICE",
|
|
|
|
"Show device description.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::showDeviceDescription));
|
2020-08-10 18:37:33 +00:00
|
|
|
this->addCommand("set-description",
|
|
|
|
"DEVICE DESCRIPTION",
|
|
|
|
"Set device description.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::setDeviceDescription));
|
2020-07-11 17:22:23 +00:00
|
|
|
this->addCommand("supported-formats",
|
|
|
|
"",
|
|
|
|
"Show supported formats.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::showSupportedFormats));
|
|
|
|
this->addFlags("supported-formats",
|
2020-08-22 00:31:45 +00:00
|
|
|
{"-i", "--input"},
|
2020-07-11 17:22:23 +00:00
|
|
|
"Show supported input formats.");
|
|
|
|
this->addFlags("supported-formats",
|
2020-08-22 00:31:45 +00:00
|
|
|
{"-o", "--output"},
|
2020-07-11 17:22:23 +00:00
|
|
|
"Show supported output formats.");
|
2021-02-06 04:45:40 +00:00
|
|
|
this->addCommand("default-format",
|
|
|
|
"",
|
|
|
|
"Default device format.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::showDefaultFormat));
|
|
|
|
this->addFlags("default-format",
|
|
|
|
{"-i", "--input"},
|
|
|
|
"Default input format.");
|
|
|
|
this->addFlags("default-format",
|
|
|
|
{"-o", "--output"},
|
|
|
|
"Default output format.");
|
2020-07-11 17:22:23 +00:00
|
|
|
this->addCommand("formats",
|
|
|
|
"DEVICE",
|
|
|
|
"Show device formats.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::showFormats));
|
|
|
|
this->addCommand("add-format",
|
|
|
|
"DEVICE FORMAT WIDTH HEIGHT FPS",
|
|
|
|
"Add a new device format.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::addFormat));
|
|
|
|
this->addFlags("add-format",
|
2020-08-22 00:31:45 +00:00
|
|
|
{"-i", "--index"},
|
2020-07-11 17:22:23 +00:00
|
|
|
"INDEX",
|
|
|
|
"Add format at INDEX.");
|
|
|
|
this->addCommand("remove-format",
|
2020-07-28 00:43:21 +00:00
|
|
|
"DEVICE INDEX",
|
2020-07-11 17:22:23 +00:00
|
|
|
"Remove device format.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::removeFormat));
|
2020-08-10 18:37:33 +00:00
|
|
|
this->addCommand("remove-formats",
|
|
|
|
"DEVICE",
|
|
|
|
"Remove all device formats.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::removeFormats));
|
2020-07-11 17:22:23 +00:00
|
|
|
this->addCommand("update",
|
|
|
|
"",
|
|
|
|
"Update devices.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::update));
|
2020-07-28 00:43:21 +00:00
|
|
|
this->addCommand("load",
|
|
|
|
"SETTINGS.INI",
|
|
|
|
"Create devices from a setting file.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::loadSettings));
|
2020-08-22 00:31:45 +00:00
|
|
|
this->addCommand("stream",
|
|
|
|
"DEVICE FORMAT WIDTH HEIGHT",
|
|
|
|
"Read frames from stdin and send them to the device.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::stream));
|
2021-05-24 22:47:39 +00:00
|
|
|
this->addFlags("stream",
|
|
|
|
{"-f", "--fps"},
|
|
|
|
"FPS",
|
|
|
|
"Read stream input at a constant frame rate.");
|
2021-02-06 04:45:40 +00:00
|
|
|
this->addCommand("listen-events",
|
|
|
|
"",
|
|
|
|
"Keep the manager running and listening to global events.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::listenEvents));
|
2020-08-10 18:37:33 +00:00
|
|
|
this->addCommand("controls",
|
|
|
|
"DEVICE",
|
|
|
|
"Show device controls.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::showControls));
|
|
|
|
this->addCommand("get-control",
|
|
|
|
"DEVICE CONTROL",
|
|
|
|
"Read device control.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::readControl));
|
2020-09-09 15:21:03 +00:00
|
|
|
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));
|
2020-08-22 00:31:45 +00:00
|
|
|
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));
|
2020-07-11 17:22:23 +00:00
|
|
|
this->addCommand("clients",
|
|
|
|
"",
|
|
|
|
"Show clients using the camera.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::showClients));
|
2021-02-06 04:45:40 +00:00
|
|
|
this->addCommand("dump",
|
|
|
|
"",
|
|
|
|
"Show all information in a parseable XML format.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::dumpInfo));
|
2021-05-24 22:47:39 +00:00
|
|
|
this->addCommand("hacks",
|
|
|
|
"",
|
|
|
|
"List system hacks to make the virtual camera work.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::hacks),
|
|
|
|
true);
|
|
|
|
this->addCommand("hack-info",
|
|
|
|
"HACK",
|
|
|
|
"Show hack information.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::hackInfo),
|
|
|
|
true);
|
|
|
|
this->addFlags("hack-info",
|
|
|
|
{"-s", "--issafe"},
|
|
|
|
"Is hack safe?");
|
|
|
|
this->addFlags("hack-info",
|
|
|
|
{"-c", "--description"},
|
|
|
|
"Show hack description.");
|
|
|
|
this->addCommand("hack",
|
|
|
|
"HACK PARAMS...",
|
|
|
|
"Apply system hack.",
|
|
|
|
AKVCAM_BIND_FUNC(CmdParserPrivate::hack),
|
|
|
|
true);
|
|
|
|
this->addFlags("hack",
|
|
|
|
{"-y", "--yes"},
|
|
|
|
"Accept all risks and continue anyway.");
|
2020-07-11 17:22:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AkVCam::CmdParser::~CmdParser()
|
|
|
|
{
|
|
|
|
delete this->d;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParser::parse(int argc, char **argv)
|
|
|
|
{
|
|
|
|
auto program = this->d->basename(argv[0]);
|
|
|
|
auto command = &this->d->m_commands[0];
|
|
|
|
StringMap flags;
|
|
|
|
StringVector arguments {program};
|
|
|
|
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
|
|
std::string arg = argv[i];
|
2021-02-06 04:45:40 +00:00
|
|
|
char *p = nullptr;
|
|
|
|
strtod(arg.c_str(), &p);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
2021-05-25 19:53:30 +00:00
|
|
|
if (arg[0] == '-' && p && strnlen(p, 1024) != 0) {
|
2020-07-11 17:22:23 +00:00
|
|
|
auto flag = this->d->parserFlag(command->flags, arg);
|
|
|
|
|
|
|
|
if (!flag) {
|
|
|
|
if (command->command.empty())
|
|
|
|
std::cout << "Invalid option '"
|
|
|
|
<< arg
|
|
|
|
<< "'"
|
|
|
|
<< std::endl;
|
|
|
|
else
|
|
|
|
std::cout << "Invalid option '"
|
|
|
|
<< arg << "' for '"
|
|
|
|
<< command->command
|
|
|
|
<< "'"
|
|
|
|
<< std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-11 17:22:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string value;
|
|
|
|
|
|
|
|
if (!flag->value.empty()) {
|
|
|
|
auto next = i + 1;
|
|
|
|
|
|
|
|
if (next < argc) {
|
|
|
|
value = argv[next];
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
flags[arg] = value;
|
|
|
|
} else {
|
|
|
|
if (command->command.empty()) {
|
|
|
|
if (!flags.empty()) {
|
|
|
|
auto result = command->func(flags, {program});
|
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
flags.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto cmd = this->d->parserCommand(arg);
|
|
|
|
|
|
|
|
if (cmd) {
|
|
|
|
command = cmd;
|
|
|
|
flags.clear();
|
|
|
|
} else {
|
|
|
|
std::cout << "Unknown command '" << arg << "'" << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-11 17:22:23 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arguments.push_back(arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-16 19:49:23 +00:00
|
|
|
if (this->d->m_ipcBridge.isBusyFor(command->command)) {
|
|
|
|
std::cerr << "This operation is not permitted." << std::endl;
|
|
|
|
std::cerr << "The virtual camera is in use. Stop or close the virtual "
|
|
|
|
<< "camera clients and try again." << std::endl;
|
|
|
|
std::cerr << std::endl;
|
|
|
|
auto clients = this->d->m_ipcBridge.clientsPids();
|
|
|
|
|
|
|
|
if (!clients.empty()) {
|
|
|
|
std::vector<std::string> table {
|
|
|
|
"Pid",
|
|
|
|
"Executable"
|
|
|
|
};
|
|
|
|
auto columns = table.size();
|
|
|
|
|
|
|
|
for (auto &pid: clients) {
|
|
|
|
table.push_back(std::to_string(pid));
|
|
|
|
table.push_back(this->d->m_ipcBridge.clientExe(pid));
|
|
|
|
}
|
|
|
|
|
|
|
|
this->d->drawTable(table, columns, true);
|
|
|
|
}
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EBUSY;
|
2021-06-16 19:49:23 +00:00
|
|
|
}
|
|
|
|
|
2021-05-24 22:47:39 +00:00
|
|
|
if (this->d->m_ipcBridge.needsRoot(command->command)
|
|
|
|
|| (command->command == "hack"
|
|
|
|
&& arguments.size() >= 2
|
2021-06-21 21:25:48 +00:00
|
|
|
&& this->d->m_ipcBridge.hackNeedsRoot(arguments[1]))) {
|
|
|
|
std::cerr << "You must run this command with administrator privileges." << std::endl;
|
|
|
|
|
|
|
|
return -EPERM;
|
|
|
|
}
|
2021-02-06 04:45:40 +00:00
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return command->func(flags, arguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AkVCam::CmdParser::setDefaultFuntion(const ProgramOptionsFunc &func)
|
|
|
|
{
|
|
|
|
this->d->m_commands[0].func = func;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AkVCam::CmdParser::addCommand(const std::string &command,
|
|
|
|
const std::string &arguments,
|
|
|
|
const std::string &helpString,
|
2021-05-24 22:47:39 +00:00
|
|
|
const ProgramOptionsFunc &func,
|
|
|
|
bool advanced)
|
2020-07-11 17:22:23 +00:00
|
|
|
{
|
|
|
|
auto it =
|
|
|
|
std::find_if(this->d->m_commands.begin(),
|
|
|
|
this->d->m_commands.end(),
|
|
|
|
[&command] (const CmdParserCommand &cmd) -> bool {
|
|
|
|
return cmd.command == command;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (it == this->d->m_commands.end()) {
|
|
|
|
this->d->m_commands.push_back({command,
|
|
|
|
arguments,
|
|
|
|
helpString,
|
|
|
|
func,
|
2021-05-24 22:47:39 +00:00
|
|
|
{},
|
|
|
|
advanced});
|
2020-07-11 17:22:23 +00:00
|
|
|
} else {
|
|
|
|
it->command = command;
|
|
|
|
it->arguments = arguments;
|
|
|
|
it->helpString = helpString;
|
|
|
|
it->func = func;
|
2021-05-24 22:47:39 +00:00
|
|
|
it->advanced = advanced;
|
2020-07-11 17:22:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AkVCam::CmdParser::addFlags(const std::string &command,
|
|
|
|
const StringVector &flags,
|
|
|
|
const std::string &value,
|
|
|
|
const std::string &helpString)
|
|
|
|
{
|
|
|
|
auto it =
|
|
|
|
std::find_if(this->d->m_commands.begin(),
|
|
|
|
this->d->m_commands.end(),
|
|
|
|
[&command] (const CmdParserCommand &cmd) -> bool {
|
|
|
|
return cmd.command == command;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (it == this->d->m_commands.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
it->flags.push_back({flags, value, helpString});
|
|
|
|
}
|
|
|
|
|
|
|
|
void AkVCam::CmdParser::addFlags(const std::string &command,
|
|
|
|
const StringVector &flags,
|
|
|
|
const std::string &helpString)
|
|
|
|
{
|
|
|
|
this->addFlags(command, flags, "", helpString);
|
|
|
|
}
|
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
const std::map<AkVCam::ControlType, std::string> &AkVCam::CmdParserPrivate::typeStrMap()
|
|
|
|
{
|
|
|
|
static const std::map<ControlType, std::string> typeStr {
|
|
|
|
{ControlTypeInteger, "Integer"},
|
|
|
|
{ControlTypeBoolean, "Boolean"},
|
|
|
|
{ControlTypeMenu , "Menu" },
|
|
|
|
};
|
|
|
|
|
|
|
|
return typeStr;
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
std::string AkVCam::CmdParserPrivate::basename(const std::string &path)
|
|
|
|
{
|
|
|
|
auto rit =
|
|
|
|
std::find_if(path.rbegin(),
|
|
|
|
path.rend(),
|
|
|
|
[] (char c) -> bool {
|
|
|
|
return c == '/' || c == '\\';
|
|
|
|
});
|
|
|
|
|
|
|
|
auto program =
|
|
|
|
rit == path.rend()?
|
|
|
|
path:
|
2020-10-08 23:07:51 +00:00
|
|
|
path.substr(path.size() + size_t(path.rbegin() - rit));
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
auto it =
|
|
|
|
std::find_if(program.begin(),
|
|
|
|
program.end(),
|
|
|
|
[] (char c) -> bool {
|
|
|
|
return c == '.';
|
|
|
|
});
|
|
|
|
|
|
|
|
program =
|
|
|
|
it == path.end()?
|
|
|
|
program:
|
2020-10-08 23:07:51 +00:00
|
|
|
program.substr(0, size_t(it - program.begin()));
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AkVCam::CmdParserPrivate::printFlags(const std::vector<CmdParserFlags> &cmdFlags,
|
|
|
|
size_t indent)
|
|
|
|
{
|
|
|
|
std::vector<char> spaces(indent, ' ');
|
|
|
|
auto maxFlagsLen = this->maxFlagsLength(cmdFlags);
|
|
|
|
auto maxFlagsValueLen = this->maxFlagsValueLength(cmdFlags);
|
|
|
|
|
|
|
|
for (auto &flag: cmdFlags) {
|
2020-08-10 18:37:33 +00:00
|
|
|
auto allFlags = join(flag.flags, ", ");
|
2020-07-11 17:22:23 +00:00
|
|
|
std::cout << std::string(spaces.data(), indent)
|
2020-08-10 18:37:33 +00:00
|
|
|
<< fill(allFlags, maxFlagsLen);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
if (maxFlagsValueLen > 0)
|
|
|
|
std::cout << " " << fill(flag.value, maxFlagsValueLen);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
std::cout << " "
|
|
|
|
<< flag.helpString
|
|
|
|
<< std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-24 22:47:39 +00:00
|
|
|
size_t AkVCam::CmdParserPrivate::maxCommandLength(bool showAdvancedHelp)
|
2020-07-11 17:22:23 +00:00
|
|
|
{
|
|
|
|
size_t length = 0;
|
|
|
|
|
|
|
|
for (auto &cmd: this->m_commands)
|
2021-05-24 22:47:39 +00:00
|
|
|
if (!cmd.advanced || showAdvancedHelp)
|
|
|
|
length = std::max(cmd.command.size(), length);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2021-05-24 22:47:39 +00:00
|
|
|
size_t AkVCam::CmdParserPrivate::maxArgumentsLength(bool showAdvancedHelp)
|
2020-07-11 17:22:23 +00:00
|
|
|
{
|
|
|
|
size_t length = 0;
|
|
|
|
|
|
|
|
for (auto &cmd: this->m_commands)
|
2021-05-24 22:47:39 +00:00
|
|
|
if (!cmd.advanced || showAdvancedHelp)
|
|
|
|
length = std::max(cmd.arguments.size(), length);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t AkVCam::CmdParserPrivate::maxFlagsLength(const std::vector<CmdParserFlags> &flags)
|
|
|
|
{
|
|
|
|
size_t length = 0;
|
|
|
|
|
|
|
|
for (auto &flag: flags)
|
2020-08-10 18:37:33 +00:00
|
|
|
length = std::max(join(flag.flags, ", ").size(), length);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t AkVCam::CmdParserPrivate::maxFlagsValueLength(const std::vector<CmdParserFlags> &flags)
|
|
|
|
{
|
|
|
|
size_t length = 0;
|
|
|
|
|
|
|
|
for (auto &flag: flags)
|
|
|
|
length = std::max(flag.value.size(), length);
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
size_t AkVCam::CmdParserPrivate::maxColumnLength(const AkVCam::StringVector &table,
|
|
|
|
size_t width,
|
|
|
|
size_t column)
|
2020-07-28 00:43:21 +00:00
|
|
|
{
|
|
|
|
size_t length = 0;
|
2020-09-09 15:21:03 +00:00
|
|
|
size_t height = table.size() / width;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
for (size_t y = 0; y < height; y++) {
|
|
|
|
auto &str = table[y * width + column];
|
2020-07-28 00:43:21 +00:00
|
|
|
length = std::max(str.size(), length);
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
std::vector<size_t> AkVCam::CmdParserPrivate::maxColumnsLength(const AkVCam::StringVector &table,
|
|
|
|
size_t width)
|
2020-07-28 00:43:21 +00:00
|
|
|
{
|
2020-09-09 15:21:03 +00:00
|
|
|
std::vector<size_t> lengths;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
for (size_t x = 0; x < width; x++)
|
|
|
|
lengths.push_back(this->maxColumnLength(table, width, x));
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
return lengths;
|
|
|
|
}
|
|
|
|
|
2021-06-16 19:49:23 +00:00
|
|
|
void AkVCam::CmdParserPrivate::drawTableHLine(const std::vector<size_t> &columnsLength,
|
|
|
|
bool toStdErr)
|
2020-09-09 15:21:03 +00:00
|
|
|
{
|
2021-06-16 19:49:23 +00:00
|
|
|
std::ostream *out = &std::cout;
|
|
|
|
|
|
|
|
if (toStdErr)
|
|
|
|
out = &std::cerr;
|
|
|
|
|
|
|
|
*out << '+';
|
2020-09-09 15:21:03 +00:00
|
|
|
|
|
|
|
for (auto &len: columnsLength)
|
2021-06-16 19:49:23 +00:00
|
|
|
*out << std::string("-") * (len + 2) << '+';
|
2020-09-09 15:21:03 +00:00
|
|
|
|
2021-06-16 19:49:23 +00:00
|
|
|
*out << std::endl;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void AkVCam::CmdParserPrivate::drawTable(const AkVCam::StringVector &table,
|
2021-06-16 19:49:23 +00:00
|
|
|
size_t width,
|
|
|
|
bool toStdErr)
|
2020-09-09 15:21:03 +00:00
|
|
|
{
|
|
|
|
size_t height = table.size() / width;
|
|
|
|
auto columnsLength = this->maxColumnsLength(table, width);
|
2021-06-16 19:49:23 +00:00
|
|
|
this->drawTableHLine(columnsLength, toStdErr);
|
|
|
|
std::ostream *out = &std::cout;
|
|
|
|
|
|
|
|
if (toStdErr)
|
|
|
|
out = &std::cerr;
|
2020-09-09 15:21:03 +00:00
|
|
|
|
|
|
|
for (size_t y = 0; y < height; y++) {
|
2021-06-16 19:49:23 +00:00
|
|
|
*out << "|";
|
2020-09-09 15:21:03 +00:00
|
|
|
|
|
|
|
for (size_t x = 0; x < width; x++) {
|
|
|
|
auto &element = table[x + y * width];
|
2021-06-16 19:49:23 +00:00
|
|
|
*out << " " << fill(element, columnsLength[x]) << " |";
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 19:49:23 +00:00
|
|
|
*out << std::endl;
|
2020-09-09 15:21:03 +00:00
|
|
|
|
|
|
|
if (y == 0 && height > 1)
|
2021-06-16 19:49:23 +00:00
|
|
|
this->drawTableHLine(columnsLength, toStdErr);
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 19:49:23 +00:00
|
|
|
this->drawTableHLine(columnsLength, toStdErr);
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
AkVCam::CmdParserCommand *AkVCam::CmdParserPrivate::parserCommand(const std::string &command)
|
|
|
|
{
|
|
|
|
for (auto &cmd: this->m_commands)
|
|
|
|
if (cmd.command == command)
|
|
|
|
return &cmd;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AkVCam::CmdParserFlags *AkVCam::CmdParserPrivate::parserFlag(const std::vector<CmdParserFlags> &cmdFlags,
|
|
|
|
const std::string &flag)
|
|
|
|
{
|
|
|
|
for (auto &flags: cmdFlags)
|
|
|
|
for (auto &f: flags.flags)
|
|
|
|
if (f == flag)
|
|
|
|
return &flags;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AkVCam::CmdParserPrivate::containsFlag(const 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 true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-07-28 00:43:21 +00:00
|
|
|
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 {};
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
int AkVCam::CmdParserPrivate::defaultHandler(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2021-05-24 22:47:39 +00:00
|
|
|
if (flags.empty()
|
|
|
|
|| this->containsFlag(flags, "", "-h")
|
|
|
|
|| this->containsFlag(flags, "", "--help-all"))
|
2020-07-11 17:22:23 +00:00
|
|
|
return this->showHelp(flags, args);
|
|
|
|
|
2021-02-06 04:45:40 +00:00
|
|
|
if (this->containsFlag(flags, "", "-v")) {
|
|
|
|
std::cout << COMMONS_VERSION << std::endl;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-28 00:43:21 +00:00
|
|
|
if (this->containsFlag(flags, "", "-p"))
|
|
|
|
this->m_parseable = true;
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::showHelp(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-07-28 00:43:21 +00:00
|
|
|
UNUSED(flags);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
std::cout << args[0]
|
2020-09-09 15:21:03 +00:00
|
|
|
<< " [OPTIONS...] COMMAND [COMMAND_OPTIONS...] ..."
|
|
|
|
<< std::endl;
|
2020-07-11 17:22:23 +00:00
|
|
|
std::cout << std::endl;
|
|
|
|
std::cout << "AkVirtualCamera virtual device manager." << std::endl;
|
|
|
|
std::cout << std::endl;
|
|
|
|
std::cout << "General Options:" << std::endl;
|
|
|
|
std::cout << std::endl;
|
|
|
|
this->printFlags(this->m_commands[0].flags, 4);
|
|
|
|
std::cout << std::endl;
|
|
|
|
std::cout << "Commands:" << std::endl;
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
2021-05-24 22:47:39 +00:00
|
|
|
bool showAdvancedHelp = this->containsFlag(flags, "", "--help-all");
|
|
|
|
auto maxCmdLen = this->maxCommandLength(showAdvancedHelp);
|
|
|
|
auto maxArgsLen = this->maxArgumentsLength(showAdvancedHelp);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
for (auto &cmd: this->m_commands) {
|
2021-05-24 22:47:39 +00:00
|
|
|
if (cmd.command.empty()
|
|
|
|
|| (cmd.advanced && !showAdvancedHelp))
|
2020-07-11 17:22:23 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
std::cout << " "
|
2020-08-10 18:37:33 +00:00
|
|
|
<< fill(cmd.command, maxCmdLen)
|
2020-07-11 17:22:23 +00:00
|
|
|
<< " "
|
2020-08-10 18:37:33 +00:00
|
|
|
<< fill(cmd.arguments, maxArgsLen)
|
2020-07-11 17:22:23 +00:00
|
|
|
<< " "
|
|
|
|
<< cmd.helpString << std::endl;
|
|
|
|
|
|
|
|
if (!cmd.flags.empty())
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
|
|
this->printFlags(cmd.flags, 8);
|
|
|
|
|
|
|
|
if (!cmd.flags.empty())
|
|
|
|
std::cout << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::showDevices(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-07-28 00:43:21 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
|
|
|
|
2020-08-10 18:37:33 +00:00
|
|
|
auto devices = this->m_ipcBridge.devices();
|
|
|
|
|
|
|
|
if (devices.empty())
|
|
|
|
return 0;
|
|
|
|
|
2021-06-19 16:45:59 +00:00
|
|
|
std::sort(devices.begin(), devices.end());
|
|
|
|
|
2020-07-28 00:43:21 +00:00
|
|
|
if (this->m_parseable) {
|
2020-08-10 18:37:33 +00:00
|
|
|
for (auto &device: devices)
|
2020-07-28 00:43:21 +00:00
|
|
|
std::cout << device << std::endl;
|
|
|
|
} else {
|
2020-09-09 15:21:03 +00:00
|
|
|
std::vector<std::string> table {
|
|
|
|
"Device",
|
|
|
|
"Description"
|
|
|
|
};
|
|
|
|
auto columns = table.size();
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2020-08-10 18:37:33 +00:00
|
|
|
for (auto &device: devices) {
|
2020-09-09 15:21:03 +00:00
|
|
|
table.push_back(device);
|
2020-10-06 23:06:54 +00:00
|
|
|
table.push_back(this->m_ipcBridge.description(device));
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
this->drawTable(table, columns);
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::addDevice(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-08-22 00:31:45 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
2020-07-28 00:43:21 +00:00
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Device description not provided." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2021-06-19 16:45:59 +00:00
|
|
|
auto deviceId = this->flagValue(flags, "add-device", "-i");
|
|
|
|
deviceId = this->m_ipcBridge.addDevice(args[1], deviceId);
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
if (deviceId.empty()) {
|
|
|
|
std::cerr << "Failed to create device." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EIO;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this->m_parseable)
|
|
|
|
std::cout << deviceId << std::endl;
|
|
|
|
else
|
|
|
|
std::cout << "Device created as " << deviceId << std::endl;
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::removeDevice(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-07-28 00:43:21 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Device not provided." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-16 19:33:16 +00:00
|
|
|
auto deviceId = args[1];
|
2020-08-10 18:37:33 +00:00
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto it = std::find(devices.begin(), devices.end(), deviceId);
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
if (it == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this->m_ipcBridge.removeDevice(args[1]);
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-10 18:37:33 +00:00
|
|
|
int AkVCam::CmdParserPrivate::removeDevices(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
|
|
|
auto devices = this->m_ipcBridge.devices();
|
|
|
|
|
|
|
|
for (auto &device: devices)
|
|
|
|
this->m_ipcBridge.removeDevice(device);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
int AkVCam::CmdParserPrivate::showDeviceDescription(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-07-28 00:43:21 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Device not provided." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-16 19:33:16 +00:00
|
|
|
auto deviceId = args[1];
|
2020-08-10 18:37:33 +00:00
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto it = std::find(devices.begin(), devices.end(), deviceId);
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
if (it == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 23:06:54 +00:00
|
|
|
std::cout << this->m_ipcBridge.description(args[1]) << std::endl;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-10 18:37:33 +00:00
|
|
|
int AkVCam::CmdParserPrivate::setDeviceDescription(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 3) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto deviceId = args[1];
|
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
|
|
|
if (dit == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 23:06:54 +00:00
|
|
|
this->m_ipcBridge.setDescription(deviceId, args[2]);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
int AkVCam::CmdParserPrivate::showSupportedFormats(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-07-28 00:43:21 +00:00
|
|
|
UNUSED(args);
|
|
|
|
|
|
|
|
auto type =
|
|
|
|
this->containsFlag(flags, "supported-formats", "-i")?
|
2020-08-22 00:31:45 +00:00
|
|
|
IpcBridge::StreamTypeInput:
|
|
|
|
IpcBridge::StreamTypeOutput;
|
2020-07-28 00:43:21 +00:00
|
|
|
auto formats = this->m_ipcBridge.supportedPixelFormats(type);
|
|
|
|
|
|
|
|
if (!this->m_parseable) {
|
2020-08-22 00:31:45 +00:00
|
|
|
if (type == IpcBridge::StreamTypeInput)
|
2020-07-28 00:43:21 +00:00
|
|
|
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;
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-06 04:45:40 +00:00
|
|
|
int AkVCam::CmdParserPrivate::showDefaultFormat(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(args);
|
|
|
|
|
|
|
|
auto type =
|
|
|
|
this->containsFlag(flags, "default-format", "-i")?
|
|
|
|
IpcBridge::StreamTypeInput:
|
|
|
|
IpcBridge::StreamTypeOutput;
|
|
|
|
auto format = this->m_ipcBridge.defaultPixelFormat(type);
|
|
|
|
std::cout << VideoFormat::stringFromFourcc(format) << std::endl;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
int AkVCam::CmdParserPrivate::showFormats(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-07-28 00:43:21 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Device not provided." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-16 19:33:16 +00:00
|
|
|
auto deviceId = args[1];
|
2020-08-10 18:37:33 +00:00
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto it = std::find(devices.begin(), devices.end(), deviceId);
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
if (it == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::addFormat(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-07-28 00:43:21 +00:00
|
|
|
if (args.size() < 6) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto deviceId = args[1];
|
2020-08-10 18:37:33 +00:00
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
if (dit == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto format = VideoFormat::fourccFromString(args[2]);
|
|
|
|
|
|
|
|
if (!format) {
|
|
|
|
std::cerr << "Invalid pixel format." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2021-02-06 04:45:40 +00:00
|
|
|
auto formats =
|
|
|
|
this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
|
2020-07-28 00:43:21 +00:00
|
|
|
auto fit = std::find(formats.begin(), formats.end(), format);
|
|
|
|
|
|
|
|
if (fit == formats.end()) {
|
2020-08-22 00:31:45 +00:00
|
|
|
std::cerr << "Format not supported." << std::endl;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *p = nullptr;
|
|
|
|
auto width = strtoul(args[3].c_str(), &p, 10);
|
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
std::cerr << "Width must be an unsigned integer." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
p = nullptr;
|
|
|
|
auto height = strtoul(args[4].c_str(), &p, 10);
|
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
std::cerr << "Height must be an unsigned integer." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Fraction fps(args[5]);
|
|
|
|
|
|
|
|
if (fps.num() < 1 || fps.den() < 1) {
|
|
|
|
std::cerr << "Invalid frame rate." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto indexStr = this->flagValue(flags, "add-format", "-i");
|
|
|
|
int index = -1;
|
|
|
|
|
|
|
|
if (!indexStr.empty()) {
|
|
|
|
p = nullptr;
|
2020-10-08 23:07:51 +00:00
|
|
|
index = int(strtoul(indexStr.c_str(), &p, 10));
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
std::cerr << "Index must be an unsigned integer." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-08 23:07:51 +00:00
|
|
|
VideoFormat fmt(format, int(width), int(height), {fps});
|
2020-07-28 00:43:21 +00:00
|
|
|
this->m_ipcBridge.addFormat(deviceId, fmt, index);
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::removeFormat(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-08-10 18:37:33 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
2020-07-28 00:43:21 +00:00
|
|
|
if (args.size() < 3) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto deviceId = args[1];
|
2020-08-10 18:37:33 +00:00
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
if (dit == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char *p = nullptr;
|
|
|
|
auto index = strtoul(args[2].c_str(), &p, 10);
|
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
std::cerr << "Index must be an unsigned integer." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto formats = this->m_ipcBridge.formats(deviceId);
|
|
|
|
|
|
|
|
if (index >= formats.size()) {
|
|
|
|
std::cerr << "Index is out of range." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ERANGE;
|
2020-07-28 00:43:21 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 23:07:51 +00:00
|
|
|
this->m_ipcBridge.removeFormat(deviceId, int(index));
|
2020-07-28 00:43:21 +00:00
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-10 18:37:33 +00:00
|
|
|
int AkVCam::CmdParserPrivate::removeFormats(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto deviceId = args[1];
|
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
|
|
|
if (dit == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this->m_ipcBridge.setFormats(deviceId, {});
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
int AkVCam::CmdParserPrivate::update(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-08-10 18:37:33 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
2020-09-09 15:21:03 +00:00
|
|
|
this->m_ipcBridge.updateDevices();
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-28 00:43:21 +00:00
|
|
|
int AkVCam::CmdParserPrivate::loadSettings(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
2020-09-09 15:21:03 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Settings file not provided." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Settings settings;
|
|
|
|
|
|
|
|
if (!settings.load(args[1])) {
|
|
|
|
std::cerr << "Settings file not valid." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EIO;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this->loadGenerals(settings);
|
|
|
|
auto devices = this->m_ipcBridge.devices();
|
|
|
|
|
|
|
|
for (auto &device: devices)
|
|
|
|
this->m_ipcBridge.removeDevice(device);
|
|
|
|
|
|
|
|
this->createDevices(settings, this->readFormats(settings));
|
|
|
|
|
2020-07-28 00:43:21 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
int AkVCam::CmdParserPrivate::stream(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
2020-07-11 17:22:23 +00:00
|
|
|
{
|
2020-08-10 18:37:33 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (args.size() < 5) {
|
2020-08-10 18:37:33 +00:00
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto deviceId = args[1];
|
|
|
|
auto devices = this->m_ipcBridge.devices();
|
2020-08-16 19:33:16 +00:00
|
|
|
auto dit = std::find(devices.begin(), devices.end(), deviceId);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
|
|
|
if (dit == devices.end()) {
|
2020-08-16 19:33:16 +00:00
|
|
|
std::cerr << "'" << deviceId << "' doesn't exists." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
auto format = VideoFormat::fourccFromString(args[2]);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (!format) {
|
|
|
|
std::cerr << "Invalid pixel format." << std::endl;
|
2020-07-11 17:22:23 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-22 00:31:45 +00:00
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-02-06 04:45:40 +00:00
|
|
|
auto formats =
|
|
|
|
this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
|
2020-08-22 00:31:45 +00:00
|
|
|
auto fit = std::find(formats.begin(), formats.end(), format);
|
|
|
|
|
|
|
|
if (fit == formats.end()) {
|
|
|
|
std::cerr << "Format not supported." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
char *p = nullptr;
|
|
|
|
auto width = strtoul(args[3].c_str(), &p, 10);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (*p) {
|
|
|
|
std::cerr << "Width must be an unsigned integer." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
p = nullptr;
|
|
|
|
auto height = strtoul(args[4].c_str(), &p, 10);
|
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
std::cerr << "Height must be an unsigned integer." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2021-05-24 22:47:39 +00:00
|
|
|
auto fpsStr = this->flagValue(flags, "stream", "-f");
|
|
|
|
double fps = std::numeric_limits<double>::quiet_NaN();
|
|
|
|
|
|
|
|
if (!fpsStr.empty()) {
|
|
|
|
p = nullptr;
|
|
|
|
fps = int(strtod(fpsStr.c_str(), &p));
|
|
|
|
|
|
|
|
if (*p) {
|
|
|
|
if (!Fraction::isFraction(fpsStr)) {
|
|
|
|
std::cerr << "The framerate must be a number or a fraction." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2021-05-24 22:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fps = Fraction(fpsStr).value();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fps <= 0 || std::isinf(fps)) {
|
|
|
|
std::cerr << "The framerate is out of range." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ERANGE;
|
2021-05-24 22:47:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-08 23:07:51 +00:00
|
|
|
VideoFormat fmt(format, int(width), int(height), {{30, 1}});
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (!this->m_ipcBridge.deviceStart(deviceId, fmt)) {
|
|
|
|
std::cerr << "Can't start stream." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EIO;
|
2020-08-22 00:31:45 +00:00
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
static bool exit = false;
|
|
|
|
auto signalHandler = [] (int) {
|
|
|
|
exit = true;
|
|
|
|
};
|
|
|
|
signal(SIGINT, signalHandler);
|
|
|
|
signal(SIGTERM, signalHandler);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
AkVCam::VideoFrame frame(fmt);
|
|
|
|
size_t bufferSize = 0;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-02-06 04:45:40 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
// Set std::cin in binary mode.
|
|
|
|
_setmode(_fileno(stdin), _O_BINARY);
|
|
|
|
#endif
|
|
|
|
|
2021-05-24 22:47:39 +00:00
|
|
|
auto clock = [] (const std::chrono::time_point<std::chrono::high_resolution_clock> &since) -> double {
|
|
|
|
return std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now() - since).count();
|
|
|
|
};
|
|
|
|
|
|
|
|
const double minThreshold = 0.04;
|
|
|
|
const double maxThreshold = 0.1;
|
|
|
|
const double framedupThreshold = 0.1;
|
|
|
|
const double nosyncThreshold = 10.0;
|
|
|
|
|
|
|
|
double lastPts = 0.0;
|
|
|
|
auto t0 = std::chrono::high_resolution_clock::now();
|
|
|
|
double drift = 0;
|
|
|
|
uint64_t i = 0;
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
do {
|
|
|
|
std::cin.read(reinterpret_cast<char *>(frame.data().data()
|
|
|
|
+ bufferSize),
|
2020-10-08 23:07:51 +00:00
|
|
|
std::streamsize(frame.data().size() - bufferSize));
|
|
|
|
bufferSize += size_t(std::cin.gcount());
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (bufferSize == frame.data().size()) {
|
2021-05-24 22:47:39 +00:00
|
|
|
if (fpsStr.empty()) {
|
|
|
|
this->m_ipcBridge.write(deviceId, frame);
|
|
|
|
} else {
|
|
|
|
double pts = double(i) / fps;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
double clock_pts = clock(t0) + drift;
|
|
|
|
double diff = pts - clock_pts;
|
|
|
|
double delay = pts - lastPts;
|
|
|
|
double syncThreshold =
|
|
|
|
std::max(minThreshold,
|
|
|
|
std::min(delay, maxThreshold));
|
|
|
|
|
|
|
|
if (!std::isnan(diff)
|
|
|
|
&& std::abs(diff) < nosyncThreshold
|
|
|
|
&& delay < framedupThreshold) {
|
|
|
|
if (diff <= -syncThreshold) {
|
|
|
|
lastPts = pts;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (diff > syncThreshold) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::duration<double>(diff - syncThreshold));
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
drift = clock(t0) - pts;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->m_ipcBridge.write(deviceId, frame);
|
|
|
|
lastPts = pts;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
bufferSize = 0;
|
|
|
|
}
|
|
|
|
} while (!std::cin.eof() && !exit);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
this->m_ipcBridge.deviceStop(deviceId);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-07-11 17:22:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-06 04:45:40 +00:00
|
|
|
int AkVCam::CmdParserPrivate::listenEvents(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
|
|
|
|
|
|
|
auto serverStateChanged = [] (void *, IpcBridge::ServerState state) {
|
|
|
|
if (state == IpcBridge::ServerStateAvailable)
|
|
|
|
std::cout << "ServerAvailable" << std::endl;
|
|
|
|
else
|
|
|
|
std::cout << "ServerGone" << std::endl;
|
|
|
|
};
|
|
|
|
auto devicesChanged = [] (void *, const std::vector<std::string> &) {
|
|
|
|
std::cout << "DevicesUpdated" << std::endl;
|
|
|
|
};
|
|
|
|
auto pictureChanged = [] (void *, const std::string &) {
|
|
|
|
std::cout << "PictureUpdated" << std::endl;
|
|
|
|
};
|
|
|
|
|
|
|
|
this->m_ipcBridge.connectServerStateChanged(this, serverStateChanged);
|
|
|
|
this->m_ipcBridge.connectDevicesChanged(this, devicesChanged);
|
|
|
|
this->m_ipcBridge.connectPictureChanged(this, pictureChanged);
|
|
|
|
|
|
|
|
static bool exit = false;
|
|
|
|
auto signalHandler = [] (int) {
|
|
|
|
exit = true;
|
|
|
|
};
|
|
|
|
signal(SIGINT, signalHandler);
|
|
|
|
signal(SIGTERM, signalHandler);
|
|
|
|
|
|
|
|
while (!exit)
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
int AkVCam::CmdParserPrivate::showControls(const StringMap &flags,
|
2020-09-09 15:21:03 +00:00
|
|
|
const StringVector &args)
|
2020-07-11 17:22:23 +00:00
|
|
|
{
|
2020-09-09 15:21:03 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Device not provided." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this->m_parseable) {
|
|
|
|
for (auto &control: this->m_ipcBridge.controls(deviceId))
|
|
|
|
std::cout << control.id << std::endl;
|
|
|
|
} else {
|
|
|
|
auto typeStr = typeStrMap();
|
|
|
|
|
|
|
|
std::vector<std::string> table {
|
|
|
|
"Control",
|
|
|
|
"Description",
|
|
|
|
"Type",
|
|
|
|
"Minimum",
|
|
|
|
"Maximum",
|
|
|
|
"Step",
|
|
|
|
"Default",
|
|
|
|
"Value"
|
|
|
|
};
|
|
|
|
auto columns = table.size();
|
|
|
|
|
|
|
|
for (auto &control: this->m_ipcBridge.controls(deviceId)) {
|
|
|
|
table.push_back(control.id);
|
|
|
|
table.push_back(control.description);
|
|
|
|
table.push_back(typeStr[control.type]);
|
|
|
|
table.push_back(std::to_string(control.minimum));
|
|
|
|
table.push_back(std::to_string(control.maximum));
|
|
|
|
table.push_back(std::to_string(control.step));
|
|
|
|
table.push_back(std::to_string(control.defaultValue));
|
|
|
|
table.push_back(std::to_string(control.value));
|
|
|
|
}
|
|
|
|
|
|
|
|
this->drawTable(table, columns);
|
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
int AkVCam::CmdParserPrivate::readControl(const StringMap &flags,
|
2020-09-09 15:21:03 +00:00
|
|
|
const StringVector &args)
|
2020-08-22 00:31:45 +00:00
|
|
|
{
|
2020-09-09 15:21:03 +00:00
|
|
|
if (args.size() < 3) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &control: this->m_ipcBridge.controls(deviceId))
|
2020-09-10 20:26:08 +00:00
|
|
|
if (control.id == args[2]) {
|
2020-09-09 15:21:03 +00:00
|
|
|
if (flags.empty()) {
|
|
|
|
std::cout << control.value << std::endl;
|
|
|
|
} else {
|
|
|
|
if (this->containsFlag(flags, "get-control", "-c")) {
|
|
|
|
std::cout << control.description << std::endl;
|
|
|
|
}
|
2021-05-24 22:47:39 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
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")) {
|
2021-02-06 04:45:40 +00:00
|
|
|
for (size_t i = 0; i < control.menu.size(); i++)
|
2020-09-09 15:21:03 +00:00
|
|
|
if (this->m_parseable)
|
|
|
|
std::cout << control.menu[i] << std::endl;
|
|
|
|
else
|
2021-02-06 04:45:40 +00:00
|
|
|
std::cout << i
|
|
|
|
<< ": "
|
|
|
|
<< control.menu[i]
|
|
|
|
<< std::endl;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cerr << "'" << args[2] << "' control not available." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENOSYS;
|
2020-08-22 00:31:45 +00:00
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
int AkVCam::CmdParserPrivate::writeControls(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
2020-08-22 00:31:45 +00:00
|
|
|
{
|
2020-09-09 15:21:03 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 3) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENODEV;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::map<std::string, int> controls;
|
|
|
|
|
|
|
|
for (size_t i = 2; i < args.size(); i++) {
|
|
|
|
if (args[i].find('=') == std::string::npos) {
|
|
|
|
std::cerr << "Argumment "
|
|
|
|
<< i
|
|
|
|
<< " is not in the form KEY=VALUE."
|
|
|
|
<< std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto pair = splitOnce(args[i], "=");
|
|
|
|
|
|
|
|
if (pair.first.empty()) {
|
|
|
|
std::cerr << "Key for argumment "
|
|
|
|
<< i
|
|
|
|
<< " is emty."
|
|
|
|
<< std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 23:07:51 +00:00
|
|
|
controls[key] = int(it - control.menu.begin());
|
2020-09-09 15:21:03 +00:00
|
|
|
} else {
|
|
|
|
if (val >= control.menu.size()) {
|
|
|
|
std::cerr << "Value at argument "
|
|
|
|
<< i
|
|
|
|
<< " is out of range."
|
|
|
|
<< std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ERANGE;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-08 23:07:51 +00:00
|
|
|
controls[key] = int(val);
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
std::cerr << "No such '"
|
|
|
|
<< key
|
|
|
|
<< "' control in argument "
|
|
|
|
<< i
|
|
|
|
<< "."
|
|
|
|
<< std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENOSYS;
|
2020-09-09 15:21:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this->m_ipcBridge.setControls(deviceId, controls);
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
int AkVCam::CmdParserPrivate::picture(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-10-06 23:06:54 +00:00
|
|
|
std::cout << this->m_ipcBridge.picture() << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
int AkVCam::CmdParserPrivate::setPicture(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 23:06:54 +00:00
|
|
|
this->m_ipcBridge.setPicture(args[1]);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
int AkVCam::CmdParserPrivate::logLevel(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
auto level = this->m_ipcBridge.logLevel();
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (this->m_parseable)
|
|
|
|
std::cout << level << std::endl;
|
|
|
|
else
|
|
|
|
std::cout << AkVCam::Logger::levelToString(level) << std::endl;
|
2020-08-10 18:37:33 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
int AkVCam::CmdParserPrivate::setLogLevel(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
2020-08-10 18:37:33 +00:00
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
auto levelStr = args[1];
|
|
|
|
char *p = nullptr;
|
|
|
|
auto level = strtol(levelStr.c_str(), &p, 10);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
if (*p)
|
|
|
|
level = AkVCam::Logger::levelFromString(levelStr);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
2020-08-22 00:31:45 +00:00
|
|
|
this->m_ipcBridge.setLogLevel(level);
|
2020-07-11 17:22:23 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::showClients(const StringMap &flags,
|
|
|
|
const StringVector &args)
|
|
|
|
{
|
2020-08-10 18:37:33 +00:00
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
|
|
|
auto clients = this->m_ipcBridge.clientsPids();
|
|
|
|
|
|
|
|
if (clients.empty())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (this->m_parseable) {
|
|
|
|
for (auto &pid: clients)
|
|
|
|
std::cout << pid
|
|
|
|
<< " "
|
|
|
|
<< this->m_ipcBridge.clientExe(pid)
|
|
|
|
<< std::endl;
|
|
|
|
} else {
|
2020-09-09 15:21:03 +00:00
|
|
|
std::vector<std::string> table {
|
|
|
|
"Pid",
|
|
|
|
"Executable"
|
|
|
|
};
|
|
|
|
auto columns = table.size();
|
2020-08-10 18:37:33 +00:00
|
|
|
|
|
|
|
for (auto &pid: clients) {
|
2020-09-09 15:21:03 +00:00
|
|
|
table.push_back(std::to_string(pid));
|
|
|
|
table.push_back(this->m_ipcBridge.clientExe(pid));
|
2020-08-10 18:37:33 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
this->drawTable(table, columns);
|
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2021-02-06 04:45:40 +00:00
|
|
|
int AkVCam::CmdParserPrivate::dumpInfo(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
|
|
|
static const auto indent = 4 * std::string(" ");
|
|
|
|
|
|
|
|
std::cout << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << std::endl;
|
|
|
|
std::cout << "<info>" << std::endl;
|
|
|
|
std::cout << indent << "<devices>" << std::endl;
|
|
|
|
|
|
|
|
auto devices = this->m_ipcBridge.devices();
|
|
|
|
|
|
|
|
for (auto &device: devices) {
|
|
|
|
std::cout << 2 * indent << "<device>" << std::endl;
|
|
|
|
std::cout << 3 * indent << "<id>" << device << "</id>" << std::endl;
|
|
|
|
std::cout << 3 * indent
|
|
|
|
<< "<description>"
|
|
|
|
<< this->m_ipcBridge.description(device)
|
|
|
|
<< "</description>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 3 * indent << "<formats>" << std::endl;
|
|
|
|
|
|
|
|
for (auto &format: this->m_ipcBridge.formats(device)) {
|
|
|
|
std::cout << 4 * indent << "<format>" << std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<pixel-format>"
|
|
|
|
<< VideoFormat::stringFromFourcc(format.fourcc())
|
|
|
|
<< "</pixel-format>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<width>"
|
|
|
|
<< format.width()
|
|
|
|
<< "</width>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<height>"
|
|
|
|
<< format.height()
|
|
|
|
<< "</height>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<fps>"
|
|
|
|
<< format.minimumFrameRate().toString()
|
|
|
|
<< "</fps>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 4 * indent << "</format>" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << 3 * indent << "</formats>" << std::endl;
|
|
|
|
std::cout << 3 * indent << "<controls>" << std::endl;
|
|
|
|
|
|
|
|
for (auto &control: this->m_ipcBridge.controls(device)) {
|
|
|
|
std::cout << 4 * indent << "<control>" << std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<id>"
|
|
|
|
<< control.id
|
|
|
|
<< "</id>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<description>"
|
|
|
|
<< control.description
|
|
|
|
<< "</description>"
|
|
|
|
<< std::endl;
|
|
|
|
auto typeStr = typeStrMap();
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<type>"
|
|
|
|
<< typeStr[control.type]
|
|
|
|
<< "</type>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<minimum>"
|
|
|
|
<< control.minimum
|
|
|
|
<< "</minimum>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<maximum>"
|
|
|
|
<< control.maximum
|
|
|
|
<< "</maximum>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<step>"
|
|
|
|
<< control.step
|
|
|
|
<< "</step>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<default-value>"
|
|
|
|
<< control.defaultValue
|
|
|
|
<< "</default-value>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 5 * indent
|
|
|
|
<< "<value>"
|
|
|
|
<< control.value
|
|
|
|
<< "</value>"
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
if (!control.menu.empty() && control.type == ControlTypeMenu) {
|
|
|
|
std::cout << 5 * indent << "<menu>" << std::endl;
|
|
|
|
|
|
|
|
for (auto &item: control.menu)
|
|
|
|
std::cout << 6 * indent
|
|
|
|
<< "<item>"
|
|
|
|
<< item
|
|
|
|
<< "</item>"
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
std::cout << 5 * indent << "</menu>" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << 4 * indent << "</control>" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << 3 * indent << "</controls>" << std::endl;
|
|
|
|
std::cout << 2 * indent << "</device>" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << indent << "</devices>" << std::endl;
|
|
|
|
std::cout << indent << "<input-formats>" << std::endl;
|
|
|
|
|
|
|
|
for (auto &format: this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeInput))
|
|
|
|
std::cout << 2 * indent
|
|
|
|
<< "<pixel-format>"
|
|
|
|
<< VideoFormat::stringFromFourcc(format)
|
|
|
|
<< "</pixel-format>"
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
std::cout << indent << "</input-formats>" << std::endl;
|
|
|
|
|
|
|
|
auto defInputFormat =
|
|
|
|
this->m_ipcBridge.defaultPixelFormat(IpcBridge::StreamTypeInput);
|
|
|
|
std::cout << indent
|
|
|
|
<< "<default-input-format>"
|
|
|
|
<< VideoFormat::stringFromFourcc(defInputFormat)
|
|
|
|
<< "</default-input-format>"
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
std::cout << indent << "<output-formats>" << std::endl;
|
|
|
|
|
|
|
|
for (auto &format: this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput))
|
|
|
|
std::cout << 2 * indent
|
|
|
|
<< "<pixel-format>"
|
|
|
|
<< VideoFormat::stringFromFourcc(format)
|
|
|
|
<< "</pixel-format>"
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
std::cout << indent << "</output-formats>" << std::endl;
|
|
|
|
|
|
|
|
auto defOutputFormat =
|
|
|
|
this->m_ipcBridge.defaultPixelFormat(IpcBridge::StreamTypeOutput);
|
|
|
|
std::cout << indent
|
|
|
|
<< "<default-output-format>"
|
|
|
|
<< VideoFormat::stringFromFourcc(defOutputFormat)
|
|
|
|
<< "</default-output-format>"
|
|
|
|
<< std::endl;
|
|
|
|
|
|
|
|
std::cout << indent << "<clients>" << std::endl;
|
|
|
|
|
|
|
|
for (auto &pid: this->m_ipcBridge.clientsPids()) {
|
|
|
|
std::cout << 2 * indent << "<client>" << std::endl;
|
|
|
|
std::cout << 3 * indent << "<pid>" << pid << "</pid>" << std::endl;
|
|
|
|
std::cout << 3 * indent
|
|
|
|
<< "<exe>"
|
|
|
|
<< this->m_ipcBridge.clientExe(pid)
|
|
|
|
<< "</exe>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << 2 * indent << "</client>" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << indent << "</clients>" << std::endl;
|
|
|
|
std::cout << indent
|
|
|
|
<< "<picture>"
|
|
|
|
<< this->m_ipcBridge.picture()
|
|
|
|
<< "</picture>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << indent
|
|
|
|
<< "<loglevel>"
|
|
|
|
<< this->m_ipcBridge.logLevel()
|
|
|
|
<< "</loglevel>"
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << "</info>" << std::endl;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-24 22:47:39 +00:00
|
|
|
int AkVCam::CmdParserPrivate::hacks(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
UNUSED(flags);
|
|
|
|
UNUSED(args);
|
|
|
|
|
|
|
|
auto hacks = this->m_ipcBridge.hacks();
|
|
|
|
|
|
|
|
if (hacks.empty())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (this->m_parseable) {
|
|
|
|
for (auto &hack: hacks)
|
|
|
|
std::cout << hack << std::endl;
|
|
|
|
} else {
|
|
|
|
std::cout << "Hacks are intended to fix common problems with the "
|
|
|
|
"virtual camera, and are intended to be used by developers "
|
|
|
|
"and advanced users only." << std::endl;
|
|
|
|
std::cout << std::endl;
|
|
|
|
std::cout << "WARNING: Unsafe hacks can brick your system, make it "
|
2021-06-16 19:49:23 +00:00
|
|
|
"unstable, or expose it to a serious security risk, "
|
|
|
|
"remember to make a backup of your files and system. You "
|
2021-05-24 22:47:39 +00:00
|
|
|
"are solely responsible of whatever happens for using "
|
|
|
|
"them. You been warned, don't come and cry later."
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << std::endl;
|
|
|
|
std::vector<std::string> table {
|
|
|
|
"Hack",
|
|
|
|
"Is safe?",
|
|
|
|
"Description"
|
|
|
|
};
|
|
|
|
auto columns = table.size();
|
|
|
|
|
|
|
|
for (auto &hack: hacks) {
|
|
|
|
table.push_back(hack);
|
|
|
|
table.push_back(this->m_ipcBridge.hackIsSafe(hack)? "Yes": "No");
|
|
|
|
table.push_back(this->m_ipcBridge.hackDescription(hack));
|
|
|
|
}
|
|
|
|
|
|
|
|
this->drawTable(table, columns);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::hackInfo(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2021-05-24 22:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto hack = args[1];
|
|
|
|
auto hacks = this->m_ipcBridge.hacks();
|
|
|
|
auto dit = std::find(hacks.begin(), hacks.end(), hack);
|
|
|
|
|
|
|
|
if (dit == hacks.end()) {
|
|
|
|
std::cerr << "Unknown hack: " << hack << "." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENOSYS;
|
2021-05-24 22:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this->containsFlag(flags, "hack-info", "-c"))
|
|
|
|
std::cout << this->m_ipcBridge.hackDescription(hack) << std::endl;
|
|
|
|
|
|
|
|
if (this->containsFlag(flags, "hack-info", "-s")) {
|
|
|
|
if (this->m_ipcBridge.hackIsSafe(hack))
|
|
|
|
std::cout << "Yes" << std::endl;
|
|
|
|
else
|
|
|
|
std::cout << "No" << std::endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int AkVCam::CmdParserPrivate::hack(const AkVCam::StringMap &flags,
|
|
|
|
const AkVCam::StringVector &args)
|
|
|
|
{
|
|
|
|
if (args.size() < 2) {
|
|
|
|
std::cerr << "Not enough arguments." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EINVAL;
|
2021-05-24 22:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto hack = args[1];
|
|
|
|
auto hacks = this->m_ipcBridge.hacks();
|
|
|
|
auto dit = std::find(hacks.begin(), hacks.end(), hack);
|
|
|
|
|
|
|
|
if (dit == hacks.end()) {
|
|
|
|
std::cerr << "Unknown hack: " << hack << "." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -ENOSYS;
|
2021-05-24 22:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool accepted = this->m_parseable | this->m_ipcBridge.hackIsSafe(hack);
|
|
|
|
|
|
|
|
if (!accepted && !this->m_parseable) {
|
|
|
|
std::cout << "WARNING: Applying this hack can brick your system, make "
|
2021-06-16 19:49:23 +00:00
|
|
|
"it unstable, or expose it to a serious security risk, "
|
|
|
|
"remember to make a backup of your files and system. "
|
2021-05-24 22:47:39 +00:00
|
|
|
"Agreeing to continue, you accept the full responsability "
|
|
|
|
"of whatever happens from now on."
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << std::endl;
|
|
|
|
|
|
|
|
if (this->containsFlag(flags, "hack", "-y")) {
|
|
|
|
std::cout << "You agreed to continue from command line."
|
|
|
|
<< std::endl;
|
|
|
|
std::cout << std::endl;
|
|
|
|
accepted = true;
|
|
|
|
} else {
|
|
|
|
std::cout << "If you agree to continue write YES: ";
|
|
|
|
std::string answer;
|
|
|
|
std::cin >> answer;
|
|
|
|
std::cout << std::endl;
|
|
|
|
accepted = answer == "YES";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!accepted) {
|
|
|
|
std::cerr << "Hack not applied." << std::endl;
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
return -EIO;
|
2021-05-24 22:47:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
StringVector hargs;
|
|
|
|
|
|
|
|
for (size_t i = 2; i < args.size(); i++)
|
|
|
|
hargs.push_back(args[i]);
|
|
|
|
|
|
|
|
auto result = this->m_ipcBridge.execHack(hack, hargs);
|
|
|
|
|
|
|
|
if (result == 0)
|
|
|
|
std::cout << "Success" << std::endl;
|
|
|
|
else
|
|
|
|
std::cout << "Failed" << std::endl;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
void AkVCam::CmdParserPrivate::loadGenerals(Settings &settings)
|
|
|
|
{
|
|
|
|
settings.beginGroup("General");
|
|
|
|
|
2020-10-06 23:06:54 +00:00
|
|
|
if (settings.contains("default_frame"))
|
|
|
|
this->m_ipcBridge.setPicture(settings.value("default_frame"));
|
2020-08-10 18:37:33 +00:00
|
|
|
|
2020-09-09 15:21:03 +00:00
|
|
|
if (settings.contains("loglevel")) {
|
|
|
|
auto logLevel= settings.value("loglevel");
|
|
|
|
char *p = nullptr;
|
|
|
|
auto level = strtol(logLevel.c_str(), &p, 10);
|
|
|
|
|
|
|
|
if (*p)
|
|
|
|
level = AkVCam::Logger::levelFromString(logLevel);
|
|
|
|
|
|
|
|
this->m_ipcBridge.setLogLevel(level);
|
|
|
|
}
|
|
|
|
|
|
|
|
settings.endGroup();
|
|
|
|
}
|
|
|
|
|
|
|
|
AkVCam::VideoFormatMatrix AkVCam::CmdParserPrivate::readFormats(Settings &settings)
|
|
|
|
{
|
|
|
|
VideoFormatMatrix formatsMatrix;
|
|
|
|
settings.beginGroup("Formats");
|
|
|
|
auto nFormats = settings.beginArray("formats");
|
|
|
|
|
|
|
|
for (size_t i = 0; i < nFormats; i++) {
|
|
|
|
settings.setArrayIndex(i);
|
|
|
|
formatsMatrix.push_back(this->readFormat(settings));
|
|
|
|
}
|
|
|
|
|
|
|
|
settings.endArray();
|
|
|
|
settings.endGroup();
|
|
|
|
|
|
|
|
return formatsMatrix;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<AkVCam::VideoFormat> AkVCam::CmdParserPrivate::readFormat(Settings &settings)
|
|
|
|
{
|
|
|
|
std::vector<AkVCam::VideoFormat> formats;
|
|
|
|
|
|
|
|
auto pixFormats = settings.valueList("format", ",");
|
|
|
|
auto widths = settings.valueList("width", ",");
|
|
|
|
auto heights = settings.valueList("height", ",");
|
|
|
|
auto frameRates = settings.valueList("fps", ",");
|
|
|
|
|
|
|
|
if (pixFormats.empty()
|
|
|
|
|| widths.empty()
|
|
|
|
|| heights.empty()
|
|
|
|
|| frameRates.empty()) {
|
|
|
|
std::cerr << "Error reading formats." << std::endl;
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
StringMatrix formatMatrix;
|
|
|
|
formatMatrix.push_back(pixFormats);
|
|
|
|
formatMatrix.push_back(widths);
|
|
|
|
formatMatrix.push_back(heights);
|
|
|
|
formatMatrix.push_back(frameRates);
|
|
|
|
|
|
|
|
for (auto &format_list: this->matrixCombine(formatMatrix)) {
|
|
|
|
auto pixFormat = VideoFormat::fourccFromString(format_list[0]);
|
|
|
|
char *p = nullptr;
|
|
|
|
auto width = strtol(format_list[1].c_str(), &p, 10);
|
|
|
|
p = nullptr;
|
|
|
|
auto height = strtol(format_list[2].c_str(), &p, 10);
|
|
|
|
Fraction frame_rate(format_list[3]);
|
|
|
|
VideoFormat format(pixFormat,
|
|
|
|
width,
|
|
|
|
height,
|
2021-02-06 04:45:40 +00:00
|
|
|
{frame_rate});
|
2020-09-09 15:21:03 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-06-21 21:25:48 +00:00
|
|
|
auto deviceId = settings.value("id");
|
|
|
|
deviceId = this->m_ipcBridge.addDevice(description, deviceId);
|
2021-02-06 04:45:40 +00:00
|
|
|
auto supportedFormats =
|
|
|
|
this->m_ipcBridge.supportedPixelFormats(IpcBridge::StreamTypeOutput);
|
2020-09-09 15:21:03 +00:00
|
|
|
|
|
|
|
for (auto &format: formats) {
|
|
|
|
auto it = std::find(supportedFormats.begin(),
|
|
|
|
supportedFormats.end(),
|
|
|
|
format.fourcc());
|
|
|
|
|
|
|
|
if (it != supportedFormats.end())
|
|
|
|
this->m_ipcBridge.addFormat(deviceId, format, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<AkVCam::VideoFormat> AkVCam::CmdParserPrivate::readDeviceFormats(Settings &settings,
|
|
|
|
const VideoFormatMatrix &availableFormats)
|
|
|
|
{
|
|
|
|
std::vector<AkVCam::VideoFormat> formats;
|
|
|
|
auto formatsIndex = settings.valueList("formats", ",");
|
|
|
|
|
|
|
|
for (auto &indexStr: formatsIndex) {
|
|
|
|
char *p = nullptr;
|
|
|
|
auto index = strtoul(indexStr.c_str(), &p, 10);
|
|
|
|
|
|
|
|
if (*p)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
index--;
|
|
|
|
|
|
|
|
if (index >= availableFormats.size())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (auto &format: availableFormats[index])
|
|
|
|
formats.push_back(format);
|
|
|
|
}
|
|
|
|
|
|
|
|
return formats;
|
2020-07-11 17:22:23 +00:00
|
|
|
}
|
2020-07-28 00:43:21 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
2021-02-06 04:45:40 +00:00
|
|
|
|
|
|
|
std::string AkVCam::operator *(size_t n, const std::string &str)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
|
|
ss << str;
|
|
|
|
|
|
|
|
return ss.str();
|
|
|
|
}
|
2021-05-24 22:47:39 +00:00
|
|
|
|
|
|
|
AkVCam::CmdParserCommand::CmdParserCommand()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
AkVCam::CmdParserCommand::CmdParserCommand(const std::string &command,
|
|
|
|
const std::string &arguments,
|
|
|
|
const std::string &helpString,
|
|
|
|
const AkVCam::ProgramOptionsFunc &func,
|
2021-05-25 19:53:30 +00:00
|
|
|
const std::vector<AkVCam::CmdParserFlags> &flags,
|
2021-05-24 22:47:39 +00:00
|
|
|
bool advanced):
|
|
|
|
command(command),
|
|
|
|
arguments(arguments),
|
|
|
|
helpString(helpString),
|
|
|
|
func(func),
|
|
|
|
flags(flags),
|
|
|
|
advanced(advanced)
|
|
|
|
{
|
|
|
|
}
|