1 /* Webcamoid, webcam capture application.
2 * Copyright (C) 2018 Gonzalo Exequiel Pedone
3 *
4 * Webcamoid is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Webcamoid is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Web-Site: http://webcamoid.github.io/
18 */
19
20 #include <algorithm>
21 #include <fstream>
22 #include <cmath>
23 #include <memory>
24 #include <mutex>
25 #include <sstream>
26 #include <thread>
27 #include <dshow.h>
28
29 #include "ipcbridge.h"
30 #include "PlatformUtils/src/messageserver.h"
31 #include "PlatformUtils/src/mutex.h"
32 #include "PlatformUtils/src/sharedmemory.h"
33 #include "PlatformUtils/src/utils.h"
34 #include "VCamUtils/src/image/videoformat.h"
35 #include "VCamUtils/src/image/videoframe.h"
36 #include "VCamUtils/src/logger/logger.h"
37
38 #define AkIpcBridgeLogMethod() \
39 AkLoggerLog("IpcBridge::", __FUNCTION__, "()")
40
41 #define AkIpcBridgePrivateLogMethod() \
42 AkLoggerLog("IpcBridgePrivate::", __FUNCTION__, "()")
43
44 namespace AkVCam
45 {
46 typedef std::shared_ptr<IMoniker> MonikerPtr;
47 typedef std::shared_ptr<IBaseFilter> BaseFilterPtr;
48 typedef std::shared_ptr<IPropertyBag> PropertyBagPtr;
49 typedef std::shared_ptr<IPin> PinPtr;
50 typedef std::shared_ptr<AM_MEDIA_TYPE> MediaTypePtr;
51
52 struct DeviceSharedProperties
53 {
54 SharedMemory sharedMemory;
55 Mutex mutex;
56 };
57
58 class IpcBridgePrivate
59 {
60 public:
61 IpcBridge *self;
62 std::map<std::string, DeviceSharedProperties> m_devices;
63 std::map<uint32_t, MessageHandler> m_messageHandlers;
64 std::vector<std::string> m_broadcasting;
65 std::map<std::string, std::string> m_options;
66 MessageServer m_messageServer;
67 MessageServer m_mainServer;
68 SharedMemory m_sharedMemory;
69 Mutex m_globalMutex;
70 std::string m_portName;
71 std::wstring m_error;
72 bool m_asClient;
73
74 explicit IpcBridgePrivate(IpcBridge *self);
75 ~IpcBridgePrivate();
76
77 static inline std::vector<std::wstring> *driverPaths();
78 std::vector<MonikerPtr> listCameras() const;
79 BaseFilterPtr filter(IMoniker *moniker) const;
80 PropertyBagPtr propertyBag(IMoniker *moniker) const;
81 bool isVirtualCamera(const MonikerPtr &moniker) const;
82 bool isVirtualCamera(IBaseFilter *baseFilter) const;
83 std::string cameraPath(const MonikerPtr &moniker) const;
84 std::string cameraPath(IPropertyBag *propertyBag) const;
85 std::wstring cameraDescription(const MonikerPtr &moniker) const;
86 std::wstring cameraDescription(IPropertyBag *propertyBag) const;
87 std::vector<PinPtr> enumPins(IBaseFilter *baseFilter) const;
88 std::vector<VideoFormat> enumVideoFormats(IPin *pin) const;
89 std::vector<std::wstring> findFiles(const std::wstring &path) const;
90 std::vector<std::string> findFiles(const std::string &path,
91 const std::string &fileName) const;
92 std::vector<std::wstring> findFiles(const std::wstring &path,
93 const std::wstring &fileName) const;
94 std::wstring regAddLine(const std::wstring &key,
95 const std::wstring &value,
96 const std::wstring &data,
97 BOOL wow=false) const;
98 std::wstring regAddLine(const std::wstring &key,
99 const std::wstring &value,
100 int data,
101 BOOL wow=false) const;
102 std::wstring regDeleteLine(const std::wstring &key,
103 BOOL wow=false) const;
104 std::wstring regDeleteLine(const std::wstring &key,
105 const std::wstring &value,
106 BOOL wow=false) const;
107 std::wstring regMoveLine(const std::wstring &fromKey,
108 const std::wstring &toKey,
109 BOOL wow=false) const;
110 std::wstring dirname(const std::wstring &path) const;
111 void updateDeviceSharedProperties();
112 void updateDeviceSharedProperties(const std::string &deviceId,
113 const std::string &owner);
114 std::wstring locateDriverPath() const;
115 static void pipeStateChanged(void *userData,
116 MessageServer::PipeState state);
117
118 // Message handling methods
119 void isAlive(Message *message);
120 void frameReady(Message *message);
121 void setBroadcasting(Message *message);
122 void setMirror(Message *message);
123 void setScaling(Message *message);
124 void setAspectRatio(Message *message);
125 void setSwapRgb(Message *message);
126 void listenerAdd(Message *message);
127 void listenerRemove(Message *message);
128
129 // Execute commands with elevated privileges.
130 int sudo(const std::vector<std::string> ¶meters,
131 const std::wstring &directory={},
132 bool show=false);
133 };
134
135 static const int maxFrameWidth = 1920;
136 static const int maxFrameHeight = 1080;
137 static const size_t maxFrameSize = maxFrameWidth * maxFrameHeight;
138 static const size_t maxBufferSize = sizeof(Frame) + 3 * maxFrameSize;
139 }
140
IpcBridge()141 AkVCam::IpcBridge::IpcBridge()
142 {
143 AkIpcBridgeLogMethod();
144 this->d = new IpcBridgePrivate(this);
145 }
146
~IpcBridge()147 AkVCam::IpcBridge::~IpcBridge()
148 {
149 delete this->d;
150 }
151
errorMessage() const152 std::wstring AkVCam::IpcBridge::errorMessage() const
153 {
154 return this->d->m_error;
155 }
156
setOption(const std::string & key,const std::string & value)157 void AkVCam::IpcBridge::setOption(const std::string &key, const std::string &value)
158 {
159 AkIpcBridgeLogMethod();
160
161 if (value.empty())
162 this->d->m_options.erase(key);
163 else
164 this->d->m_options[key] = value;
165 }
166
driverPaths() const167 std::vector<std::wstring> AkVCam::IpcBridge::driverPaths() const
168 {
169 AkIpcBridgeLogMethod();
170
171 return *this->d->driverPaths();
172 }
173
setDriverPaths(const std::vector<std::wstring> & driverPaths)174 void AkVCam::IpcBridge::setDriverPaths(const std::vector<std::wstring> &driverPaths)
175 {
176 AkIpcBridgeLogMethod();
177 *this->d->driverPaths() = driverPaths;
178 }
179
availableDrivers() const180 std::vector<std::string> AkVCam::IpcBridge::availableDrivers() const
181 {
182 return {"AkVirtualCamera"};
183 }
184
driver() const185 std::string AkVCam::IpcBridge::driver() const
186 {
187 return {"AkVirtualCamera"};
188 }
189
setDriver(const std::string & driver)190 bool AkVCam::IpcBridge::setDriver(const std::string &driver)
191 {
192 return driver == "AkVirtualCamera";
193 }
194
availableRootMethods() const195 std::vector<std::string> AkVCam::IpcBridge::availableRootMethods() const
196 {
197 return {"runas"};
198 }
199
rootMethod() const200 std::string AkVCam::IpcBridge::rootMethod() const
201 {
202 return {"runas"};
203 }
204
setRootMethod(const std::string & rootMethod)205 bool AkVCam::IpcBridge::setRootMethod(const std::string &rootMethod)
206 {
207 return rootMethod == "runas";
208 }
209
connectService(bool asClient)210 void AkVCam::IpcBridge::connectService(bool asClient)
211 {
212 AkIpcBridgeLogMethod();
213 this->d->m_asClient = asClient;
214 this->d->m_mainServer.start();
215 }
216
disconnectService()217 void AkVCam::IpcBridge::disconnectService()
218 {
219 AkIpcBridgeLogMethod();
220 this->d->m_mainServer.stop(true);
221 this->d->m_asClient = false;
222 }
223
registerPeer(bool asClient)224 bool AkVCam::IpcBridge::registerPeer(bool asClient)
225 {
226 AkIpcBridgeLogMethod();
227
228 Message message;
229 message.messageId = AKVCAM_ASSISTANT_MSG_REQUEST_PORT;
230 message.dataSize = sizeof(MsgRequestPort);
231 auto requestData = messageData<MsgRequestPort>(&message);
232 requestData->client = asClient;
233
234 if (!MessageServer::sendMessage(L"\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME_L,
235 &message))
236 return false;
237
238 std::string portName(requestData->port);
239 auto pipeName = "\\\\.\\pipe\\" + portName;
240 this->d->m_messageServer.setPipeName(std::wstring(pipeName.begin(),
241 pipeName.end()));
242 this->d->m_messageServer.setHandlers(this->d->m_messageHandlers);
243 AkLoggerLog("Recommended port name: ", portName);
244
245 if (!this->d->m_messageServer.start()) {
246 AkLoggerLog("Can't start message server");
247
248 return false;
249 }
250
251 message.clear();
252 message.messageId = AKVCAM_ASSISTANT_MSG_ADD_PORT;
253 message.dataSize = sizeof(MsgAddPort);
254 auto addData = messageData<MsgAddPort>(&message);
255 memcpy(addData->port,
256 portName.c_str(),
257 (std::min<size_t>)(portName.size(), MAX_STRING));
258 memcpy(addData->pipeName,
259 pipeName.c_str(),
260 (std::min<size_t>)(pipeName.size(), MAX_STRING));
261
262 AkLoggerLog("Registering port name: ", portName);
263
264 if (!MessageServer::sendMessage(L"\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME_L,
265 &message)) {
266 this->d->m_messageServer.stop(true);
267
268 return false;
269 }
270
271 if (!addData->status) {
272 this->d->m_messageServer.stop(true);
273
274 return false;
275 }
276
277 this->d->m_sharedMemory.setName(L"Local\\"
278 + std::wstring(portName.begin(),
279 portName.end())
280 + L".data");
281 this->d->m_globalMutex = Mutex(std::wstring(portName.begin(),
282 portName.end())
283 + L".mutex");
284 this->d->m_portName = portName;
285 AkLoggerLog("Peer registered as ", portName);
286
287 return true;
288 }
289
unregisterPeer()290 void AkVCam::IpcBridge::unregisterPeer()
291 {
292 AkIpcBridgeLogMethod();
293
294 if (this->d->m_portName.empty())
295 return;
296
297 this->d->m_sharedMemory.setName({});
298 Message message;
299 message.messageId = AKVCAM_ASSISTANT_MSG_REMOVE_PORT;
300 message.dataSize = sizeof(MsgRemovePort);
301 auto data = messageData<MsgRemovePort>(&message);
302 memcpy(data->port,
303 this->d->m_portName.c_str(),
304 (std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
305 MessageServer::sendMessage(L"\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME_L,
306 &message);
307 this->d->m_messageServer.stop(true);
308 this->d->m_portName.clear();
309 }
310
listDevices() const311 std::vector<std::string> AkVCam::IpcBridge::listDevices() const
312 {
313 AkIpcBridgeLogMethod();
314 std::vector<std::string> devices;
315
316 for (auto camera: this->d->listCameras())
317 if (this->d->isVirtualCamera(camera))
318 devices.push_back(this->d->cameraPath(camera));
319
320 #ifdef QT_DEBUG
321 AkLoggerLog("Devices:");
322
323 for (auto &device: devices)
324 AkLoggerLog(" ", device);
325 #endif
326
327 return devices;
328 }
329
description(const std::string & deviceId) const330 std::wstring AkVCam::IpcBridge::description(const std::string &deviceId) const
331 {
332 AkIpcBridgeLogMethod();
333
334 for (auto camera: this->d->listCameras()) {
335 auto propertyBag = this->d->propertyBag(camera.get());
336
337 if (this->d->isVirtualCamera(camera)
338 && this->d->cameraPath(propertyBag.get()) == deviceId)
339 return this->d->cameraDescription(propertyBag.get());
340 }
341
342 return {};
343 }
344
supportedOutputPixelFormats() const345 std::vector<AkVCam::PixelFormat> AkVCam::IpcBridge::supportedOutputPixelFormats() const
346 {
347 return {
348 PixelFormatRGB32,
349 PixelFormatRGB24,
350 PixelFormatRGB16,
351 PixelFormatRGB15,
352 PixelFormatUYVY,
353 PixelFormatYUY2,
354 PixelFormatNV12
355 };
356 }
357
formats(const std::string & deviceId) const358 std::vector<AkVCam::VideoFormat> AkVCam::IpcBridge::formats(const std::string &deviceId) const
359 {
360 AkIpcBridgeLogMethod();
361
362 std::vector<AkVCam::VideoFormat> formats;
363
364 for (auto camera: this->d->listCameras()) {
365 auto baseFilter = this->d->filter(camera.get());
366
367 if (this->d->isVirtualCamera(baseFilter.get())
368 && this->d->cameraPath(camera) == deviceId) {
369 auto pins = this->d->enumPins(baseFilter.get());
370
371 if (!pins.empty())
372 formats = this->d->enumVideoFormats(pins[0].get());
373
374 break;
375 }
376 }
377
378 return formats;
379 }
380
broadcaster(const std::string & deviceId) const381 std::string AkVCam::IpcBridge::broadcaster(const std::string &deviceId) const
382 {
383 AkIpcBridgeLogMethod();
384
385 Message message;
386 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING;
387 message.dataSize = sizeof(MsgBroadcasting);
388 auto data = messageData<MsgBroadcasting>(&message);
389 memcpy(data->device,
390 deviceId.c_str(),
391 (std::min<size_t>)(deviceId.size(), MAX_STRING));
392
393 if (!this->d->m_mainServer.sendMessage(&message))
394 return {};
395
396 if (!data->status)
397 return {};
398
399 std::string broadcaster(data->broadcaster);
400
401 AkLoggerLog("Device: ", deviceId);
402 AkLoggerLog("Broadcaster: ", broadcaster);
403
404 return broadcaster;
405 }
406
isHorizontalMirrored(const std::string & deviceId)407 bool AkVCam::IpcBridge::isHorizontalMirrored(const std::string &deviceId)
408 {
409 AkIpcBridgeLogMethod();
410
411 Message message;
412 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING;
413 message.dataSize = sizeof(MsgMirroring);
414 auto data = messageData<MsgMirroring>(&message);
415 memcpy(data->device,
416 deviceId.c_str(),
417 (std::min<size_t>)(deviceId.size(), MAX_STRING));
418
419 if (!this->d->m_mainServer.sendMessage(&message))
420 return false;
421
422 if (!data->status)
423 return false;
424
425 return data->hmirror;
426 }
427
isVerticalMirrored(const std::string & deviceId)428 bool AkVCam::IpcBridge::isVerticalMirrored(const std::string &deviceId)
429 {
430 AkIpcBridgeLogMethod();
431
432 Message message;
433 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_MIRRORING;
434 message.dataSize = sizeof(MsgMirroring);
435 auto data = messageData<MsgMirroring>(&message);
436 memcpy(data->device,
437 deviceId.c_str(),
438 (std::min<size_t>)(deviceId.size(), MAX_STRING));
439
440 if (!this->d->m_mainServer.sendMessage(&message))
441 return false;
442
443 if (!data->status)
444 return false;
445
446 return data->vmirror;
447 }
448
scalingMode(const std::string & deviceId)449 AkVCam::Scaling AkVCam::IpcBridge::scalingMode(const std::string &deviceId)
450 {
451 AkIpcBridgeLogMethod();
452
453 Message message;
454 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SCALING;
455 message.dataSize = sizeof(MsgScaling);
456 auto data = messageData<MsgScaling>(&message);
457 memcpy(data->device,
458 deviceId.c_str(),
459 (std::min<size_t>)(deviceId.size(), MAX_STRING));
460
461 if (!this->d->m_mainServer.sendMessage(&message))
462 return ScalingFast;
463
464 if (!data->status)
465 return ScalingFast;
466
467 return data->scaling;
468 }
469
aspectRatioMode(const std::string & deviceId)470 AkVCam::AspectRatio AkVCam::IpcBridge::aspectRatioMode(const std::string &deviceId)
471 {
472 AkIpcBridgeLogMethod();
473
474 Message message;
475 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_ASPECTRATIO;
476 message.dataSize = sizeof(MsgAspectRatio);
477 auto data = messageData<MsgAspectRatio>(&message);
478 memcpy(data->device,
479 deviceId.c_str(),
480 (std::min<size_t>)(deviceId.size(), MAX_STRING));
481
482 if (!this->d->m_mainServer.sendMessage(&message))
483 return AspectRatioIgnore;
484
485 if (!data->status)
486 return AspectRatioIgnore;
487
488 return data->aspect;
489 }
490
swapRgb(const std::string & deviceId)491 bool AkVCam::IpcBridge::swapRgb(const std::string &deviceId)
492 {
493 AkIpcBridgeLogMethod();
494
495 Message message;
496 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SWAPRGB;
497 message.dataSize = sizeof(MsgSwapRgb);
498 auto data = messageData<MsgSwapRgb>(&message);
499 memcpy(data->device,
500 deviceId.c_str(),
501 (std::min<size_t>)(deviceId.size(), MAX_STRING));
502
503 if (!this->d->m_mainServer.sendMessage(&message))
504 return false;
505
506 if (!data->status)
507 return false;
508
509 return data->swap;
510 }
511
listeners(const std::string & deviceId)512 std::vector<std::string> AkVCam::IpcBridge::listeners(const std::string &deviceId)
513 {
514 AkIpcBridgeLogMethod();
515
516 Message message;
517 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENERS;
518 message.dataSize = sizeof(MsgListeners);
519 auto data = messageData<MsgListeners>(&message);
520 memcpy(data->device,
521 deviceId.c_str(),
522 (std::min<size_t>)(deviceId.size(), MAX_STRING));
523
524 if (!this->d->m_mainServer.sendMessage(&message))
525 return {};
526
527 if (!data->status)
528 return {};
529
530 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER;
531 std::vector<std::string> listeners;
532
533 for (size_t i = 0; i < data->nlistener; i++) {
534 data->nlistener = i;
535
536 if (!this->d->m_mainServer.sendMessage(&message))
537 continue;
538
539 if (!data->status)
540 continue;
541
542 listeners.push_back(std::string(data->listener));
543 }
544
545 return listeners;
546 }
547
deviceCreate(const std::wstring & description,const std::vector<VideoFormat> & formats)548 std::string AkVCam::IpcBridge::deviceCreate(const std::wstring &description,
549 const std::vector<VideoFormat> &formats)
550 {
551 AkIpcBridgeLogMethod();
552
553 auto driverPath = this->d->locateDriverPath();
554
555 if (driverPath.empty()) {
556 this->d->m_error = L"Driver not found";
557
558 return {};
559 }
560
561 // Create a device path for the new device and add it's entry.
562 auto devicePath = createDevicePath();
563
564 if (devicePath.empty()) {
565 this->d->m_error = L"Can't create a device";
566
567 return {};
568 }
569
570 std::wstringstream ss;
571 ss << L"@echo off" << std::endl;
572 ss << L"chcp " << GetACP() << std::endl;
573
574 auto driverInstallPath =
575 programFilesPath() + L"\\" DSHOW_PLUGIN_NAME_L L".plugin";
576
577 // Copy all plugins
578 std::vector<std::wstring> installPaths;
579
580 for (auto path: this->d->findFiles(driverPath,
581 DSHOW_PLUGIN_NAME_L L".dll")) {
582 auto installPath = replace(path, driverPath, driverInstallPath);
583
584 if (!isEqualFile(path, installPath))
585 ss << L"mkdir \""
586 << this->d->dirname(installPath)
587 << L"\""
588 << std::endl
589 << L"copy /y \""
590 << path
591 << L"\" \""
592 << installPath
593 << L"\""
594 << std::endl;
595
596 installPaths.push_back(installPath);
597 }
598
599 // Copy all services
600 std::vector<std::wstring> assistantInstallPaths;
601
602 for (auto path: this->d->findFiles(driverPath,
603 DSHOW_PLUGIN_ASSISTANT_NAME_L L".exe")) {
604 auto installPath = replace(path, driverPath, driverInstallPath);
605
606 if (!isEqualFile(path, installPath))
607 ss << L"mkdir \""
608 << this->d->dirname(installPath)
609 << L"\""
610 << std::endl
611 << L"copy /y \""
612 << path
613 << L"\" \""
614 << installPath
615 << L"\""
616 << std::endl;
617
618 assistantInstallPaths.push_back(installPath);
619 }
620
621 // Copy shared files
622 for (auto path: this->d->findFiles(driverPath + L"/share")) {
623 auto installPath = replace(path, driverPath, driverInstallPath);
624
625 if (!isEqualFile(path, installPath))
626 ss << L"mkdir \""
627 << this->d->dirname(installPath)
628 << L"\""
629 << std::endl
630 << L"copy /y \""
631 << path
632 << L"\" \""
633 << installPath
634 << L"\""
635 << std::endl;
636 }
637
638 BOOL wow = isWow64();
639
640 // List cameras and create a line with the number of cameras.
641 auto nCameras = camerasCount();
642
643 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras",
644 L"size",
645 int(nCameras + 1),
646 wow)
647 << std::endl;
648
649 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
650 + std::to_wstring(nCameras + 1),
651 L"path",
652 devicePath,
653 wow)
654 << std::endl;
655
656 // Add description line.
657 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
658 + std::to_wstring(nCameras + 1),
659 L"description",
660 description,
661 wow)
662 << std::endl;
663
664 // Set number of formats.
665 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
666 + std::to_wstring(nCameras + 1)
667 + L"\\Formats",
668 L"size",
669 int(formats.size()),
670 wow)
671 << std::endl;
672
673 // Setup formats.
674 for (size_t i = 0; i < formats.size(); i++) {
675 auto videoFormat = formats[i];
676 auto format = VideoFormat::wstringFromFourcc(videoFormat.fourcc());
677
678 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
679 + std::to_wstring(nCameras + 1)
680 + L"\\Formats\\"
681 + std::to_wstring(i + 1),
682 L"format",
683 format,
684 wow)
685 << std::endl;
686
687 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
688 + std::to_wstring(nCameras + 1)
689 + L"\\Formats\\"
690 + std::to_wstring(i + 1),
691 L"width",
692 videoFormat.width(),
693 wow)
694 << std::endl;
695
696 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
697 + std::to_wstring(nCameras + 1)
698 + L"\\Formats\\"
699 + std::to_wstring(i + 1),
700 L"height",
701 videoFormat.height(),
702 wow)
703 << std::endl;
704
705 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
706 + std::to_wstring(nCameras + 1)
707 + L"\\Formats\\"
708 + std::to_wstring(i + 1),
709 L"fps",
710 videoFormat.minimumFrameRate().toWString(),
711 wow)
712 << std::endl;
713 }
714
715 for (auto path: installPaths)
716 ss << L"regsvr32 /s \"" << path << L"\"" << std::endl;
717
718 std::vector<std::wstring> preferredArch;
719
720 if (wow)
721 preferredArch.push_back(L"x64");
722
723 preferredArch.push_back(DSHOW_PLUGIN_ARCH_L);
724
725 if (wcscmp(DSHOW_PLUGIN_ARCH_L, L"x64") == 0)
726 preferredArch.push_back(L"x32");
727
728 for (auto &arch: preferredArch) {
729 auto assistantPath = driverInstallPath
730 + L"\\"
731 + arch
732 + L"\\" DSHOW_PLUGIN_ASSISTANT_NAME_L L".exe";
733
734 if (std::find(assistantInstallPaths.begin(),
735 assistantInstallPaths.end(),
736 assistantPath) != assistantInstallPaths.end()) {
737 ss << "\"" << assistantPath << "\" --install" << std::endl;
738
739 break;
740 }
741 }
742
743 // Create the script.
744 auto temp = tempPath();
745 auto scriptPath = std::string(temp.begin(), temp.end())
746 + "\\device_create_"
747 + timeStamp()
748 + ".bat";
749 std::wfstream script;
750 script.imbue(std::locale(""));
751 script.open(scriptPath, std::ios_base::out | std::ios_base::trunc);
752
753 if (script.is_open()) {
754 script << ss.str();
755 script.close();
756
757 // Execute the script with elevated privileges.
758 if (this->d->sudo({"cmd", "/c", scriptPath}))
759 devicePath.clear();
760
761 std::wstring wScriptPath(scriptPath.begin(), scriptPath.end());
762 DeleteFile(wScriptPath.c_str());
763 } else {
764 devicePath.clear();
765 }
766
767 return std::string(devicePath.begin(), devicePath.end());
768 }
769
deviceDestroy(const std::string & deviceId)770 bool AkVCam::IpcBridge::deviceDestroy(const std::string &deviceId)
771 {
772 AkIpcBridgeLogMethod();
773
774 auto camera = cameraFromId(std::wstring(deviceId.begin(), deviceId.end()));
775
776 if (camera < 0)
777 return false;
778
779 auto driverPath = this->d->locateDriverPath();
780
781 if (driverPath.empty()) {
782 this->d->m_error = L"Driver not found";
783
784 return false;
785 }
786
787 std::wstringstream ss;
788 ss << L"@echo off" << std::endl;
789 ss << L"chcp " << GetACP() << std::endl;
790
791 auto driverInstallPath =
792 programFilesPath() + L"\\" DSHOW_PLUGIN_NAME_L L".plugin";
793 std::vector<std::wstring> installPaths;
794
795 for (auto path: this->d->findFiles(std::wstring(driverPath.begin(),
796 driverPath.end()),
797 DSHOW_PLUGIN_NAME_L L".dll")) {
798 auto installPath = replace(path, driverPath, driverInstallPath);
799
800 installPaths.push_back(installPath);
801 }
802
803 std::vector<std::wstring> assistantInstallPaths;
804
805 for (auto path: this->d->findFiles(std::wstring(driverPath.begin(),
806 driverPath.end()),
807 DSHOW_PLUGIN_ASSISTANT_NAME_L L".exe")) {
808 auto installPath = replace(path, driverPath, driverInstallPath);
809
810 assistantInstallPaths.push_back(installPath);
811 }
812
813 BOOL wow = isWow64();
814
815 // List cameras and create a line with the number of cameras.
816 auto nCameras = camerasCount();
817
818 if (nCameras > 1) {
819 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras",
820 L"size",
821 int(nCameras - 1),
822 wow)
823 << std::endl;
824
825 ss << this->d->regDeleteLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
826 + std::to_wstring(camera + 1),
827 wow)
828 << std::endl;
829
830 for (DWORD i = DWORD(camera + 1); i < nCameras; i++) {
831 ss << this->d->regMoveLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
832 + std::to_wstring(i + 1),
833 L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
834 + std::to_wstring(i),
835 wow)
836 << std::endl;
837 }
838
839 for (auto path: installPaths)
840 ss << L"regsvr32 /s \"" << path << L"\"" << std::endl;
841 } else {
842 for (auto path: installPaths)
843 ss << L"regsvr32 /s /u \"" << path << L"\"" << std::endl;
844
845 for (auto path: assistantInstallPaths)
846 ss << L"\"" << path << L"\" --uninstall" << std::endl;
847
848 ss << this->d->regDeleteLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras",
849 wow)
850 << std::endl;
851
852 if (lstrcmpi(driverPath.c_str(), driverInstallPath.c_str()))
853 ss << L"rmdir /s /q \"" << driverInstallPath << L"\"" << std::endl;
854 }
855
856 auto temp = tempPath();
857 auto scriptPath = std::string(temp.begin(), temp.end())
858 + "\\device_destroy_"
859 + timeStamp()
860 + ".bat";
861 std::wfstream script;
862 script.imbue(std::locale(""));
863 script.open(scriptPath, std::ios_base::out | std::ios_base::trunc);
864
865 if (script.is_open()) {
866 script << ss.str();
867 script.close();
868 this->d->sudo({"cmd", "/c", scriptPath});
869 std::wstring wScriptPath(scriptPath.begin(), scriptPath.end());
870 DeleteFile(wScriptPath.c_str());
871 }
872
873 return true;
874 }
875
changeDescription(const std::string & deviceId,const std::wstring & description)876 bool AkVCam::IpcBridge::changeDescription(const std::string &deviceId,
877 const std::wstring &description)
878 {
879 AkIpcBridgeLogMethod();
880
881 auto camera = cameraFromId(std::wstring(deviceId.begin(), deviceId.end()));
882
883 if (camera < 0)
884 return false;
885
886 auto driverPath = this->d->locateDriverPath();
887
888 if (driverPath.empty()) {
889 this->d->m_error = L"Driver not found";
890
891 return false;
892 }
893
894 std::wstringstream ss;
895 ss << L"@echo off" << std::endl;
896 ss << L"chcp " << GetACP() << std::endl;
897
898 auto driverInstallPath =
899 programFilesPath() + L"\\" DSHOW_PLUGIN_NAME_L L".plugin";
900 std::vector<std::wstring> installPaths;
901
902 for (auto path: this->d->findFiles(std::wstring(driverPath.begin(),
903 driverPath.end()),
904 DSHOW_PLUGIN_NAME_L L".dll")) {
905 auto installPath = replace(path, driverPath, driverInstallPath);
906 installPaths.push_back(installPath);
907 }
908
909 ss << this->d->regAddLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras\\"
910 + std::to_wstring(camera + 1),
911 L"description",
912 description,
913 isWow64())
914 << std::endl;
915
916 for (auto path: installPaths)
917 ss << L"regsvr32 /s \"" << path << L"\"" << std::endl;
918
919 auto temp = tempPath();
920 auto scriptPath = std::string(temp.begin(), temp.end())
921 + "\\device_change_description_"
922 + timeStamp()
923 + ".bat";
924 std::wfstream script;
925 script.imbue(std::locale(""));
926 script.open(scriptPath, std::ios_base::out | std::ios_base::trunc);
927 bool ok = false;
928
929 if (script.is_open()) {
930 script << ss.str();
931 script.close();
932 ok = this->d->sudo({"cmd", "/c", scriptPath}) == 0;
933 std::wstring wScriptPath(scriptPath.begin(), scriptPath.end());
934 DeleteFile(wScriptPath.c_str());
935 }
936
937 return ok;
938 }
939
destroyAllDevices()940 bool AkVCam::IpcBridge::destroyAllDevices()
941 {
942 AkIpcBridgeLogMethod();
943
944 auto driverPath = this->d->locateDriverPath();
945
946 if (driverPath.empty()) {
947 this->d->m_error = L"Driver not found";
948
949 return false;
950 }
951
952 std::wstringstream ss;
953 ss << L"@echo off" << std::endl;
954 ss << L"chcp " << GetACP() << std::endl;
955
956 auto driverInstallPath =
957 programFilesPath() + L"\\" DSHOW_PLUGIN_NAME_L L".plugin";
958
959 for (auto path: this->d->findFiles(std::wstring(driverPath.begin(),
960 driverPath.end()),
961 DSHOW_PLUGIN_NAME_L L".dll")) {
962 auto installPath = replace(path, driverPath, driverInstallPath);
963 ss << L"regsvr32 /s /u \"" << installPath << L"\"" << std::endl;
964 }
965
966 for (auto path: this->d->findFiles(std::wstring(driverPath.begin(),
967 driverPath.end()),
968 DSHOW_PLUGIN_ASSISTANT_NAME_L L".exe")) {
969 auto installPath = replace(path, driverPath, driverInstallPath);
970 ss << L"\"" << installPath << L"\" --uninstall" << std::endl;
971 }
972
973 ss << this->d->regDeleteLine(L"HKLM\\SOFTWARE\\Webcamoid\\VirtualCamera\\Cameras",
974 isWow64())
975 << std::endl;
976
977 if (lstrcmpi(driverPath.c_str(), driverInstallPath.c_str()))
978 ss << "rmdir /s /q \"" << driverInstallPath << L"\"" << std::endl;
979
980 auto temp = tempPath();
981 auto scriptPath = std::string(temp.begin(), temp.end())
982 + "\\device_remove_all_"
983 + timeStamp()
984 + ".bat";
985 std::wfstream script;
986 script.imbue(std::locale(""));
987 script.open(scriptPath, std::ios_base::out | std::ios_base::trunc);
988 bool ok = false;
989
990 if (script.is_open()) {
991 script << ss.str();
992 script.close();
993 ok = this->d->sudo({"cmd", "/c", scriptPath}) == 0;
994 std::wstring wScriptPath(scriptPath.begin(), scriptPath.end());
995 DeleteFile(wScriptPath.c_str());
996 }
997
998 return ok;
999 }
1000
deviceStart(const std::string & deviceId,const VideoFormat & format)1001 bool AkVCam::IpcBridge::deviceStart(const std::string &deviceId,
1002 const VideoFormat &format)
1003 {
1004 UNUSED(format)
1005 AkIpcBridgeLogMethod();
1006 auto it = std::find(this->d->m_broadcasting.begin(),
1007 this->d->m_broadcasting.end(),
1008 deviceId);
1009
1010 if (it != this->d->m_broadcasting.end())
1011 return false;
1012
1013 std::wstring portName(this->d->m_portName.begin(),
1014 this->d->m_portName.end());
1015 this->d->m_sharedMemory.setName(L"Local\\" + portName + L".data");
1016 this->d->m_globalMutex = Mutex(portName + L".mutex");
1017
1018 if (!this->d->m_sharedMemory.open(maxBufferSize,
1019 SharedMemory::OpenModeWrite))
1020 return false;
1021
1022 Message message;
1023 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING;
1024 message.dataSize = sizeof(MsgBroadcasting);
1025 auto data = messageData<MsgBroadcasting>(&message);
1026 memcpy(data->device,
1027 deviceId.c_str(),
1028 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1029 memcpy(data->broadcaster,
1030 this->d->m_portName.c_str(),
1031 (std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
1032
1033 if (!this->d->m_mainServer.sendMessage(&message)) {
1034 this->d->m_sharedMemory.close();
1035
1036 return false;
1037 }
1038
1039 if (!data->status)
1040 return false;
1041
1042 this->d->m_broadcasting.push_back(deviceId);
1043
1044 return true;
1045 }
1046
deviceStop(const std::string & deviceId)1047 void AkVCam::IpcBridge::deviceStop(const std::string &deviceId)
1048 {
1049 AkIpcBridgeLogMethod();
1050 auto it = std::find(this->d->m_broadcasting.begin(),
1051 this->d->m_broadcasting.end(),
1052 deviceId);
1053
1054 if (it == this->d->m_broadcasting.end())
1055 return;
1056
1057 Message message;
1058 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING;
1059 message.dataSize = sizeof(MsgBroadcasting);
1060 auto data = messageData<MsgBroadcasting>(&message);
1061 memcpy(data->device,
1062 deviceId.c_str(),
1063 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1064
1065 this->d->m_mainServer.sendMessage(&message);
1066 this->d->m_sharedMemory.close();
1067 this->d->m_broadcasting.erase(it);
1068 }
1069
write(const std::string & deviceId,const VideoFrame & frame)1070 bool AkVCam::IpcBridge::write(const std::string &deviceId,
1071 const VideoFrame &frame)
1072 {
1073 AkIpcBridgeLogMethod();
1074
1075 if (frame.format().size() < 1)
1076 return false;
1077
1078 auto buffer =
1079 reinterpret_cast<Frame *>(this->d->m_sharedMemory.lock(&this->d->m_globalMutex));
1080
1081 if (!buffer)
1082 return false;
1083
1084 if (size_t(frame.format().width() * frame.format().height()) > maxFrameSize) {
1085 auto scaledFrame = frame.scaled(maxFrameSize);
1086 buffer->format = scaledFrame.format().fourcc();
1087 buffer->width = scaledFrame.format().width();
1088 buffer->height = scaledFrame.format().height();
1089 buffer->size = uint32_t(frame.data().size());
1090 memcpy(buffer->data,
1091 scaledFrame.data().data(),
1092 scaledFrame.data().size());
1093 } else {
1094 buffer->format = frame.format().fourcc();
1095 buffer->width = frame.format().width();
1096 buffer->height = frame.format().height();
1097 buffer->size = uint32_t(frame.data().size());
1098 memcpy(buffer->data,
1099 frame.data().data(),
1100 frame.data().size());
1101 }
1102
1103 this->d->m_sharedMemory.unlock(&this->d->m_globalMutex);
1104
1105 Message message;
1106 message.messageId = AKVCAM_ASSISTANT_MSG_FRAME_READY;
1107 message.dataSize = sizeof(MsgFrameReady);
1108 auto data = messageData<MsgFrameReady>(&message);
1109 memcpy(data->device,
1110 deviceId.c_str(),
1111 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1112 memcpy(data->port,
1113 this->d->m_portName.c_str(),
1114 (std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
1115
1116 return this->d->m_mainServer.sendMessage(&message) == TRUE;
1117 }
1118
setMirroring(const std::string & deviceId,bool horizontalMirrored,bool verticalMirrored)1119 void AkVCam::IpcBridge::setMirroring(const std::string &deviceId,
1120 bool horizontalMirrored,
1121 bool verticalMirrored)
1122 {
1123 AkIpcBridgeLogMethod();
1124
1125 Message message;
1126 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING;
1127 message.dataSize = sizeof(MsgMirroring);
1128 auto data = messageData<MsgMirroring>(&message);
1129 memcpy(data->device,
1130 deviceId.c_str(),
1131 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1132 data->hmirror = horizontalMirrored;
1133 data->vmirror = verticalMirrored;
1134 this->d->m_mainServer.sendMessage(&message);
1135 }
1136
setScaling(const std::string & deviceId,Scaling scaling)1137 void AkVCam::IpcBridge::setScaling(const std::string &deviceId,
1138 Scaling scaling)
1139 {
1140 AkIpcBridgeLogMethod();
1141
1142 Message message;
1143 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING;
1144 message.dataSize = sizeof(MsgScaling);
1145 auto data = messageData<MsgScaling>(&message);
1146 memcpy(data->device,
1147 deviceId.c_str(),
1148 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1149 data->scaling = scaling;
1150 this->d->m_mainServer.sendMessage(&message);
1151 }
1152
setAspectRatio(const std::string & deviceId,AspectRatio aspectRatio)1153 void AkVCam::IpcBridge::setAspectRatio(const std::string &deviceId,
1154 AspectRatio aspectRatio)
1155 {
1156 AkIpcBridgeLogMethod();
1157
1158 Message message;
1159 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO;
1160 message.dataSize = sizeof(MsgAspectRatio);
1161 auto data = messageData<MsgAspectRatio>(&message);
1162 memcpy(data->device,
1163 deviceId.c_str(),
1164 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1165 data->aspect = aspectRatio;
1166 this->d->m_mainServer.sendMessage(&message);
1167 }
1168
setSwapRgb(const std::string & deviceId,bool swap)1169 void AkVCam::IpcBridge::setSwapRgb(const std::string &deviceId, bool swap)
1170 {
1171 AkIpcBridgeLogMethod();
1172
1173 Message message;
1174 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB;
1175 message.dataSize = sizeof(MsgSwapRgb);
1176 auto data = messageData<MsgSwapRgb>(&message);
1177 memcpy(data->device,
1178 deviceId.c_str(),
1179 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1180 data->swap = swap;
1181 this->d->m_mainServer.sendMessage(&message);
1182 }
1183
addListener(const std::string & deviceId)1184 bool AkVCam::IpcBridge::addListener(const std::string &deviceId)
1185 {
1186 AkIpcBridgeLogMethod();
1187
1188 Message message;
1189 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD;
1190 message.dataSize = sizeof(MsgListeners);
1191 auto data = messageData<MsgListeners>(&message);
1192 memcpy(data->device,
1193 deviceId.c_str(),
1194 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1195 memcpy(data->listener,
1196 this->d->m_portName.c_str(),
1197 (std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
1198
1199 if (!this->d->m_mainServer.sendMessage(&message))
1200 return false;
1201
1202 return data->status;
1203 }
1204
removeListener(const std::string & deviceId)1205 bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
1206 {
1207 AkIpcBridgeLogMethod();
1208
1209 Message message;
1210 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE;
1211 message.dataSize = sizeof(MsgListeners);
1212 auto data = messageData<MsgListeners>(&message);
1213 memcpy(data->device,
1214 deviceId.c_str(),
1215 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1216 memcpy(data->listener,
1217 this->d->m_portName.c_str(),
1218 (std::min<size_t>)(this->d->m_portName.size(), MAX_STRING));
1219
1220 if (!this->d->m_mainServer.sendMessage(&message))
1221 return false;
1222
1223 return data->status;
1224 }
1225
IpcBridgePrivate(IpcBridge * self)1226 AkVCam::IpcBridgePrivate::IpcBridgePrivate(IpcBridge *self):
1227 self(self),
1228 m_asClient(false)
1229 {
1230 this->m_mainServer.setPipeName(L"\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME_L);
1231 this->m_mainServer.setMode(MessageServer::ServerModeSend);
1232 this->m_mainServer.connectPipeStateChanged(this,
1233 &IpcBridgePrivate::pipeStateChanged);
1234 this->updateDeviceSharedProperties();
1235
1236 this->m_messageHandlers = std::map<uint32_t, MessageHandler> {
1237 {AKVCAM_ASSISTANT_MSG_ISALIVE , AKVCAM_BIND_FUNC(IpcBridgePrivate::isAlive) },
1238 {AKVCAM_ASSISTANT_MSG_FRAME_READY , AKVCAM_BIND_FUNC(IpcBridgePrivate::frameReady) },
1239 {AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING, AKVCAM_BIND_FUNC(IpcBridgePrivate::setBroadcasting)},
1240 {AKVCAM_ASSISTANT_MSG_DEVICE_SETMIRRORING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setMirror) },
1241 {AKVCAM_ASSISTANT_MSG_DEVICE_SETSCALING , AKVCAM_BIND_FUNC(IpcBridgePrivate::setScaling) },
1242 {AKVCAM_ASSISTANT_MSG_DEVICE_SETASPECTRATIO , AKVCAM_BIND_FUNC(IpcBridgePrivate::setAspectRatio) },
1243 {AKVCAM_ASSISTANT_MSG_DEVICE_SETSWAPRGB , AKVCAM_BIND_FUNC(IpcBridgePrivate::setSwapRgb) },
1244 {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_ADD , AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerAdd) },
1245 {AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER_REMOVE, AKVCAM_BIND_FUNC(IpcBridgePrivate::listenerRemove) },
1246 };
1247 }
1248
~IpcBridgePrivate()1249 AkVCam::IpcBridgePrivate::~IpcBridgePrivate()
1250 {
1251 this->m_mainServer.stop(true);
1252 }
1253
driverPaths()1254 std::vector<std::wstring> *AkVCam::IpcBridgePrivate::driverPaths()
1255 {
1256 static std::vector<std::wstring> paths;
1257
1258 return &paths;
1259 }
1260
listCameras() const1261 std::vector<AkVCam::MonikerPtr> AkVCam::IpcBridgePrivate::listCameras() const
1262 {
1263 std::vector<MonikerPtr> cameras;
1264
1265 // Create the System Device Enumerator.
1266 ICreateDevEnum *deviceEnumerator = nullptr;
1267 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,
1268 nullptr,
1269 CLSCTX_INPROC_SERVER,
1270 IID_ICreateDevEnum,
1271 reinterpret_cast<void **>(&deviceEnumerator));
1272
1273 if (FAILED(hr))
1274 return cameras;
1275
1276 // Create an enumerator for the category.
1277 IEnumMoniker *enumMoniker = nullptr;
1278
1279 if (deviceEnumerator->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
1280 &enumMoniker,
1281 0) == S_OK) {
1282 enumMoniker->Reset();
1283 IMoniker *moniker = nullptr;
1284
1285 while (enumMoniker->Next(1, &moniker, nullptr) == S_OK)
1286 cameras.push_back(MonikerPtr(moniker, [](IMoniker *moniker) {
1287 moniker->Release();
1288 }));
1289
1290 enumMoniker->Release();
1291 }
1292
1293 deviceEnumerator->Release();
1294
1295 return cameras;
1296 }
1297
filter(IMoniker * moniker) const1298 AkVCam::BaseFilterPtr AkVCam::IpcBridgePrivate::filter(IMoniker *moniker) const
1299 {
1300 if (!moniker)
1301 return {};
1302
1303 IBaseFilter *baseFilter = nullptr;
1304
1305 if (FAILED(moniker->BindToObject(nullptr,
1306 nullptr,
1307 IID_IBaseFilter,
1308 reinterpret_cast<void **>(&baseFilter)))) {
1309 return {};
1310 }
1311
1312 return BaseFilterPtr(baseFilter, [] (IBaseFilter *baseFilter) {
1313 baseFilter->Release();
1314 });
1315 }
1316
propertyBag(IMoniker * moniker) const1317 AkVCam::PropertyBagPtr AkVCam::IpcBridgePrivate::propertyBag(IMoniker *moniker) const
1318 {
1319 if (!moniker)
1320 return {};
1321
1322 IPropertyBag *propertyBag = nullptr;
1323
1324 if (FAILED(moniker->BindToStorage(nullptr,
1325 nullptr,
1326 IID_IPropertyBag,
1327 reinterpret_cast<void **>(&propertyBag)))) {
1328 return {};
1329 }
1330
1331 return PropertyBagPtr(propertyBag, [] (IPropertyBag *propertyBag) {
1332 propertyBag->Release();
1333 });
1334 }
1335
isVirtualCamera(const MonikerPtr & moniker) const1336 bool AkVCam::IpcBridgePrivate::isVirtualCamera(const MonikerPtr &moniker) const
1337 {
1338 auto baseFilter = this->filter(moniker.get());
1339
1340 if (!baseFilter)
1341 return false;
1342
1343 return this->isVirtualCamera(baseFilter.get());
1344 }
1345
isVirtualCamera(IBaseFilter * baseFilter) const1346 bool AkVCam::IpcBridgePrivate::isVirtualCamera(IBaseFilter *baseFilter) const
1347 {
1348 if (!baseFilter)
1349 return false;
1350
1351 CLSID clsid;
1352 memset(&clsid, 0, sizeof(CLSID));
1353 baseFilter->GetClassID(&clsid);
1354
1355 return cameraFromId(clsid) >= 0;
1356 }
1357
cameraPath(const MonikerPtr & moniker) const1358 std::string AkVCam::IpcBridgePrivate::cameraPath(const MonikerPtr &moniker) const
1359 {
1360 auto propertyBag = this->propertyBag(moniker.get());
1361
1362 return this->cameraPath(propertyBag.get());
1363 }
1364
cameraPath(IPropertyBag * propertyBag) const1365 std::string AkVCam::IpcBridgePrivate::cameraPath(IPropertyBag *propertyBag) const
1366 {
1367 VARIANT var;
1368 VariantInit(&var);
1369
1370 if (FAILED(propertyBag->Read(L"DevicePath", &var, nullptr)))
1371 return std::string();
1372
1373 std::wstring wstr(var.bstrVal);
1374 std::string devicePath(wstr.begin(), wstr.end());
1375 VariantClear(&var);
1376
1377 return devicePath;
1378 }
1379
cameraDescription(const MonikerPtr & moniker) const1380 std::wstring AkVCam::IpcBridgePrivate::cameraDescription(const MonikerPtr &moniker) const
1381 {
1382 auto propertyBag = this->propertyBag(moniker.get());
1383
1384 return this->cameraDescription(propertyBag.get());
1385 }
1386
cameraDescription(IPropertyBag * propertyBag) const1387 std::wstring AkVCam::IpcBridgePrivate::cameraDescription(IPropertyBag *propertyBag) const
1388 {
1389 VARIANT var;
1390 VariantInit(&var);
1391
1392 if (FAILED(propertyBag->Read(L"Description", &var, nullptr)))
1393 if (FAILED(propertyBag->Read(L"FriendlyName", &var, nullptr)))
1394 return {};
1395
1396 std::wstring wstr(var.bstrVal);
1397 VariantClear(&var);
1398
1399 return wstr;
1400 }
1401
enumPins(IBaseFilter * baseFilter) const1402 std::vector<AkVCam::PinPtr> AkVCam::IpcBridgePrivate::enumPins(IBaseFilter *baseFilter) const
1403 {
1404 std::vector<PinPtr> pins;
1405 IEnumPins *enumPins = nullptr;
1406
1407 if (SUCCEEDED(baseFilter->EnumPins(&enumPins))) {
1408 enumPins->Reset();
1409 IPin *pin = nullptr;
1410
1411 while (enumPins->Next(1, &pin, nullptr) == S_OK) {
1412 PIN_DIRECTION direction = PINDIR_INPUT;
1413
1414 if (SUCCEEDED(pin->QueryDirection(&direction))
1415 && direction == PINDIR_OUTPUT) {
1416 pins.push_back(PinPtr(pin, [] (IPin *pin) {
1417 pin->Release();
1418 }));
1419
1420 continue;
1421 }
1422
1423 pin->Release();
1424 }
1425
1426 enumPins->Release();
1427 }
1428
1429 return pins;
1430 }
1431
enumVideoFormats(IPin * pin) const1432 std::vector<AkVCam::VideoFormat> AkVCam::IpcBridgePrivate::enumVideoFormats(IPin *pin) const
1433 {
1434 std::vector<AkVCam::VideoFormat> mediaTypes;
1435 IEnumMediaTypes *pEnum = nullptr;
1436
1437 if (FAILED(pin->EnumMediaTypes(&pEnum)))
1438 return mediaTypes;
1439
1440 pEnum->Reset();
1441 AM_MEDIA_TYPE *mediaType = nullptr;
1442
1443 while (pEnum->Next(1, &mediaType, nullptr) == S_OK) {
1444 auto format = formatFromMediaType(mediaType);
1445 deleteMediaType(&mediaType);
1446
1447 if (format.size() > 0)
1448 mediaTypes.push_back(format);
1449 }
1450
1451 pEnum->Release();
1452
1453 return mediaTypes;
1454 }
1455
findFiles(const std::wstring & path) const1456 std::vector<std::wstring> AkVCam::IpcBridgePrivate::findFiles(const std::wstring &path) const
1457 {
1458 std::wstring path_ = path;
1459
1460 auto attributes = GetFileAttributes(path.c_str());
1461
1462 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
1463 path_ += L"\\*";
1464
1465 WIN32_FIND_DATA data;
1466 memset(&data, 0, sizeof(WIN32_FIND_DATA));
1467 auto find = FindFirstFile(path_.c_str(), &data);
1468
1469 if (find == INVALID_HANDLE_VALUE)
1470 return {};
1471
1472 std::vector<std::wstring> paths;
1473
1474 do {
1475 std::wstring fileName(data.cFileName);
1476
1477 if (fileName == L"." || fileName == L"..")
1478 continue;
1479
1480 std::wstring filePath = path + L"\\" + fileName;
1481
1482 if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1483 for (auto path: this->findFiles(filePath))
1484 paths.push_back(path);
1485 else
1486 paths.push_back(filePath);
1487 } while (FindNextFile(find, &data));
1488
1489 FindClose(find);
1490
1491 return paths;
1492 }
1493
findFiles(const std::string & path,const std::string & fileName) const1494 std::vector<std::string> AkVCam::IpcBridgePrivate::findFiles(const std::string &path,
1495 const std::string &fileName) const
1496 {
1497 auto wfiles =
1498 this->findFiles(std::wstring(path.begin(), path.end()),
1499 std::wstring(fileName.begin(), fileName.end()));
1500
1501 std::vector<std::string> files;
1502
1503 for (auto &file: wfiles)
1504 files.push_back(std::string(file.begin(), file.end()));
1505
1506 return files;
1507 }
1508
findFiles(const std::wstring & path,const std::wstring & fileName) const1509 std::vector<std::wstring> AkVCam::IpcBridgePrivate::findFiles(const std::wstring &path,
1510 const std::wstring &fileName) const
1511 {
1512 std::vector<std::wstring> plugins;
1513
1514 for (auto file: this->findFiles(path)) {
1515 auto pos = file.rfind(L"\\");
1516 auto fName = file.substr(pos + 1);
1517
1518 if (!lstrcmpi(fName.c_str(), fileName.c_str()))
1519 plugins.push_back(file);
1520 }
1521
1522 return plugins;
1523 }
1524
regAddLine(const std::wstring & key,const std::wstring & value,const std::wstring & data,BOOL wow) const1525 std::wstring AkVCam::IpcBridgePrivate::regAddLine(const std::wstring &key,
1526 const std::wstring &value,
1527 const std::wstring &data,
1528 BOOL wow) const
1529 {
1530 std::wstringstream ss;
1531 ss << L"reg add \""
1532 << key
1533 << L"\" /v "
1534 << value
1535 << L" /d \""
1536 << data
1537 << L"\" /f";
1538
1539 if (wow)
1540 ss << L" /reg:64";
1541
1542 return ss.str();
1543 }
1544
regAddLine(const std::wstring & key,const std::wstring & value,int data,BOOL wow) const1545 std::wstring AkVCam::IpcBridgePrivate::regAddLine(const std::wstring &key,
1546 const std::wstring &value,
1547 int data,
1548 BOOL wow) const
1549 {
1550 std::wstringstream ss;
1551 ss << L"reg add \""
1552 << key
1553 << L"\" /v "
1554 << value
1555 << L" /t REG_DWORD"
1556 << L" /d "
1557 << data
1558 << L" /f";
1559
1560 if (wow)
1561 ss << L" /reg:64";
1562
1563 return ss.str();
1564 }
1565
regDeleteLine(const std::wstring & key,BOOL wow) const1566 std::wstring AkVCam::IpcBridgePrivate::regDeleteLine(const std::wstring &key,
1567 BOOL wow) const
1568 {
1569 std::wstringstream ss;
1570 ss << L"reg delete \"" + key + L"\" /f";
1571
1572 if (wow)
1573 ss << L" /reg:64";
1574
1575 return ss.str();
1576 }
1577
regDeleteLine(const std::wstring & key,const std::wstring & value,BOOL wow) const1578 std::wstring AkVCam::IpcBridgePrivate::regDeleteLine(const std::wstring &key,
1579 const std::wstring &value,
1580 BOOL wow) const
1581 {
1582 std::wstringstream ss;
1583 ss << L"reg delete \"" + key + L"\" /v \"" + value + L"\" /f";
1584
1585 if (wow)
1586 ss << L" /reg:64";
1587
1588 return ss.str();
1589 }
1590
regMoveLine(const std::wstring & fromKey,const std::wstring & toKey,BOOL wow) const1591 std::wstring AkVCam::IpcBridgePrivate::regMoveLine(const std::wstring &fromKey,
1592 const std::wstring &toKey,
1593 BOOL wow) const
1594 {
1595 std::wstringstream ss;
1596 ss << L"reg copy \"" << fromKey << L"\" \"" << toKey << L"\" /s /f";
1597
1598 if (wow)
1599 ss << L" /reg:64";
1600
1601 ss << std::endl
1602 << regDeleteLine(fromKey, wow);
1603
1604 return ss.str();
1605 }
1606
dirname(const std::wstring & path) const1607 std::wstring AkVCam::IpcBridgePrivate::dirname(const std::wstring &path) const
1608 {
1609 return path.substr(0, path.rfind(L"\\"));
1610 }
1611
updateDeviceSharedProperties()1612 void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties()
1613 {
1614 for (DWORD i = 0; i < camerasCount(); i++) {
1615 auto cameraPath = AkVCam::cameraPath(i);
1616 std::string deviceId(cameraPath.begin(), cameraPath.end());
1617
1618 Message message;
1619 message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING;
1620 message.dataSize = sizeof(MsgBroadcasting);
1621 auto data = messageData<MsgBroadcasting>(&message);
1622 memcpy(data->device,
1623 deviceId.c_str(),
1624 (std::min<size_t>)(deviceId.size(), MAX_STRING));
1625 this->m_mainServer.sendMessage(&message);
1626 this->updateDeviceSharedProperties(deviceId,
1627 std::string(data->broadcaster));
1628 }
1629 }
1630
updateDeviceSharedProperties(const std::string & deviceId,const std::string & owner)1631 void AkVCam::IpcBridgePrivate::updateDeviceSharedProperties(const std::string &deviceId,
1632 const std::string &owner)
1633 {
1634 if (owner.empty()) {
1635 this->m_devices[deviceId] = {SharedMemory(), Mutex()};
1636 } else {
1637 Mutex mutex(std::wstring(owner.begin(), owner.end()) + L".mutex");
1638 SharedMemory sharedMemory;
1639 sharedMemory.setName(L"Local\\"
1640 + std::wstring(owner.begin(), owner.end())
1641 + L".data");
1642
1643 if (sharedMemory.open())
1644 this->m_devices[deviceId] = {sharedMemory, mutex};
1645 }
1646 }
1647
locateDriverPath() const1648 std::wstring AkVCam::IpcBridgePrivate::locateDriverPath() const
1649 {
1650 std::wstring driverPath;
1651
1652 for (auto it = this->driverPaths()->rbegin();
1653 it != this->driverPaths()->rend();
1654 it++) {
1655 auto path = *it;
1656 path = replace(path, L"/", L"\\");
1657
1658 if (path.back() != L'\\')
1659 path += L'\\';
1660
1661 path += DSHOW_PLUGIN_NAME_L L".plugin";
1662
1663 if (this->findFiles(path, DSHOW_PLUGIN_NAME_L L".dll").empty())
1664 continue;
1665
1666 if (this->findFiles(path, DSHOW_PLUGIN_ASSISTANT_NAME_L L".exe").empty())
1667 continue;
1668
1669 driverPath = path;
1670
1671 break;
1672 }
1673
1674 return driverPath;
1675 }
1676
pipeStateChanged(void * userData,MessageServer::PipeState state)1677 void AkVCam::IpcBridgePrivate::pipeStateChanged(void *userData,
1678 MessageServer::PipeState state)
1679 {
1680 AkIpcBridgePrivateLogMethod();
1681 auto self = reinterpret_cast<IpcBridgePrivate *>(userData);
1682
1683 switch (state) {
1684 case MessageServer::PipeStateAvailable:
1685 AkLoggerLog("Server Available");
1686
1687 if (self->self->registerPeer(self->m_asClient)) {
1688 AKVCAM_EMIT(self->self,
1689 ServerStateChanged,
1690 IpcBridge::ServerStateAvailable)
1691 }
1692
1693 break;
1694
1695 case MessageServer::PipeStateGone:
1696 AkLoggerLog("Server Gone");
1697 AKVCAM_EMIT(self->self,
1698 ServerStateChanged,
1699 IpcBridge::ServerStateGone)
1700 self->self->unregisterPeer();
1701
1702 break;
1703 }
1704 }
1705
isAlive(Message * message)1706 void AkVCam::IpcBridgePrivate::isAlive(Message *message)
1707 {
1708 auto data = messageData<MsgIsAlive>(message);
1709 data->alive = true;
1710 }
1711
frameReady(Message * message)1712 void AkVCam::IpcBridgePrivate::frameReady(Message *message)
1713 {
1714 auto data = messageData<MsgFrameReady>(message);
1715 std::string deviceId(data->device);
1716
1717 if (this->m_devices.count(deviceId) < 1) {
1718 this->updateDeviceSharedProperties(deviceId, std::string(data->port));
1719
1720 return;
1721 }
1722
1723 auto frame =
1724 reinterpret_cast<Frame *>(this->m_devices[deviceId]
1725 .sharedMemory
1726 .lock(&this->m_devices[deviceId].mutex));
1727
1728 if (!frame)
1729 return;
1730
1731 VideoFormat videoFormat(frame->format, frame->width, frame->height);
1732 VideoFrame videoFrame(videoFormat);
1733 memcpy(videoFrame.data().data(), frame->data, frame->size);
1734 this->m_devices[deviceId].sharedMemory.unlock(&this->m_devices[deviceId].mutex);
1735 AKVCAM_EMIT(this->self, FrameReady, deviceId, videoFrame)
1736 }
1737
setBroadcasting(Message * message)1738 void AkVCam::IpcBridgePrivate::setBroadcasting(Message *message)
1739 {
1740 auto data = messageData<MsgBroadcasting>(message);
1741 std::string deviceId(data->device);
1742 std::string broadcaster(data->broadcaster);
1743 this->updateDeviceSharedProperties(deviceId, broadcaster);
1744 AKVCAM_EMIT(this->self, BroadcastingChanged, deviceId, broadcaster)
1745 }
1746
setMirror(Message * message)1747 void AkVCam::IpcBridgePrivate::setMirror(Message *message)
1748 {
1749 auto data = messageData<MsgMirroring>(message);
1750 std::string deviceId(data->device);
1751 AKVCAM_EMIT(this->self,
1752 MirrorChanged,
1753 deviceId,
1754 data->hmirror,
1755 data->vmirror)
1756 }
1757
setScaling(Message * message)1758 void AkVCam::IpcBridgePrivate::setScaling(Message *message)
1759 {
1760 auto data = messageData<MsgScaling>(message);
1761 std::string deviceId(data->device);
1762 AKVCAM_EMIT(this->self, ScalingChanged, deviceId, data->scaling)
1763 }
1764
setAspectRatio(Message * message)1765 void AkVCam::IpcBridgePrivate::setAspectRatio(Message *message)
1766 {
1767 auto data = messageData<MsgAspectRatio>(message);
1768 std::string deviceId(data->device);
1769 AKVCAM_EMIT(this->self, AspectRatioChanged, deviceId, data->aspect)
1770 }
1771
setSwapRgb(Message * message)1772 void AkVCam::IpcBridgePrivate::setSwapRgb(Message *message)
1773 {
1774 auto data = messageData<MsgSwapRgb>(message);
1775 std::string deviceId(data->device);
1776 AKVCAM_EMIT(this->self, SwapRgbChanged, deviceId, data->swap)
1777 }
1778
listenerAdd(Message * message)1779 void AkVCam::IpcBridgePrivate::listenerAdd(Message *message)
1780 {
1781 auto data = messageData<MsgListeners>(message);
1782 std::string deviceId(data->device);
1783 AKVCAM_EMIT(this->self,
1784 ListenerAdded,
1785 deviceId,
1786 std::string(data->listener))
1787 }
1788
listenerRemove(Message * message)1789 void AkVCam::IpcBridgePrivate::listenerRemove(Message *message)
1790 {
1791 auto data = messageData<MsgListeners>(message);
1792 std::string deviceId(data->device);
1793 AKVCAM_EMIT(this->self,
1794 ListenerRemoved,
1795 deviceId,
1796 std::string(data->listener))
1797 }
1798
sudo(const std::vector<std::string> & parameters,const std::wstring & directory,bool show)1799 int AkVCam::IpcBridgePrivate::sudo(const std::vector<std::string> ¶meters,
1800 const std::wstring &directory,
1801 bool show)
1802 {
1803 if (parameters.size() < 1)
1804 return E_FAIL;
1805
1806 auto command = parameters[0];
1807 std::wstring wcommand(command.begin(), command.end());
1808 std::wstring wparameters;
1809
1810 for (size_t i = 1; i < parameters.size(); i++) {
1811 auto param = parameters[i];
1812
1813 if (i > 1)
1814 wparameters += L" ";
1815
1816 wparameters += std::wstring(param.begin(), param.end());
1817 }
1818
1819 SHELLEXECUTEINFO execInfo;
1820 memset(&execInfo, 0, sizeof(SHELLEXECUTEINFO));
1821 execInfo.cbSize = sizeof(SHELLEXECUTEINFO);
1822 execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
1823 execInfo.hwnd = nullptr;
1824 execInfo.lpVerb = L"runas";
1825 execInfo.lpFile = wcommand.data();
1826 execInfo.lpParameters = wparameters.data();
1827 execInfo.lpDirectory = directory.data();
1828 execInfo.nShow = show? SW_SHOWNORMAL: SW_HIDE;
1829 execInfo.hInstApp = nullptr;
1830 ShellExecuteEx(&execInfo);
1831
1832 if (!execInfo.hProcess) {
1833 this->m_error = L"Failed executing script";
1834
1835 return E_FAIL;
1836 }
1837
1838 WaitForSingleObject(execInfo.hProcess, INFINITE);
1839
1840 DWORD exitCode;
1841 GetExitCodeProcess(execInfo.hProcess, &exitCode);
1842 CloseHandle(execInfo.hProcess);
1843
1844 if (FAILED(exitCode))
1845 this->m_error = L"Script failed with code " + std::to_wstring(exitCode);
1846
1847 return int(exitCode);
1848 }
1849