Check if the virtual camera is in use before modifying the devices. Fixed clients detection in Windows.
This commit is contained in:
parent
03a21de594
commit
e3bedce3ce
13 changed files with 288 additions and 257 deletions
|
@ -93,8 +93,11 @@ namespace AkVCam {
|
|||
size_t column);
|
||||
std::vector<size_t> maxColumnsLength(const StringVector &table,
|
||||
size_t width);
|
||||
void drawTableHLine(const std::vector<size_t> &columnsLength);
|
||||
void drawTable(const StringVector &table, size_t width);
|
||||
void drawTableHLine(const std::vector<size_t> &columnsLength,
|
||||
bool toStdErr=false);
|
||||
void drawTable(const StringVector &table,
|
||||
size_t width,
|
||||
bool toStdErr=false);
|
||||
CmdParserCommand *parserCommand(const std::string &command);
|
||||
const CmdParserFlags *parserFlag(const std::vector<CmdParserFlags> &cmdFlags,
|
||||
const std::string &flag);
|
||||
|
@ -422,6 +425,31 @@ int AkVCam::CmdParser::parse(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (this->d->m_ipcBridge.needsRoot(command->command)
|
||||
|| (command->command == "hack"
|
||||
&& arguments.size() >= 2
|
||||
|
@ -619,38 +647,49 @@ std::vector<size_t> AkVCam::CmdParserPrivate::maxColumnsLength(const AkVCam::Str
|
|||
return lengths;
|
||||
}
|
||||
|
||||
void AkVCam::CmdParserPrivate::drawTableHLine(const std::vector<size_t> &columnsLength)
|
||||
void AkVCam::CmdParserPrivate::drawTableHLine(const std::vector<size_t> &columnsLength,
|
||||
bool toStdErr)
|
||||
{
|
||||
std::cout << '+';
|
||||
std::ostream *out = &std::cout;
|
||||
|
||||
if (toStdErr)
|
||||
out = &std::cerr;
|
||||
|
||||
*out << '+';
|
||||
|
||||
for (auto &len: columnsLength)
|
||||
std::cout << std::string("-") * (len + 2) << '+';
|
||||
*out << std::string("-") * (len + 2) << '+';
|
||||
|
||||
std::cout << std::endl;
|
||||
*out << std::endl;
|
||||
}
|
||||
|
||||
void AkVCam::CmdParserPrivate::drawTable(const AkVCam::StringVector &table,
|
||||
size_t width)
|
||||
size_t width,
|
||||
bool toStdErr)
|
||||
{
|
||||
size_t height = table.size() / width;
|
||||
auto columnsLength = this->maxColumnsLength(table, width);
|
||||
this->drawTableHLine(columnsLength);
|
||||
this->drawTableHLine(columnsLength, toStdErr);
|
||||
std::ostream *out = &std::cout;
|
||||
|
||||
if (toStdErr)
|
||||
out = &std::cerr;
|
||||
|
||||
for (size_t y = 0; y < height; y++) {
|
||||
std::cout << "|";
|
||||
*out << "|";
|
||||
|
||||
for (size_t x = 0; x < width; x++) {
|
||||
auto &element = table[x + y * width];
|
||||
std::cout << " " << fill(element, columnsLength[x]) << " |";
|
||||
*out << " " << fill(element, columnsLength[x]) << " |";
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
*out << std::endl;
|
||||
|
||||
if (y == 0 && height > 1)
|
||||
this->drawTableHLine(columnsLength);
|
||||
this->drawTableHLine(columnsLength, toStdErr);
|
||||
}
|
||||
|
||||
this->drawTableHLine(columnsLength);
|
||||
this->drawTableHLine(columnsLength, toStdErr);
|
||||
}
|
||||
|
||||
AkVCam::CmdParserCommand *AkVCam::CmdParserPrivate::parserCommand(const std::string &command)
|
||||
|
@ -2026,7 +2065,8 @@ int AkVCam::CmdParserPrivate::hacks(const AkVCam::StringMap &flags,
|
|||
"and advanced users only." << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "WARNING: Unsafe hacks can brick your system, make it "
|
||||
"unstable, or expose it to a serious security risk. You "
|
||||
"unstable, or expose it to a serious security risk, "
|
||||
"remember to make a backup of your files and system. You "
|
||||
"are solely responsible of whatever happens for using "
|
||||
"them. You been warned, don't come and cry later."
|
||||
<< std::endl;
|
||||
|
@ -2105,7 +2145,8 @@ int AkVCam::CmdParserPrivate::hack(const AkVCam::StringMap &flags,
|
|||
|
||||
if (!accepted && !this->m_parseable) {
|
||||
std::cout << "WARNING: Applying this hack can brick your system, make "
|
||||
"it unstable, or expose it to a serious security risk. "
|
||||
"it unstable, or expose it to a serious security risk, "
|
||||
"remember to make a backup of your files and system. "
|
||||
"Agreeing to continue, you accept the full responsability "
|
||||
"of whatever happens from now on."
|
||||
<< std::endl;
|
||||
|
|
|
@ -91,7 +91,7 @@ namespace AkVCam
|
|||
const std::map<std::string, int> &controls)
|
||||
|
||||
public:
|
||||
IpcBridge();
|
||||
IpcBridge(bool isVCam=false);
|
||||
~IpcBridge();
|
||||
|
||||
/* Server & Client */
|
||||
|
@ -103,7 +103,7 @@ namespace AkVCam
|
|||
std::string logPath(const std::string &logName={}) const;
|
||||
|
||||
// Register the peer to the global server.
|
||||
bool registerPeer();
|
||||
bool registerPeer(bool isVCam=false);
|
||||
|
||||
// Unregister the peer to the global server.
|
||||
void unregisterPeer();
|
||||
|
@ -172,6 +172,8 @@ namespace AkVCam
|
|||
// Decrement the count of device listeners
|
||||
bool removeListener(const std::string &deviceId);
|
||||
|
||||
bool isBusyFor(const std::string &operation) const;
|
||||
|
||||
bool needsRoot(const std::string &operation) const;
|
||||
int sudo(int argc, char **argv) const;
|
||||
|
||||
|
|
|
@ -133,18 +133,22 @@
|
|||
std::vector<CallbackName##Callback> m_##CallbackName##Callback;
|
||||
|
||||
#define AKVCAM_EMIT(owner, CallbackName, ...) \
|
||||
{ \
|
||||
AkLogDebug() << "Emiting: " << #CallbackName << std::endl; \
|
||||
\
|
||||
for (auto &callback: owner->m_##CallbackName##Callback) \
|
||||
if (callback.second) \
|
||||
callback.second(callback.first, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define AKVCAM_EMIT_NOARGS(owner, CallbackName) \
|
||||
{ \
|
||||
AkLogDebug() << "Emiting: " << #CallbackName << std::endl; \
|
||||
\
|
||||
for (auto &callback: owner->m_##CallbackName##Callback) \
|
||||
if (callback.second) \
|
||||
callback.second(callback.first); \
|
||||
}
|
||||
|
||||
namespace AkVCam
|
||||
{
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace AkVCam
|
|||
class AssistantPrivate
|
||||
{
|
||||
public:
|
||||
AssistantPeers m_clients;
|
||||
AssistantPeers m_peers;
|
||||
DeviceConfigs m_deviceConfigs;
|
||||
std::map<int64_t, XpcMessage> m_messageHandlers;
|
||||
CFRunLoopTimerRef m_timer {nullptr};
|
||||
|
@ -203,10 +203,10 @@ void AkVCam::AssistantPrivate::peerDied()
|
|||
AkLogFunction();
|
||||
std::vector<std::string> deadPeers;
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
for (auto &peer: this->m_peers) {
|
||||
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
|
||||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_ISALIVE);
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(peer.second,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
|
@ -218,7 +218,7 @@ void AkVCam::AssistantPrivate::peerDied()
|
|||
xpc_release(reply);
|
||||
|
||||
if (!alive)
|
||||
deadPeers.push_back(client.first);
|
||||
deadPeers.push_back(peer.first);
|
||||
}
|
||||
|
||||
for (auto &peer: deadPeers)
|
||||
|
@ -230,15 +230,15 @@ void AkVCam::AssistantPrivate::removePortByName(const std::string &portName)
|
|||
AkLogFunction();
|
||||
AkLogInfo() << "Port: " << portName << std::endl;
|
||||
|
||||
for (auto &peer: this->m_clients)
|
||||
for (auto &peer: this->m_peers)
|
||||
if (peer.first == portName) {
|
||||
xpc_release(peer.second);
|
||||
this->m_clients.erase(portName);
|
||||
this->m_peers.erase(portName);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->m_clients.empty())
|
||||
if (this->m_peers.empty())
|
||||
this->startTimer();
|
||||
|
||||
this->releaseDevicesFromPeer(portName);
|
||||
|
@ -257,8 +257,8 @@ void AkVCam::AssistantPrivate::releaseDevicesFromPeer(const std::string &portNam
|
|||
xpc_dictionary_set_string(dictionary, "device", config.first.c_str());
|
||||
xpc_dictionary_set_string(dictionary, "broadcaster", "");
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, dictionary);
|
||||
for (auto &peer: this->m_peers)
|
||||
xpc_connection_send_message(peer.second, dictionary);
|
||||
|
||||
xpc_release(dictionary);
|
||||
} else {
|
||||
|
@ -298,8 +298,8 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client,
|
|||
xpc_connection_resume(connection);
|
||||
bool ok = true;
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
if (client.first == portName) {
|
||||
for (auto &peer: this->m_peers)
|
||||
if (peer.first == portName) {
|
||||
ok = false;
|
||||
|
||||
break;
|
||||
|
@ -307,7 +307,7 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client,
|
|||
|
||||
if (ok) {
|
||||
AkLogInfo() << "Adding Peer: " << portName << std::endl;
|
||||
this->m_clients[portName] = connection;
|
||||
this->m_peers[portName] = connection;
|
||||
this->stopTimer();
|
||||
}
|
||||
|
||||
|
@ -349,8 +349,8 @@ void AkVCam::AssistantPrivate::devicesUpdate(xpc_connection_t client,
|
|||
if (xpc_dictionary_get_bool(event, "propagate")) {
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
for (auto &peer: this->m_peers)
|
||||
xpc_connection_send_message(peer.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
}
|
||||
|
@ -371,8 +371,8 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
|
|||
this->m_deviceConfigs[deviceId].broadcaster = broadcaster;
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
for (auto &peer: this->m_peers)
|
||||
xpc_connection_send_message(peer.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
}
|
||||
|
@ -387,9 +387,9 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
|
|||
auto reply = xpc_dictionary_create_reply(event);
|
||||
bool ok = true;
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
AkLogDebug() << "Sending frame to " << client.first << std::endl;
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
|
||||
for (auto &peer: this->m_peers) {
|
||||
AkLogDebug() << "Sending frame to " << peer.first << std::endl;
|
||||
auto reply = xpc_connection_send_message_with_reply_sync(peer.second,
|
||||
event);
|
||||
auto replyType = xpc_get_type(reply);
|
||||
bool isOk = false;
|
||||
|
@ -417,8 +417,8 @@ void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client,
|
|||
AkLogFunction();
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
for (auto &peer: this->m_peers)
|
||||
xpc_connection_send_message(peer.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
}
|
||||
|
@ -502,8 +502,8 @@ void AkVCam::AssistantPrivate::listenerAdd(xpc_connection_t client,
|
|||
listeners.push_back(listener);
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
for (auto &peer: this->m_peers)
|
||||
xpc_connection_send_message(peer.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
ok = true;
|
||||
|
@ -532,8 +532,8 @@ void AkVCam::AssistantPrivate::listenerRemove(xpc_connection_t client,
|
|||
listeners.erase(it);
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
for (auto &peer: this->m_peers)
|
||||
xpc_connection_send_message(peer.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
ok = true;
|
||||
|
@ -564,8 +564,8 @@ void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client,
|
|||
|
||||
auto notification = xpc_copy(event);
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
xpc_connection_send_message(client.second, notification);
|
||||
for (auto &peer: this->m_peers)
|
||||
xpc_connection_send_message(peer.second, notification);
|
||||
|
||||
xpc_release(notification);
|
||||
}
|
||||
|
|
|
@ -134,14 +134,14 @@ namespace AkVCam
|
|||
}
|
||||
}
|
||||
|
||||
AkVCam::IpcBridge::IpcBridge()
|
||||
AkVCam::IpcBridge::IpcBridge(bool isVCam)
|
||||
{
|
||||
AkLogFunction();
|
||||
this->d = new IpcBridgePrivate(this);
|
||||
auto loglevel = AkVCam::Preferences::logLevel();
|
||||
AkVCam::Logger::setLogLevel(loglevel);
|
||||
ipcBridgePrivate().add(this);
|
||||
this->registerPeer();
|
||||
this->registerPeer(isVCam);
|
||||
}
|
||||
|
||||
AkVCam::IpcBridge::~IpcBridge()
|
||||
|
@ -191,7 +191,7 @@ std::string AkVCam::IpcBridge::logPath(const std::string &logName) const
|
|||
"/tmp/" + logName + ".log");
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::registerPeer()
|
||||
bool AkVCam::IpcBridge::registerPeer(bool isVCam)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
|
@ -271,6 +271,7 @@ bool AkVCam::IpcBridge::registerPeer()
|
|||
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_ADD_PORT);
|
||||
xpc_dictionary_set_string(dictionary, "port", portName.c_str());
|
||||
xpc_dictionary_set_connection(dictionary, "connection", messagePort);
|
||||
xpc_dictionary_set_bool(dictionary, "isvcam", isVCam);
|
||||
reply = xpc_connection_send_message_with_reply_sync(serverMessagePort,
|
||||
dictionary);
|
||||
xpc_release(dictionary);
|
||||
|
@ -810,6 +811,26 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
|
|||
return status;
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::isBusyFor(const std::string &operation) const
|
||||
{
|
||||
static const std::vector<std::string> operations {
|
||||
"add-device",
|
||||
"add-format",
|
||||
"load",
|
||||
"remove-device",
|
||||
"remove-devices",
|
||||
"remove-format",
|
||||
"remove-formats",
|
||||
"set-description",
|
||||
"update",
|
||||
"hack"
|
||||
};
|
||||
|
||||
auto it = std::find(operations.begin(), operations.end(), operation);
|
||||
|
||||
return it != operations.end() && !this->clientsPids().empty();
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
|
||||
{
|
||||
static const std::vector<std::string> operations;
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace AkVCam
|
|||
PluginInterface *self;
|
||||
ULONG m_ref;
|
||||
ULONG m_reserved;
|
||||
IpcBridge m_ipcBridge;
|
||||
IpcBridge m_ipcBridge {true};
|
||||
|
||||
void updateDevices();
|
||||
static HRESULT QueryInterface(void *self,
|
||||
|
|
|
@ -41,7 +41,9 @@ target_link_libraries(Assistant
|
|||
PlatformUtils
|
||||
advapi32
|
||||
gdi32
|
||||
kernel32
|
||||
ole32
|
||||
psapi
|
||||
shell32
|
||||
strmiids
|
||||
uuid)
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <thread>
|
||||
#include <vector>
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <sddl.h>
|
||||
|
||||
|
@ -39,13 +40,20 @@
|
|||
|
||||
namespace AkVCam
|
||||
{
|
||||
struct PeerInfo
|
||||
{
|
||||
std::string pipeName;
|
||||
uint64_t pid {0};
|
||||
bool isVCam {false};
|
||||
};
|
||||
|
||||
struct AssistantDevice
|
||||
{
|
||||
std::string broadcaster;
|
||||
std::vector<std::string> listeners;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, std::string> AssistantPeers;
|
||||
typedef std::map<std::string, PeerInfo> AssistantPeers;
|
||||
typedef std::map<std::string, AssistantDevice> DeviceConfigs;
|
||||
|
||||
class ServicePrivate
|
||||
|
@ -54,7 +62,7 @@ namespace AkVCam
|
|||
SERVICE_STATUS m_status;
|
||||
SERVICE_STATUS_HANDLE m_statusHandler;
|
||||
MessageServer m_messageServer;
|
||||
AssistantPeers m_clients;
|
||||
AssistantPeers m_peers;
|
||||
DeviceConfigs m_deviceConfigs;
|
||||
Timer m_timer;
|
||||
std::mutex m_peerMutex;
|
||||
|
@ -64,6 +72,7 @@ namespace AkVCam
|
|||
static void stateChanged(void *userData,
|
||||
MessageServer::State state);
|
||||
void sendStatus(DWORD currentState, DWORD exitCode, DWORD wait);
|
||||
static std::vector<uint64_t> systemProcesses();
|
||||
static void checkPeers(void *userData);
|
||||
void removePortByName(const std::string &portName);
|
||||
void releaseDevicesFromPeer(const std::string &portName);
|
||||
|
@ -80,6 +89,8 @@ namespace AkVCam
|
|||
void listenerAdd(Message *message);
|
||||
void listenerRemove(Message *message);
|
||||
void controlsUpdated(Message *message);
|
||||
void clients(Message *message);
|
||||
void client(Message *message);
|
||||
};
|
||||
|
||||
GLOBAL_STATIC(ServicePrivate, servicePrivate)
|
||||
|
@ -279,8 +290,10 @@ AkVCam::ServicePrivate::ServicePrivate()
|
|||
{AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(ServicePrivate::broadcasting) },
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(ServicePrivate::setBroadCasting)},
|
||||
{AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(ServicePrivate::controlsUpdated)},
|
||||
{AKVCAM_ASSISTANT_MSG_CLIENTS , AKVCAM_BIND_FUNC(ServicePrivate::clients) },
|
||||
{AKVCAM_ASSISTANT_MSG_CLIENT , AKVCAM_BIND_FUNC(ServicePrivate::client) },
|
||||
});
|
||||
this->m_timer.setInterval(60000);
|
||||
this->m_timer.setInterval(5000);
|
||||
this->m_timer.connectTimeout(this, &ServicePrivate::checkPeers);
|
||||
this->m_timer.start();
|
||||
}
|
||||
|
@ -335,23 +348,41 @@ void AkVCam::ServicePrivate::sendStatus(DWORD currentState,
|
|||
SetServiceStatus(this->m_statusHandler, &this->m_status);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> AkVCam::ServicePrivate::systemProcesses()
|
||||
{
|
||||
std::vector<uint64_t> pids;
|
||||
|
||||
const DWORD nElements = 4096;
|
||||
DWORD process[nElements];
|
||||
memset(process, 0, nElements * sizeof(DWORD));
|
||||
DWORD needed = 0;
|
||||
|
||||
if (!EnumProcesses(process, nElements * sizeof(DWORD), &needed))
|
||||
return {};
|
||||
|
||||
size_t nProcess = needed / sizeof(DWORD);
|
||||
|
||||
for (size_t i = 0; i < nProcess; i++)
|
||||
if (process[i] > 0)
|
||||
pids.push_back(process[i]);
|
||||
|
||||
return pids;
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::checkPeers(void *userData)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto self = reinterpret_cast<ServicePrivate *>(userData);
|
||||
std::vector<std::string> deadPeers;
|
||||
auto pids = systemProcesses();
|
||||
|
||||
self->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: self->m_clients) {
|
||||
Message message;
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE;
|
||||
message.dataSize = sizeof(MsgIsAlive);
|
||||
MessageServer::sendMessage(client.second, &message);
|
||||
auto requestData = messageData<MsgIsAlive>(&message);
|
||||
for (auto &peer: self->m_peers) {
|
||||
auto it = std::find(pids.begin(), pids.end(), peer.second.pid);
|
||||
|
||||
if (!requestData->alive)
|
||||
deadPeers.push_back(client.first);
|
||||
if (it == pids.end())
|
||||
deadPeers.push_back(peer.first);
|
||||
}
|
||||
|
||||
self->m_peerMutex.unlock();
|
||||
|
@ -369,9 +400,9 @@ void AkVCam::ServicePrivate::removePortByName(const std::string &portName)
|
|||
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &peer: this->m_clients)
|
||||
for (auto &peer: this->m_peers)
|
||||
if (peer.first == portName) {
|
||||
this->m_clients.erase(portName);
|
||||
this->m_peers.erase(portName);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -397,8 +428,8 @@ void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName)
|
|||
(std::min<size_t>)(config.first.size(), MAX_STRING));
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
MessageServer::sendMessage(client.second, &message);
|
||||
for (auto &peer: this->m_peers)
|
||||
MessageServer::sendMessage(peer.second.pipeName, &message);
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
} else {
|
||||
|
@ -437,8 +468,8 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
|
|||
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
if (client.first == portName) {
|
||||
for (auto &peer: this->m_peers)
|
||||
if (peer.first == portName) {
|
||||
ok = false;
|
||||
|
||||
break;
|
||||
|
@ -446,7 +477,11 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
|
|||
|
||||
if (ok) {
|
||||
AkLogInfo() << "Adding Peer: " << portName << std::endl;
|
||||
this->m_clients[portName] = pipeName;
|
||||
PeerInfo peerInfo;
|
||||
peerInfo.pipeName = pipeName;
|
||||
peerInfo.pid = data->pid;
|
||||
peerInfo.isVCam = data->isVCam;
|
||||
this->m_peers[portName] = peerInfo;
|
||||
}
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
|
@ -480,8 +515,8 @@ void AkVCam::ServicePrivate::devicesUpdate(AkVCam::Message *message)
|
|||
this->m_deviceConfigs = configs;
|
||||
|
||||
if (data->propagate)
|
||||
for (auto &client: this->m_clients)
|
||||
MessageServer::sendMessage(client.second, message);
|
||||
for (auto &peer: this->m_peers)
|
||||
MessageServer::sendMessage(peer.second.pipeName, message);
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
}
|
||||
|
@ -503,9 +538,9 @@ void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message)
|
|||
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
for (auto &peer: this->m_peers) {
|
||||
Message msg(message);
|
||||
MessageServer::sendMessage(client.second, &msg);
|
||||
MessageServer::sendMessage(peer.second.pipeName, &msg);
|
||||
}
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
|
@ -517,8 +552,8 @@ void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message)
|
|||
AkLogFunction();
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
MessageServer::sendMessage(client.second, message);
|
||||
for (auto &peer: this->m_peers)
|
||||
MessageServer::sendMessage(peer.second.pipeName, message);
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
}
|
||||
|
@ -528,8 +563,8 @@ void AkVCam::ServicePrivate::pictureUpdated(AkVCam::Message *message)
|
|||
AkLogFunction();
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
MessageServer::sendMessage(client.second, message);
|
||||
for (auto &peer: this->m_peers)
|
||||
MessageServer::sendMessage(peer.second.pipeName, message);
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
}
|
||||
|
@ -616,9 +651,9 @@ void AkVCam::ServicePrivate::listenerAdd(AkVCam::Message *message)
|
|||
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
for (auto &peer: this->m_peers) {
|
||||
Message msg(message);
|
||||
MessageServer::sendMessage(client.second, &msg);
|
||||
MessageServer::sendMessage(peer.second.pipeName, &msg);
|
||||
}
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
|
@ -648,9 +683,9 @@ void AkVCam::ServicePrivate::listenerRemove(AkVCam::Message *message)
|
|||
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients) {
|
||||
for (auto &peer: this->m_peers) {
|
||||
Message msg(message);
|
||||
MessageServer::sendMessage(client.second, &msg);
|
||||
MessageServer::sendMessage(peer.second.pipeName, &msg);
|
||||
}
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
|
@ -665,12 +700,55 @@ void AkVCam::ServicePrivate::controlsUpdated(AkVCam::Message *message)
|
|||
AkLogFunction();
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &client: this->m_clients)
|
||||
MessageServer::sendMessage(client.second, message);
|
||||
for (auto &peer: this->m_peers)
|
||||
MessageServer::sendMessage(peer.second.pipeName, message);
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::clients(AkVCam::Message *message)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto data = messageData<MsgClients>(message);
|
||||
this->m_peerMutex.lock();
|
||||
data->nclient = 0;
|
||||
|
||||
for (auto &peer: this->m_peers)
|
||||
if (peer.second.isVCam)
|
||||
data->nclient++;
|
||||
|
||||
data->status = true;
|
||||
this->m_peerMutex.unlock();
|
||||
}
|
||||
|
||||
void AkVCam::ServicePrivate::client(AkVCam::Message *message)
|
||||
{
|
||||
AkLogFunction();
|
||||
auto data = messageData<MsgClients>(message);
|
||||
std::vector<uint64_t> pids;
|
||||
this->m_peerMutex.lock();
|
||||
|
||||
for (auto &peer: this->m_peers) {
|
||||
AkLogDebug() << "PID: " << peer.second.pid << std::endl;
|
||||
AkLogDebug() << "Is vcam: " << peer.second.isVCam << std::endl;
|
||||
|
||||
if (peer.second.isVCam)
|
||||
pids.push_back(peer.second.pid);
|
||||
}
|
||||
|
||||
this->m_peerMutex.unlock();
|
||||
std::sort(pids.begin(), pids.end());
|
||||
|
||||
if (data->nclient >= pids.size()) {
|
||||
data->status = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data->pid = pids[data->nclient];
|
||||
data->status = true;
|
||||
}
|
||||
|
||||
DWORD WINAPI controlHandler(DWORD control,
|
||||
DWORD eventType,
|
||||
LPVOID eventData,
|
||||
|
|
|
@ -53,6 +53,10 @@
|
|||
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING 0x401
|
||||
#define AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED 0x402
|
||||
|
||||
// Virtual camera clients
|
||||
#define AKVCAM_ASSISTANT_MSG_CLIENTS 0x500
|
||||
#define AKVCAM_ASSISTANT_MSG_CLIENT 0x501
|
||||
|
||||
#define MSG_BUFFER_SIZE 4096
|
||||
#define MAX_STRING 1024
|
||||
|
||||
|
@ -133,6 +137,8 @@ namespace AkVCam
|
|||
{
|
||||
char port[MAX_STRING];
|
||||
char pipeName[MAX_STRING];
|
||||
uint64_t pid;
|
||||
bool isVCam;
|
||||
bool status;
|
||||
};
|
||||
|
||||
|
@ -181,6 +187,13 @@ namespace AkVCam
|
|||
{
|
||||
char device[MAX_STRING];
|
||||
};
|
||||
|
||||
struct MsgClients
|
||||
{
|
||||
uint64_t pid;
|
||||
size_t nclient;
|
||||
bool status;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MESSAGECOMMONS_H
|
||||
|
|
|
@ -47,22 +47,17 @@ namespace AkVCam
|
|||
std::string m_pipeName;
|
||||
std::map<uint32_t, MessageHandler> m_handlers;
|
||||
MessageServer::ServerMode m_mode {MessageServer::ServerModeReceive};
|
||||
MessageServer::PipeState m_pipeState {MessageServer::PipeStateGone};
|
||||
std::thread m_mainThread;
|
||||
std::vector<PipeThreadPtr> m_clientsThreads;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable_any m_exitCheckLoop;
|
||||
int m_checkInterval {5000};
|
||||
bool m_running {false};
|
||||
|
||||
explicit MessageServerPrivate(MessageServer *self);
|
||||
bool startReceive(bool wait=false);
|
||||
void stopReceive(bool wait=false);
|
||||
bool startSend();
|
||||
void stopSend();
|
||||
void messagesLoop();
|
||||
void processPipe(PipeThreadPtr pipeThread, HANDLE pipe);
|
||||
void checkLoop();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -107,21 +102,6 @@ void AkVCam::MessageServer::setMode(ServerMode mode)
|
|||
this->d->m_mode = mode;
|
||||
}
|
||||
|
||||
int AkVCam::MessageServer::checkInterval() const
|
||||
{
|
||||
return this->d->m_checkInterval;
|
||||
}
|
||||
|
||||
int &AkVCam::MessageServer::checkInterval()
|
||||
{
|
||||
return this->d->m_checkInterval;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServer::setCheckInterval(int checkInterval)
|
||||
{
|
||||
this->d->m_checkInterval = checkInterval;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServer::setHandlers(const std::map<uint32_t, MessageHandler> &handlers)
|
||||
{
|
||||
this->d->m_handlers = handlers;
|
||||
|
@ -139,8 +119,6 @@ bool AkVCam::MessageServer::start(bool wait)
|
|||
|
||||
case ServerModeSend:
|
||||
AkLogInfo() << "Starting mode send" << std::endl;
|
||||
|
||||
return this->d->startSend();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -152,8 +130,6 @@ void AkVCam::MessageServer::stop(bool wait)
|
|||
|
||||
if (this->d->m_mode == ServerModeReceive)
|
||||
this->d->stopReceive(wait);
|
||||
else
|
||||
this->d->stopSend();
|
||||
}
|
||||
|
||||
BOOL AkVCam::MessageServer::sendMessage(Message *message,
|
||||
|
@ -265,32 +241,6 @@ void AkVCam::MessageServerPrivate::stopReceive(bool wait)
|
|||
this->m_mainThread.join();
|
||||
}
|
||||
|
||||
bool AkVCam::MessageServerPrivate::startSend()
|
||||
{
|
||||
AkLogFunction();
|
||||
AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl;
|
||||
this->m_running = true;
|
||||
this->m_mainThread = std::thread(&MessageServerPrivate::checkLoop, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServerPrivate::stopSend()
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
if (!this->m_running)
|
||||
return;
|
||||
|
||||
AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl;
|
||||
this->m_running = false;
|
||||
this->m_mutex.lock();
|
||||
this->m_exitCheckLoop.notify_all();
|
||||
this->m_mutex.unlock();
|
||||
this->m_mainThread.join();
|
||||
this->m_pipeState = MessageServer::PipeStateGone;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServerPrivate::messagesLoop()
|
||||
{
|
||||
AkLogFunction();
|
||||
|
@ -460,34 +410,3 @@ void AkVCam::MessageServerPrivate::processPipe(PipeThreadPtr pipeThread,
|
|||
pipeThread->finished = true;
|
||||
AkLogDebug() << "Pipe thread finished." << std::endl;
|
||||
}
|
||||
|
||||
void AkVCam::MessageServerPrivate::checkLoop()
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
while (this->m_running) {
|
||||
AkLogDebug() << "Waiting for pipe: " << this->m_pipeName << std::endl;
|
||||
auto result = WaitNamedPipeA(this->m_pipeName.c_str(), NMPWAIT_NOWAIT);
|
||||
|
||||
if (result
|
||||
&& this->m_pipeState != AkVCam::MessageServer::PipeStateAvailable) {
|
||||
AkLogInfo() << "Pipe Available: " << this->m_pipeName << std::endl;
|
||||
this->m_pipeState = AkVCam::MessageServer::PipeStateAvailable;
|
||||
AKVCAM_EMIT(this->self, PipeStateChanged, this->m_pipeState)
|
||||
} else if (!result
|
||||
&& this->m_pipeState != AkVCam::MessageServer::PipeStateGone
|
||||
&& GetLastError() != ERROR_SEM_TIMEOUT) {
|
||||
AkLogInfo() << "Pipe Gone: " << this->m_pipeName << std::endl;
|
||||
this->m_pipeState = AkVCam::MessageServer::PipeStateGone;
|
||||
AKVCAM_EMIT(this->self, PipeStateChanged, this->m_pipeState)
|
||||
}
|
||||
|
||||
if (!this->m_running)
|
||||
break;
|
||||
|
||||
this->m_mutex.lock();
|
||||
this->m_exitCheckLoop.wait_for(this->m_mutex,
|
||||
std::chrono::milliseconds(this->m_checkInterval));
|
||||
this->m_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,14 +52,7 @@ namespace AkVCam
|
|||
StateStopped
|
||||
};
|
||||
|
||||
enum PipeState
|
||||
{
|
||||
PipeStateAvailable,
|
||||
PipeStateGone
|
||||
};
|
||||
|
||||
AKVCAM_SIGNAL(StateChanged, State state)
|
||||
AKVCAM_SIGNAL(PipeStateChanged, PipeState state)
|
||||
|
||||
public:
|
||||
MessageServer();
|
||||
|
@ -72,9 +65,6 @@ namespace AkVCam
|
|||
ServerMode mode() const;
|
||||
ServerMode &mode();
|
||||
void setMode(ServerMode mode);
|
||||
int checkInterval() const;
|
||||
int &checkInterval();
|
||||
void setCheckInterval(int checkInterval);
|
||||
void setHandlers(const std::map<uint32_t,
|
||||
MessageHandler> &handlers);
|
||||
bool start(bool wait=false);
|
||||
|
|
|
@ -143,14 +143,14 @@ namespace AkVCam
|
|||
static const size_t maxBufferSize = sizeof(Frame) + 3 * maxFrameSize;
|
||||
}
|
||||
|
||||
AkVCam::IpcBridge::IpcBridge()
|
||||
AkVCam::IpcBridge::IpcBridge(bool isVCam)
|
||||
{
|
||||
AkLogFunction();
|
||||
this->d = new IpcBridgePrivate(this);
|
||||
auto loglevel = AkVCam::Preferences::logLevel();
|
||||
AkVCam::Logger::setLogLevel(loglevel);
|
||||
this->d->m_mainServer.start();
|
||||
this->registerPeer();
|
||||
this->registerPeer(isVCam);
|
||||
this->d->updateDeviceSharedProperties();
|
||||
this->d->startServiceStatusCheck();
|
||||
}
|
||||
|
@ -204,7 +204,7 @@ std::string AkVCam::IpcBridge::logPath(const std::string &logName) const
|
|||
return AkVCam::Preferences::readString("logfile", defaultLogFile);
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::registerPeer()
|
||||
bool AkVCam::IpcBridge::registerPeer(bool isVCam)
|
||||
{
|
||||
AkLogFunction();
|
||||
|
||||
|
@ -254,6 +254,8 @@ bool AkVCam::IpcBridge::registerPeer()
|
|||
memcpy(addData->pipeName,
|
||||
pipeName.c_str(),
|
||||
(std::min<size_t>)(pipeName.size(), MAX_STRING));
|
||||
addData->pid = GetCurrentProcessId();
|
||||
addData->isVCam = isVCam;
|
||||
|
||||
if (!MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME,
|
||||
&message)) {
|
||||
|
@ -488,10 +490,11 @@ std::vector<std::string> AkVCam::IpcBridge::listeners(const std::string &deviceI
|
|||
if (!data->status)
|
||||
return {};
|
||||
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER;
|
||||
size_t nlisteners = data->nlistener;
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER;
|
||||
std::vector<std::string> listeners;
|
||||
|
||||
for (size_t i = 0; i < data->nlistener; i++) {
|
||||
for (size_t i = 0; i < nlisteners; i++) {
|
||||
data->nlistener = i;
|
||||
|
||||
if (!this->d->m_mainServer.sendMessage(&message))
|
||||
|
@ -509,98 +512,36 @@ std::vector<std::string> AkVCam::IpcBridge::listeners(const std::string &deviceI
|
|||
std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
|
||||
{
|
||||
AkLogFunction();
|
||||
auto pluginPath = locatePluginPath();
|
||||
AkLogDebug() << "Plugin path: " << pluginPath << std::endl;
|
||||
|
||||
if (pluginPath.empty())
|
||||
Message message;
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_CLIENTS;
|
||||
message.dataSize = sizeof(MsgClients);
|
||||
auto data = messageData<MsgClients>(&message);
|
||||
|
||||
if (!this->d->m_mainServer.sendMessage(&message))
|
||||
return {};
|
||||
|
||||
std::vector<std::string> plugins;
|
||||
|
||||
// First check for the existence of the main plugin binary.
|
||||
auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll";
|
||||
AkLogDebug() << "Plugin binary: " << path << std::endl;
|
||||
|
||||
if (fileExists(path))
|
||||
plugins.push_back(path);
|
||||
|
||||
// Check if the alternative architecture plugin exists.
|
||||
auto altPlugin = this->d->alternativePlugin();
|
||||
|
||||
if (!altPlugin.empty())
|
||||
plugins.push_back(path);
|
||||
|
||||
if (plugins.empty())
|
||||
if (!data->status)
|
||||
return {};
|
||||
|
||||
auto currentPid = GetCurrentProcessId();
|
||||
size_t nclients = data->nclient;
|
||||
message.messageId = AKVCAM_ASSISTANT_MSG_CLIENT;
|
||||
std::vector<uint64_t> pids;
|
||||
|
||||
const DWORD nElements = 4096;
|
||||
DWORD process[nElements];
|
||||
memset(process, 0, nElements * sizeof(DWORD));
|
||||
DWORD needed = 0;
|
||||
for (size_t i = 0; i < nclients; i++) {
|
||||
data->nclient = i;
|
||||
|
||||
if (!EnumProcesses(process, nElements * sizeof(DWORD), &needed))
|
||||
return {};
|
||||
|
||||
size_t nProcess = needed / sizeof(DWORD);
|
||||
auto currentPid = GetCurrentProcessId();
|
||||
|
||||
for (size_t i = 0; i < nProcess; i++) {
|
||||
auto processHnd = OpenProcess(PROCESS_QUERY_INFORMATION
|
||||
| PROCESS_VM_READ,
|
||||
FALSE,
|
||||
process[i]);
|
||||
|
||||
if (!processHnd)
|
||||
if (!this->d->m_mainServer.sendMessage(&message))
|
||||
continue;
|
||||
|
||||
char processName[MAX_PATH];
|
||||
memset(&processName, 0, sizeof(MAX_PATH));
|
||||
if (!data->status)
|
||||
continue;
|
||||
|
||||
if (GetModuleBaseNameA(processHnd,
|
||||
nullptr,
|
||||
processName,
|
||||
MAX_PATH))
|
||||
AkLogDebug() << "Enumerating modules for '" << processName << "'" << std::endl;
|
||||
if (data->pid == currentPid)
|
||||
continue;
|
||||
|
||||
HMODULE modules[nElements];
|
||||
memset(modules, 0, nElements * sizeof(HMODULE));
|
||||
|
||||
if (EnumProcessModules(processHnd,
|
||||
modules,
|
||||
nElements * sizeof(HMODULE),
|
||||
&needed)) {
|
||||
size_t nModules =
|
||||
std::min<DWORD>(needed / sizeof(HMODULE), nElements);
|
||||
|
||||
for (size_t j = 0; j < nModules; j++) {
|
||||
CHAR moduleName[MAX_PATH];
|
||||
memset(moduleName, 0, MAX_PATH * sizeof(CHAR));
|
||||
|
||||
if (GetModuleFileNameExA(processHnd,
|
||||
modules[j],
|
||||
moduleName,
|
||||
MAX_PATH)) {
|
||||
auto it = std::find(plugins.begin(),
|
||||
plugins.end(),
|
||||
moduleName);
|
||||
|
||||
if (it != plugins.end()) {
|
||||
auto pidsIt = std::find(pids.begin(),
|
||||
pids.end(),
|
||||
process[i]);
|
||||
|
||||
if (process[i] > 0
|
||||
&& pidsIt == pids.end()
|
||||
&& process[i] != currentPid)
|
||||
pids.push_back(process[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(processHnd);
|
||||
pids.push_back(data->pid);
|
||||
}
|
||||
|
||||
return pids;
|
||||
|
@ -875,6 +816,26 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
|
|||
return data->status;
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::isBusyFor(const std::string &operation) const
|
||||
{
|
||||
static const std::vector<std::string> operations {
|
||||
"add-device",
|
||||
"add-format",
|
||||
"load",
|
||||
"remove-device",
|
||||
"remove-devices",
|
||||
"remove-format",
|
||||
"remove-formats",
|
||||
"set-description",
|
||||
"update",
|
||||
"hack"
|
||||
};
|
||||
|
||||
auto it = std::find(operations.begin(), operations.end(), operation);
|
||||
|
||||
return it != operations.end() && !this->clientsPids().empty();
|
||||
}
|
||||
|
||||
bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
|
||||
{
|
||||
static const std::vector<std::string> operations {
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace AkVCam
|
|||
std::string m_vendor;
|
||||
std::string m_filterName;
|
||||
IFilterGraph *m_filterGraph {nullptr};
|
||||
IpcBridge m_ipcBridge;
|
||||
IpcBridge m_ipcBridge {true};
|
||||
IpcBridge::ServerState m_serverState {IpcBridge::ServerStateGone};
|
||||
|
||||
BaseFilterPrivate(BaseFilter *self,
|
||||
|
|
Loading…
Reference in a new issue