925 lines
28 KiB
C++
925 lines
28 KiB
C++
/* 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>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include <sddl.h>
|
|
|
|
#include "service.h"
|
|
#include "PlatformUtils/src/messageserver.h"
|
|
#include "VCamUtils/src/image/videoformat.h"
|
|
#include "VCamUtils/src/image/videoframe.h"
|
|
#include "VCamUtils/src/timer.h"
|
|
#include "VCamUtils/src/logger/logger.h"
|
|
|
|
#define AkServiceLogMethod() \
|
|
AkLoggerLog("Service::", __FUNCTION__, "()")
|
|
|
|
#define AkServicePrivateLogMethod() \
|
|
AkLoggerLog("ServicePrivate::", __FUNCTION__, "()")
|
|
|
|
namespace AkVCam
|
|
{
|
|
struct AssistantDevice
|
|
{
|
|
std::string broadcaster;
|
|
std::vector<std::string> listeners;
|
|
bool horizontalMirror;
|
|
bool verticalMirror;
|
|
Scaling scaling;
|
|
AspectRatio aspectRatio;
|
|
bool swapRgb;
|
|
|
|
AssistantDevice():
|
|
horizontalMirror(false),
|
|
verticalMirror(false),
|
|
scaling(ScalingFast),
|
|
aspectRatio(AspectRatioIgnore),
|
|
swapRgb(false)
|
|
{
|
|
}
|
|
};
|
|
|
|
typedef std::map<std::string, std::string> AssistantPeers;
|
|
typedef std::map<std::string, AssistantDevice> DeviceConfigs;
|
|
|
|
class ServicePrivate
|
|
{
|
|
public:
|
|
SERVICE_STATUS m_status;
|
|
SERVICE_STATUS_HANDLE m_statusHandler;
|
|
MessageServer m_messageServer;
|
|
AssistantPeers m_servers;
|
|
AssistantPeers m_clients;
|
|
DeviceConfigs m_deviceConfigs;
|
|
Timer m_timer;
|
|
std::mutex m_peerMutex;
|
|
|
|
ServicePrivate();
|
|
static void stateChanged(void *userData,
|
|
MessageServer::State state);
|
|
static void checkPeers(void *userData);
|
|
void sendStatus(DWORD currentState, DWORD exitCode, DWORD wait);
|
|
inline static uint64_t id();
|
|
void removePortByName(const std::string &portName);
|
|
void releaseDevicesFromPeer(const std::string &portName);
|
|
void requestPort(Message *message);
|
|
void addPort(Message *message);
|
|
void removePort(Message *message);
|
|
void setBroadCasting(Message *message);
|
|
void setMirroring(Message *message);
|
|
void setScaling(Message *message);
|
|
void setAspectRatio(Message *message);
|
|
void setSwapRgb(Message *message);
|
|
void frameReady(Message *message);
|
|
void listeners(Message *message);
|
|
void listener(Message *message);
|
|
void broadcasting(Message *message);
|
|
void mirroring(Message *message);
|
|
void scaling(Message *message);
|
|
void aspectRatio(Message *message);
|
|
void swapRgb(Message *message);
|
|
void listenerAdd(Message *message);
|
|
void listenerRemove(Message *message);
|
|
};
|
|
|
|
GLOBAL_STATIC(ServicePrivate, servicePrivate)
|
|
}
|
|
|
|
DWORD WINAPI controlHandler(DWORD control,
|
|
DWORD eventType,
|
|
LPVOID eventData,
|
|
LPVOID context);
|
|
BOOL WINAPI controlDebugHandler(DWORD control);
|
|
|
|
AkVCam::Service::Service()
|
|
{
|
|
}
|
|
|
|
AkVCam::Service::~Service()
|
|
{
|
|
}
|
|
|
|
BOOL AkVCam::Service::install()
|
|
{
|
|
AkServiceLogMethod();
|
|
WCHAR fileName[MAX_PATH];
|
|
|
|
if (!GetModuleFileName(nullptr, fileName, MAX_PATH)) {
|
|
AkLoggerLog("Can't read module file name");
|
|
|
|
return false;
|
|
}
|
|
|
|
auto scManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
|
|
|
if (!scManager) {
|
|
AkLoggerLog("Can't open SCManager");
|
|
|
|
return false;
|
|
}
|
|
|
|
auto service =
|
|
CreateService(scManager,
|
|
TEXT(DSHOW_PLUGIN_ASSISTANT_NAME),
|
|
TEXT(DSHOW_PLUGIN_ASSISTANT_DESCRIPTION),
|
|
SERVICE_ALL_ACCESS,
|
|
SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_AUTO_START,
|
|
SERVICE_ERROR_NORMAL,
|
|
fileName,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr);
|
|
|
|
if (!service) {
|
|
AkLoggerLog("Can't create service");
|
|
CloseServiceHandle(scManager);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Add detailed description to the service.
|
|
SERVICE_DESCRIPTION serviceDescription;
|
|
WCHAR description[] = TEXT(DSHOW_PLUGIN_DESCRIPTION_EXT);
|
|
serviceDescription.lpDescription = description;
|
|
auto result =
|
|
ChangeServiceConfig2(service,
|
|
SERVICE_CONFIG_DESCRIPTION,
|
|
&serviceDescription);
|
|
|
|
// Configure the service so it will restart if fail.
|
|
WCHAR rebootMsg[] = L"Service failed restarting...";
|
|
|
|
std::vector<SC_ACTION> actions {
|
|
{SC_ACTION_RESTART, 5000}
|
|
};
|
|
|
|
SERVICE_FAILURE_ACTIONS failureActions {
|
|
INFINITE,
|
|
rebootMsg,
|
|
nullptr,
|
|
DWORD(actions.size()),
|
|
actions.data()
|
|
};
|
|
|
|
result =
|
|
ChangeServiceConfig2(service,
|
|
SERVICE_CONFIG_FAILURE_ACTIONS,
|
|
&failureActions);
|
|
|
|
// Run the service
|
|
StartService(service, 0, nullptr);
|
|
CloseServiceHandle(service);
|
|
CloseServiceHandle(scManager);
|
|
|
|
return result;
|
|
}
|
|
|
|
void AkVCam::Service::uninstall()
|
|
{
|
|
AkServiceLogMethod();
|
|
auto scManager = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
|
|
|
|
if (!scManager) {
|
|
AkLoggerLog("Can't open SCManager");
|
|
|
|
return;
|
|
}
|
|
|
|
auto sevice = OpenService(scManager,
|
|
TEXT(DSHOW_PLUGIN_ASSISTANT_NAME),
|
|
SERVICE_ALL_ACCESS);
|
|
|
|
if (sevice) {
|
|
if (ControlService(sevice,
|
|
SERVICE_CONTROL_STOP,
|
|
&servicePrivate()->m_status)) {
|
|
AkLoggerLog("Stopping service");
|
|
|
|
do {
|
|
Sleep(1000);
|
|
QueryServiceStatus(sevice, &servicePrivate()->m_status);
|
|
} while(servicePrivate()->m_status.dwCurrentState != SERVICE_STOPPED);
|
|
}
|
|
|
|
if (!DeleteService(sevice)) {
|
|
AkLoggerLog("Delete service failed");
|
|
}
|
|
|
|
CloseServiceHandle(sevice);
|
|
} else {
|
|
AkLoggerLog("Can't open service");
|
|
}
|
|
|
|
CloseServiceHandle(scManager);
|
|
}
|
|
|
|
void AkVCam::Service::debug()
|
|
{
|
|
AkServiceLogMethod();
|
|
SetConsoleCtrlHandler(controlDebugHandler, TRUE);
|
|
servicePrivate()->m_messageServer.start(true);
|
|
}
|
|
|
|
void AkVCam::Service::showHelp(int argc, char **argv)
|
|
{
|
|
AkServiceLogMethod();
|
|
UNUSED(argc)
|
|
|
|
auto programName = strrchr(argv[0], '\\');
|
|
|
|
if (!programName)
|
|
programName = strrchr(argv[0], '/');
|
|
|
|
if (!programName)
|
|
programName = argv[0];
|
|
else
|
|
programName++;
|
|
|
|
std::cout << "Usage: " << programName << " [options]" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "Webcamoid virtual camera server." << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "Options:" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t-i, --install\tInstall the service." << std::endl;
|
|
std::cout << "\t-u, --uninstall\tUnistall the service." << std::endl;
|
|
std::cout << "\t-d, --debug\tDebug the service." << std::endl;
|
|
std::cout << "\t-h, --help\tShow this help." << std::endl;
|
|
}
|
|
|
|
AkVCam::ServicePrivate::ServicePrivate()
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
|
|
this->m_status = {
|
|
SERVICE_WIN32_OWN_PROCESS,
|
|
SERVICE_STOPPED,
|
|
0,
|
|
NO_ERROR,
|
|
NO_ERROR,
|
|
0,
|
|
0
|
|
};
|
|
this->m_statusHandler = nullptr;
|
|
this->m_messageServer.setPipeName(L"\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME_L);
|
|
this->m_messageServer.setHandlers({
|
|
{AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(ServicePrivate::frameReady) },
|
|
{AKVCAM_ASSISTANT_MSG_REQUEST_PORT , AKVCAM_BIND_FUNC(ServicePrivate::requestPort) },
|
|
{AKVCAM_ASSISTANT_MSG_ADD_PORT , AKVCAM_BIND_FUNC(ServicePrivate::addPort) },
|
|
{AKVCAM_ASSISTANT_MSG_REMOVE_PORT , AKVCAM_BIND_FUNC(ServicePrivate::removePort) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(ServicePrivate::listenerAdd) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE, AKVCAM_BIND_FUNC(ServicePrivate::listenerRemove) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS , AKVCAM_BIND_FUNC(ServicePrivate::listeners) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER , AKVCAM_BIND_FUNC(ServicePrivate::listener) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(ServicePrivate::broadcasting) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING, AKVCAM_BIND_FUNC(ServicePrivate::setBroadCasting)},
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING , AKVCAM_BIND_FUNC(ServicePrivate::mirroring) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(ServicePrivate::setMirroring) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_SCALING , AKVCAM_BIND_FUNC(ServicePrivate::scaling) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING , AKVCAM_BIND_FUNC(ServicePrivate::setScaling) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO , AKVCAM_BIND_FUNC(ServicePrivate::aspectRatio) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO , AKVCAM_BIND_FUNC(ServicePrivate::setAspectRatio) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB , AKVCAM_BIND_FUNC(ServicePrivate::swapRgb) },
|
|
{AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB , AKVCAM_BIND_FUNC(ServicePrivate::setSwapRgb) },
|
|
});
|
|
this->m_timer.setInterval(60000);
|
|
this->m_timer.connectTimeout(this, &ServicePrivate::checkPeers);
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::stateChanged(void *userData,
|
|
MessageServer::State state)
|
|
{
|
|
UNUSED(userData)
|
|
|
|
switch (state) {
|
|
case MessageServer::StateAboutToStart:
|
|
AkVCam::servicePrivate()->sendStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
|
|
break;
|
|
|
|
case MessageServer::StateStarted:
|
|
AkVCam::servicePrivate()->sendStatus(SERVICE_RUNNING, NO_ERROR, 0);
|
|
break;
|
|
|
|
case MessageServer::StateAboutToStop:
|
|
AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
|
|
break;
|
|
|
|
case MessageServer::StateStopped:
|
|
AkVCam::servicePrivate()->sendStatus(SERVICE_STOPPED, NO_ERROR, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::checkPeers(void *userData)
|
|
{
|
|
auto self = reinterpret_cast<ServicePrivate *>(userData);
|
|
std::vector<std::string> removePorts;
|
|
|
|
self->m_peerMutex.lock();
|
|
std::vector<AssistantPeers *> allPeers {
|
|
&self->m_clients,
|
|
&self->m_servers
|
|
};
|
|
|
|
for (auto peers: allPeers)
|
|
for (auto &peer: *peers) {
|
|
Message message;
|
|
message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE;
|
|
message.dataSize = sizeof(MsgIsAlive);
|
|
MessageServer::sendMessage(peer.second, &message);
|
|
auto requestData = messageData<MsgIsAlive>(&message);
|
|
|
|
if (!requestData->alive)
|
|
removePorts.push_back(peer.first);
|
|
}
|
|
|
|
self->m_peerMutex.unlock();
|
|
|
|
for (auto &port: removePorts) {
|
|
AkLoggerLog(port, " died, removing...");
|
|
self->removePortByName(port);
|
|
}
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::sendStatus(DWORD currentState,
|
|
DWORD exitCode,
|
|
DWORD wait)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
|
|
this->m_status.dwControlsAccepted =
|
|
currentState == SERVICE_START_PENDING? 0: SERVICE_ACCEPT_STOP;
|
|
this->m_status.dwCurrentState = currentState;
|
|
this->m_status.dwWin32ExitCode = exitCode;
|
|
this->m_status.dwWaitHint = wait;
|
|
|
|
if (currentState == SERVICE_RUNNING || currentState == SERVICE_STOPPED)
|
|
this->m_status.dwCheckPoint = 0;
|
|
else
|
|
this->m_status.dwCheckPoint++;
|
|
|
|
SetServiceStatus(this->m_statusHandler, &this->m_status);
|
|
}
|
|
|
|
uint64_t AkVCam::ServicePrivate::id()
|
|
{
|
|
static uint64_t id = 0;
|
|
|
|
return id++;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::removePortByName(const std::string &portName)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
AkLoggerLog("Port: ", portName);
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
std::vector<AssistantPeers *> allPeers {
|
|
&this->m_clients,
|
|
&this->m_servers
|
|
};
|
|
|
|
bool breakLoop = false;
|
|
|
|
for (auto peers: allPeers) {
|
|
for (auto &peer: *peers)
|
|
if (peer.first == portName) {
|
|
peers->erase(portName);
|
|
breakLoop = true;
|
|
|
|
break;
|
|
}
|
|
|
|
if (breakLoop)
|
|
break;
|
|
}
|
|
|
|
bool peersEmpty = this->m_servers.empty() && this->m_clients.empty();
|
|
this->m_peerMutex.unlock();
|
|
|
|
if (peersEmpty)
|
|
this->m_timer.stop();
|
|
|
|
this->releaseDevicesFromPeer(portName);
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName)
|
|
{
|
|
for (auto &config: this->m_deviceConfigs)
|
|
if (config.second.broadcaster == portName) {
|
|
config.second.broadcaster.clear();
|
|
|
|
Message message;
|
|
message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING;
|
|
message.dataSize = sizeof(MsgBroadcasting);
|
|
auto data = messageData<MsgBroadcasting>(&message);
|
|
memcpy(data->device,
|
|
config.first.c_str(),
|
|
(std::min<size_t>)(config.first.size(), MAX_STRING));
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients)
|
|
MessageServer::sendMessage(client.second, &message);
|
|
|
|
this->m_peerMutex.unlock();
|
|
} else {
|
|
auto it = std::find(config.second.listeners.begin(),
|
|
config.second.listeners.end(),
|
|
portName);
|
|
|
|
if (it != config.second.listeners.end())
|
|
config.second.listeners.erase(it);
|
|
}
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::requestPort(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
|
|
auto data = messageData<MsgRequestPort>(message);
|
|
std::string portName = data->client?
|
|
AKVCAM_ASSISTANT_CLIENT_NAME:
|
|
AKVCAM_ASSISTANT_SERVER_NAME;
|
|
portName += std::to_string(this->id());
|
|
|
|
AkLoggerLog("Returning Port: ", portName);
|
|
memcpy(data->port,
|
|
portName.c_str(),
|
|
(std::min<size_t>)(portName.size(), MAX_STRING));
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
|
|
auto data = messageData<MsgAddPort>(message);
|
|
std::string portName(data->port);
|
|
std::string pipeName(data->pipeName);
|
|
bool ok = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
AssistantPeers *peers;
|
|
|
|
if (portName.find(AKVCAM_ASSISTANT_CLIENT_NAME) != std::string::npos)
|
|
peers = &this->m_clients;
|
|
else
|
|
peers = &this->m_servers;
|
|
|
|
for (auto &peer: *peers)
|
|
if (peer.first == portName) {
|
|
ok = false;
|
|
|
|
break;
|
|
}
|
|
|
|
if (ok) {
|
|
AkLoggerLog("Adding Peer: ", portName);
|
|
(*peers)[portName] = pipeName;
|
|
}
|
|
|
|
size_t nPeers = this->m_servers.size() + this->m_clients.size();
|
|
|
|
this->m_peerMutex.unlock();
|
|
|
|
if (ok && nPeers == 1)
|
|
this->m_timer.start();
|
|
|
|
data->status = ok;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::removePort(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
|
|
auto data = messageData<MsgRemovePort>(message);
|
|
this->removePortByName(data->port);
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgBroadcasting>(message);
|
|
std::string deviceId(data->device);
|
|
std::string broadcaster(data->broadcaster);
|
|
data->status = false;
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
if (this->m_deviceConfigs[deviceId].broadcaster == broadcaster)
|
|
return;
|
|
|
|
AkLoggerLog("Device: ", deviceId);
|
|
AkLoggerLog("Broadcaster: ", broadcaster);
|
|
this->m_deviceConfigs[deviceId].broadcaster = broadcaster;
|
|
data->status = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients) {
|
|
Message msg(message);
|
|
MessageServer::sendMessage(client.second, &msg);
|
|
}
|
|
|
|
this->m_peerMutex.unlock();
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::setMirroring(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgMirroring>(message);
|
|
std::string deviceId(data->device);
|
|
data->status = false;
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
if (this->m_deviceConfigs[deviceId].horizontalMirror == data->hmirror
|
|
&& this->m_deviceConfigs[deviceId].verticalMirror == data->vmirror)
|
|
return;
|
|
|
|
this->m_deviceConfigs[deviceId].horizontalMirror = data->hmirror;
|
|
this->m_deviceConfigs[deviceId].verticalMirror = data->vmirror;
|
|
data->status = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients) {
|
|
Message msg(message);
|
|
MessageServer::sendMessage(client.second, &msg);
|
|
}
|
|
|
|
this->m_peerMutex.unlock();
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::setScaling(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgScaling>(message);
|
|
std::string deviceId(data->device);
|
|
data->status = false;
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
if (this->m_deviceConfigs[deviceId].scaling == data->scaling)
|
|
return;
|
|
|
|
this->m_deviceConfigs[deviceId].scaling = data->scaling;
|
|
data->status = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients) {
|
|
Message msg(message);
|
|
MessageServer::sendMessage(client.second, &msg);
|
|
}
|
|
|
|
this->m_peerMutex.unlock();
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::setAspectRatio(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgAspectRatio>(message);
|
|
std::string deviceId(data->device);
|
|
data->status = false;
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
if (this->m_deviceConfigs[deviceId].aspectRatio == data->aspect)
|
|
return;
|
|
|
|
this->m_deviceConfigs[deviceId].aspectRatio = data->aspect;
|
|
data->status = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients) {
|
|
Message msg(message);
|
|
MessageServer::sendMessage(client.second, &msg);
|
|
}
|
|
|
|
this->m_peerMutex.unlock();
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::setSwapRgb(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgSwapRgb>(message);
|
|
std::string deviceId(data->device);
|
|
data->status = false;
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
if (this->m_deviceConfigs[deviceId].swapRgb == data->swap)
|
|
return;
|
|
|
|
this->m_deviceConfigs[deviceId].swapRgb = data->swap;
|
|
data->status = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients) {
|
|
Message msg(message);
|
|
MessageServer::sendMessage(client.second, &msg);
|
|
}
|
|
|
|
this->m_peerMutex.unlock();
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients)
|
|
MessageServer::sendMessage(client.second, message);
|
|
|
|
this->m_peerMutex.unlock();
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::listeners(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgListeners>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
data->nlistener = this->m_deviceConfigs[deviceId].listeners.size();
|
|
|
|
if (data->nlistener > 0) {
|
|
memcpy(data->listener,
|
|
this->m_deviceConfigs[deviceId].listeners[0].c_str(),
|
|
std::min<size_t>(this->m_deviceConfigs[deviceId].listeners[0].size(),
|
|
MAX_STRING));
|
|
}
|
|
|
|
data->status = true;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::listener(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgListeners>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
auto nlistener = this->m_deviceConfigs[deviceId].listeners.size();
|
|
|
|
if (data->nlistener >= nlistener) {
|
|
data->status = false;
|
|
|
|
return;
|
|
}
|
|
|
|
memcpy(data->listener,
|
|
this->m_deviceConfigs[deviceId].listeners[data->nlistener].c_str(),
|
|
std::min<size_t>(this->m_deviceConfigs[deviceId].listeners[data->nlistener].size(),
|
|
MAX_STRING));
|
|
|
|
data->status = true;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::broadcasting(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgBroadcasting>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
memcpy(data->broadcaster,
|
|
this->m_deviceConfigs[deviceId].broadcaster.c_str(),
|
|
std::min<size_t>(this->m_deviceConfigs[deviceId].broadcaster.size(),
|
|
MAX_STRING));
|
|
data->status = true;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::mirroring(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgMirroring>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
data->hmirror = this->m_deviceConfigs[deviceId].horizontalMirror;
|
|
data->vmirror = this->m_deviceConfigs[deviceId].verticalMirror;
|
|
data->status = true;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::scaling(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgScaling>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
data->scaling = this->m_deviceConfigs[deviceId].scaling;
|
|
data->status = true;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::aspectRatio(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgAspectRatio>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
data->aspect = this->m_deviceConfigs[deviceId].aspectRatio;
|
|
data->status = true;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::swapRgb(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgSwapRgb>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
data->swap = this->m_deviceConfigs[deviceId].swapRgb;
|
|
data->status = true;
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::listenerAdd(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgListeners>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
auto &listeners = this->m_deviceConfigs[deviceId].listeners;
|
|
std::string listener(data->listener);
|
|
auto it = std::find(listeners.begin(), listeners.end(), listener);
|
|
|
|
if (it == listeners.end()) {
|
|
listeners.push_back(listener);
|
|
data->nlistener = listeners.size();
|
|
data->status = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients) {
|
|
Message msg(message);
|
|
MessageServer::sendMessage(client.second, &msg);
|
|
}
|
|
|
|
this->m_peerMutex.unlock();
|
|
} else {
|
|
data->nlistener = listeners.size();
|
|
data->status = false;
|
|
}
|
|
}
|
|
|
|
void AkVCam::ServicePrivate::listenerRemove(AkVCam::Message *message)
|
|
{
|
|
AkServicePrivateLogMethod();
|
|
auto data = messageData<MsgListeners>(message);
|
|
std::string deviceId(data->device);
|
|
|
|
if (this->m_deviceConfigs.count(deviceId) < 1)
|
|
this->m_deviceConfigs[deviceId] = {};
|
|
|
|
auto &listeners = this->m_deviceConfigs[deviceId].listeners;
|
|
std::string listener(data->listener);
|
|
auto it = std::find(listeners.begin(), listeners.end(), listener);
|
|
|
|
if (it != listeners.end()) {
|
|
listeners.erase(it);
|
|
data->nlistener = listeners.size();
|
|
data->status = true;
|
|
|
|
this->m_peerMutex.lock();
|
|
|
|
for (auto &client: this->m_clients) {
|
|
Message msg(message);
|
|
MessageServer::sendMessage(client.second, &msg);
|
|
}
|
|
|
|
this->m_peerMutex.unlock();
|
|
} else {
|
|
data->nlistener = listeners.size();
|
|
data->status = false;
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI controlHandler(DWORD control,
|
|
DWORD eventType,
|
|
LPVOID eventData,
|
|
LPVOID context)
|
|
{
|
|
UNUSED(eventType)
|
|
UNUSED(eventData)
|
|
UNUSED(context)
|
|
AkLoggerLog("controlHandler()");
|
|
|
|
DWORD result = ERROR_CALL_NOT_IMPLEMENTED;
|
|
|
|
switch (control) {
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
case SERVICE_CONTROL_STOP:
|
|
AkVCam::servicePrivate()->sendStatus(SERVICE_STOP_PENDING,
|
|
NO_ERROR,
|
|
0);
|
|
AkVCam::servicePrivate()->m_messageServer.stop();
|
|
result = NO_ERROR;
|
|
|
|
break;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
result = NO_ERROR;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
auto state = AkVCam::servicePrivate()->m_status.dwCurrentState;
|
|
AkVCam::servicePrivate()->sendStatus(state, NO_ERROR, 0);
|
|
|
|
return result;
|
|
}
|
|
|
|
BOOL WINAPI controlDebugHandler(DWORD control)
|
|
{
|
|
AkLoggerLog("controlDebugHandler()");
|
|
|
|
if (control == CTRL_BREAK_EVENT || control == CTRL_C_EVENT) {
|
|
AkVCam::servicePrivate()->m_messageServer.stop();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void WINAPI serviceMain(DWORD dwArgc, LPTSTR *lpszArgv)
|
|
{
|
|
UNUSED(dwArgc)
|
|
UNUSED(lpszArgv)
|
|
AkLoggerLog("serviceMain()");
|
|
AkLoggerLog("Setting service control handler");
|
|
|
|
AkVCam::servicePrivate()->m_statusHandler =
|
|
RegisterServiceCtrlHandlerEx(TEXT(DSHOW_PLUGIN_ASSISTANT_NAME),
|
|
controlHandler,
|
|
nullptr);
|
|
|
|
if (!AkVCam::servicePrivate()->m_statusHandler)
|
|
return;
|
|
|
|
AkVCam::servicePrivate()->sendStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
|
|
|
|
AkLoggerLog("Setting up service");
|
|
AkVCam::servicePrivate()->m_messageServer
|
|
.connectStateChanged(AkVCam::servicePrivate(),
|
|
&AkVCam::ServicePrivate::stateChanged);
|
|
AkVCam::servicePrivate()->m_messageServer.start(true);
|
|
}
|