From cba22b7155e9f4d88aa317f53dabfcbd9dc915db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Fri, 2 Feb 2024 16:42:47 +0800 Subject: [PATCH] Camera (Windows): init support --- CMakeLists.txt | 4 +- src/detection/camera/camera_windows.c | 6 -- src/detection/camera/camera_windows.cpp | 95 +++++++++++++++++++++++++ src/util/windows/variant.hpp | 34 ++++++--- 4 files changed, 122 insertions(+), 17 deletions(-) delete mode 100644 src/detection/camera/camera_windows.c create mode 100644 src/detection/camera/camera_windows.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 65c8fac78..d73f19f4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -662,7 +662,7 @@ elseif(WIN32) src/detection/wm/wm_windows.cpp src/detection/de/de_nosupport.c src/detection/wmtheme/wmtheme_windows.c - src/detection/camera/camera_windows.c + src/detection/camera/camera_windows.cpp src/util/windows/getline.c src/util/windows/com.cpp src/util/windows/registry.c @@ -944,6 +944,8 @@ elseif(WIN32) PRIVATE "dwmapi" PRIVATE "gdi32" PRIVATE "iphlpapi" + PRIVATE "mf" + PRIVATE "mfplat" PRIVATE "ole32" PRIVATE "oleaut32" PRIVATE "ws2_32" diff --git a/src/detection/camera/camera_windows.c b/src/detection/camera/camera_windows.c deleted file mode 100644 index ff56815f2..000000000 --- a/src/detection/camera/camera_windows.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "camera.h" - -const char* ffDetectCamera(FF_MAYBE_UNUSED FFlist* result) -{ - return "Not supported on this platform"; -} diff --git a/src/detection/camera/camera_windows.cpp b/src/detection/camera/camera_windows.cpp new file mode 100644 index 000000000..350cc2819 --- /dev/null +++ b/src/detection/camera/camera_windows.cpp @@ -0,0 +1,95 @@ +extern "C" { +#include "camera.h" +#include "common/library.h" +} +#include "util/windows/com.hpp" +#include "util/windows/unicode.hpp" + +#include +#include +#include + +template +struct on_scope_exit { + on_scope_exit(Fn &&fn): _fn(std::move(fn)) {} + ~on_scope_exit() { this->_fn(); } + +private: + Fn _fn; +}; + +extern "C" +const char* ffDetectCamera(FF_MAYBE_UNUSED FFlist* result) +{ + // TODO: check if supported by Windows Server Core + + const char* error = ffInitCom(); + if (error) + return error; + + IMFAttributes* FF_AUTO_RELEASE_COM_OBJECT attrs = nullptr; + if (FAILED(MFCreateAttributes(&attrs, 1))) + return "MFCreateAttributes() failed"; + + if (FAILED(attrs->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID + ))) + return "SetGUID(MF_*) failed"; + + IMFActivate** devices = NULL; + uint32_t count; + + if (FAILED(MFEnumDeviceSources(attrs, &devices, &count))) + return "MFEnumDeviceSources() failed"; + + for (uint32_t i = 0; i < count; i++) + { + IMFActivate* device = devices[i]; + + wchar_t buffer[256]; + uint32_t length = 0; + if (FAILED(device->GetString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, buffer, sizeof(buffer), &length)) || length == 0) + continue; + + FFCameraResult* camera = (FFCameraResult*) ffListAdd(result); + ffStrbufInitNWS(&camera->name, length, buffer); + ffStrbufInit(&camera->vendor); + ffStrbufInit(&camera->id); + camera->width = 0; + camera->height = 0; + + if (SUCCEEDED(device->GetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, buffer, sizeof(buffer), &length)) && length > 0) + ffStrbufSetNWS(&camera->id, length, buffer); + + IMFMediaSource* FF_AUTO_RELEASE_COM_OBJECT source = nullptr; + if (FAILED(device->ActivateObject(IID_PPV_ARGS(&source)))) + continue; + + on_scope_exit destroySource([&] { source->Shutdown(); }); + + IMFPresentationDescriptor* FF_AUTO_RELEASE_COM_OBJECT pd = nullptr; + if (FAILED(source->CreatePresentationDescriptor(&pd))) + continue; + + IMFStreamDescriptor* FF_AUTO_RELEASE_COM_OBJECT sd = NULL; + BOOL selected; + if (FAILED(pd->GetStreamDescriptorByIndex(0, &selected, &sd))) + continue; + + IMFMediaTypeHandler* FF_AUTO_RELEASE_COM_OBJECT handler = NULL; + if (FAILED(sd->GetMediaTypeHandler(&handler))) + continue; + + // Assume first type is the maximum resolution + IMFMediaType* FF_AUTO_RELEASE_COM_OBJECT type = NULL; + if (FAILED(handler->GetMediaTypeByIndex(0, &type))) + continue; + + MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &camera->width, &camera->height); + } + + CoTaskMemFree(devices); + + return nullptr; +} diff --git a/src/util/windows/variant.hpp b/src/util/windows/variant.hpp index 8181bd776..84a471ff9 100644 --- a/src/util/windows/variant.hpp +++ b/src/util/windows/variant.hpp @@ -1,16 +1,12 @@ #include +#include #include #include #include -struct FFWmiVariant: VARIANT +template +struct FFBaseVariant: TVariant { - explicit FFWmiVariant() { VariantInit(this); } - ~FFWmiVariant() { VariantClear(this); } - - FFWmiVariant(const FFWmiVariant&) = delete; - FFWmiVariant(FFWmiVariant&&); // don't define it to enforce NRVO optimization - bool hasValue() { return this->vt != VT_EMPTY; } @@ -33,11 +29,11 @@ struct FFWmiVariant: VARIANT return this->cVal; } else if constexpr (std::is_same_v) { - assert(vt == VT_I2); + assert(this->vt == VT_I2); return this->iVal; } else if constexpr (std::is_same_v) { - assert(this->vt == VT_I4 || vt == VT_INT); + assert(this->vt == VT_I4 || this->vt == VT_INT); return this->intVal; } else if constexpr (std::is_same_v) { @@ -55,7 +51,7 @@ struct FFWmiVariant: VARIANT return this->uiVal; } else if constexpr (std::is_same_v) { - assert(this->vt == VT_UI4 || vt == VT_UINT); + assert(this->vt == VT_UI4 || this->vt == VT_UINT); return this->uintVal; } else if constexpr (std::is_same_v) { @@ -135,3 +131,21 @@ struct FFWmiVariant: VARIANT } } }; + +struct FFWmiVariant: FFBaseVariant +{ + FFWmiVariant(const FFWmiVariant&) = delete; + FFWmiVariant(FFWmiVariant&&); // don't define it to enforce NRVO optimization + explicit FFWmiVariant() { VariantInit(this); } + ~FFWmiVariant() { VariantClear(this); } +}; +static_assert(sizeof(FFWmiVariant) == sizeof(VARIANT), ""); + +struct FFPropVariant: FFBaseVariant +{ + FFPropVariant(const FFPropVariant&) = delete; + FFPropVariant(FFPropVariant&&); // don't define it to enforce NRVO optimization + explicit FFPropVariant() { PropVariantInit(this); } + ~FFPropVariant() { PropVariantClear(this); } +}; +static_assert(sizeof(FFPropVariant) == sizeof(PROPVARIANT), "");