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> &parameters,
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> &parameters,
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