virtualcam-module.cpp 5.6 KB

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