virtualcam-module.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. #include "virtualcam-filter.hpp"
  2. #include "virtualcam-guid.h"
  3. /* ========================================================================= */
  4. static const REGPINTYPES AMSMediaTypesV = {&MEDIATYPE_Video,
  5. &MEDIASUBTYPE_NV12};
  6. static const REGFILTERPINS AMSPinVideo = {L"Output", false, true,
  7. false, false, &CLSID_NULL,
  8. nullptr, 1, &AMSMediaTypesV};
  9. HINSTANCE dll_inst = nullptr;
  10. static volatile long locks = 0;
  11. /* ========================================================================= */
  12. class VCamFactory : public IClassFactory {
  13. volatile long refs = 1;
  14. CLSID cls;
  15. public:
  16. inline VCamFactory(CLSID cls_) : cls(cls_) {}
  17. // IUnknown
  18. STDMETHODIMP QueryInterface(REFIID riid, void **p_ptr);
  19. STDMETHODIMP_(ULONG) AddRef();
  20. STDMETHODIMP_(ULONG) Release();
  21. // IClassFactory
  22. STDMETHODIMP CreateInstance(LPUNKNOWN parent, REFIID riid,
  23. void **p_ptr);
  24. STDMETHODIMP LockServer(BOOL lock);
  25. };
  26. STDMETHODIMP VCamFactory::QueryInterface(REFIID riid, void **p_ptr)
  27. {
  28. if (!p_ptr) {
  29. return E_POINTER;
  30. }
  31. if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) {
  32. AddRef();
  33. *p_ptr = (void *)this;
  34. return S_OK;
  35. } else {
  36. *p_ptr = nullptr;
  37. return E_NOINTERFACE;
  38. }
  39. }
  40. STDMETHODIMP_(ULONG) VCamFactory::AddRef()
  41. {
  42. return InterlockedIncrement(&refs);
  43. }
  44. STDMETHODIMP_(ULONG) VCamFactory::Release()
  45. {
  46. long new_refs = InterlockedDecrement(&refs);
  47. if (new_refs == 0) {
  48. delete this;
  49. return 0;
  50. }
  51. return (ULONG)new_refs;
  52. }
  53. STDMETHODIMP VCamFactory::CreateInstance(LPUNKNOWN parent, REFIID, void **p_ptr)
  54. {
  55. if (!p_ptr) {
  56. return E_POINTER;
  57. }
  58. *p_ptr = nullptr;
  59. /* don't bother supporting the "parent" functionality */
  60. if (parent) {
  61. return E_NOINTERFACE;
  62. }
  63. if (IsEqualCLSID(cls, CLSID_OBS_VirtualVideo)) {
  64. *p_ptr = (void *)new VCamFilter();
  65. return S_OK;
  66. }
  67. return E_NOINTERFACE;
  68. }
  69. STDMETHODIMP VCamFactory::LockServer(BOOL lock)
  70. {
  71. if (lock) {
  72. InterlockedIncrement(&locks);
  73. } else {
  74. InterlockedDecrement(&locks);
  75. }
  76. return S_OK;
  77. }
  78. /* ========================================================================= */
  79. static inline DWORD string_size(const wchar_t *str)
  80. {
  81. return (DWORD)(wcslen(str) + 1) * sizeof(wchar_t);
  82. }
  83. static bool RegServer(const CLSID &cls, const wchar_t *desc,
  84. const wchar_t *file, const wchar_t *model = L"Both",
  85. const wchar_t *type = L"InprocServer32")
  86. {
  87. wchar_t cls_str[CHARS_IN_GUID];
  88. wchar_t temp[MAX_PATH];
  89. HKEY key = nullptr;
  90. HKEY subkey = nullptr;
  91. bool success = false;
  92. StringFromGUID2(cls, cls_str, CHARS_IN_GUID);
  93. StringCbPrintf(temp, sizeof(temp), L"CLSID\\%s", cls_str);
  94. if (RegCreateKey(HKEY_CLASSES_ROOT, temp, &key) != ERROR_SUCCESS) {
  95. goto fail;
  96. }
  97. RegSetValueW(key, nullptr, REG_SZ, desc, string_size(desc));
  98. if (RegCreateKey(key, type, &subkey) != ERROR_SUCCESS) {
  99. goto fail;
  100. }
  101. RegSetValueW(subkey, nullptr, REG_SZ, file, string_size(file));
  102. RegSetValueExW(subkey, L"ThreadingModel", 0, REG_SZ,
  103. (const BYTE *)model, string_size(model));
  104. success = true;
  105. fail:
  106. if (key) {
  107. RegCloseKey(key);
  108. }
  109. if (subkey) {
  110. RegCloseKey(subkey);
  111. }
  112. return success;
  113. }
  114. static bool UnregServer(const CLSID &cls)
  115. {
  116. wchar_t cls_str[CHARS_IN_GUID];
  117. wchar_t temp[MAX_PATH];
  118. StringFromGUID2(cls, cls_str, CHARS_IN_GUID);
  119. StringCbPrintf(temp, sizeof(temp), L"CLSID\\%s", cls_str);
  120. return RegDeleteTreeW(HKEY_CLASSES_ROOT, temp) == ERROR_SUCCESS;
  121. }
  122. static bool RegServers(bool reg)
  123. {
  124. wchar_t file[MAX_PATH];
  125. if (!GetModuleFileNameW(dll_inst, file, MAX_PATH)) {
  126. return false;
  127. }
  128. if (reg) {
  129. return RegServer(CLSID_OBS_VirtualVideo, L"OBS Virtual Camera",
  130. file);
  131. } else {
  132. return UnregServer(CLSID_OBS_VirtualVideo);
  133. }
  134. }
  135. static bool RegFilters(bool reg)
  136. {
  137. ComPtr<IFilterMapper2> fm;
  138. HRESULT hr;
  139. hr = CoCreateInstance(CLSID_FilterMapper2, nullptr,
  140. CLSCTX_INPROC_SERVER, IID_IFilterMapper2,
  141. (void **)&fm);
  142. if (FAILED(hr)) {
  143. return false;
  144. }
  145. if (reg) {
  146. ComPtr<IMoniker> moniker;
  147. REGFILTER2 rf2;
  148. rf2.dwVersion = 1;
  149. rf2.dwMerit = MERIT_DO_NOT_USE;
  150. rf2.cPins = 1;
  151. rf2.rgPins = &AMSPinVideo;
  152. hr = fm->RegisterFilter(CLSID_OBS_VirtualVideo,
  153. L"OBS Virtual Camera", &moniker,
  154. &CLSID_VideoInputDeviceCategory,
  155. nullptr, &rf2);
  156. if (FAILED(hr)) {
  157. return false;
  158. }
  159. } else {
  160. hr = fm->UnregisterFilter(&CLSID_VideoInputDeviceCategory, 0,
  161. CLSID_OBS_VirtualVideo);
  162. if (FAILED(hr)) {
  163. return false;
  164. }
  165. }
  166. return true;
  167. }
  168. /* ========================================================================= */
  169. STDAPI DllRegisterServer()
  170. {
  171. if (!RegServers(true)) {
  172. RegServers(false);
  173. return E_FAIL;
  174. }
  175. CoInitialize(0);
  176. if (!RegFilters(true)) {
  177. RegFilters(false);
  178. RegServers(false);
  179. CoUninitialize();
  180. return E_FAIL;
  181. }
  182. CoUninitialize();
  183. return S_OK;
  184. }
  185. STDAPI DllUnregisterServer()
  186. {
  187. CoInitialize(0);
  188. RegFilters(false);
  189. RegServers(false);
  190. CoUninitialize();
  191. return S_OK;
  192. }
  193. STDAPI DllInstall(BOOL install, LPCWSTR)
  194. {
  195. if (!install) {
  196. return DllUnregisterServer();
  197. } else {
  198. return DllRegisterServer();
  199. }
  200. }
  201. STDAPI DllCanUnloadNow()
  202. {
  203. return InterlockedOr(&locks, 0) == 0 ? S_OK : S_FALSE;
  204. }
  205. STDAPI DllGetClassObject(REFCLSID cls, REFIID riid, void **p_ptr)
  206. {
  207. if (!p_ptr) {
  208. return E_POINTER;
  209. }
  210. *p_ptr = nullptr;
  211. if (riid != IID_IClassFactory && riid != IID_IUnknown) {
  212. return E_NOINTERFACE;
  213. }
  214. if (!IsEqualCLSID(cls, CLSID_OBS_VirtualVideo)) {
  215. return E_INVALIDARG;
  216. }
  217. *p_ptr = (void *)new VCamFactory(cls);
  218. return S_OK;
  219. }
  220. //#define ENABLE_LOGGING
  221. #ifdef ENABLE_LOGGING
  222. void logcallback(DShow::LogType, const wchar_t *msg, void *)
  223. {
  224. OutputDebugStringW(msg);
  225. OutputDebugStringW(L"\n");
  226. }
  227. #endif
  228. BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID)
  229. {
  230. if (reason == DLL_PROCESS_ATTACH) {
  231. #ifdef ENABLE_LOGGING
  232. DShow::SetLogCallback(logcallback, nullptr);
  233. #endif
  234. dll_inst = inst;
  235. }
  236. return true;
  237. }